Skip to content

OCPP 1.6J Security Configuration

Sevket Gökay edited this page Nov 20, 2025 · 4 revisions

This document describes how to configure SteVe to support the three OCPP 1.6 security profiles defined in the OCPP 1.6 Security Whitepaper Edition 3.

Security Profile Overview

Profile 0: Unsecured Transport, no Auth

  • Transport: HTTP or WebSocket (ws://)
  • Authentication: None
  • Encryption: None
  • Use Case: Development, testing, closed networks. Not recommended for production.

Profile 1: Unsecured Transport with Basic Authentication

  • Transport: HTTP or WebSocket (ws://)
  • Authentication: HTTP Basic Authentication + Charge Point Password
  • Encryption: None
  • Use Case: Private networks with additional authentication layer. Not recommended for production.

Profile 2: TLS with Basic Authentication

  • Transport: HTTPS or Secure WebSocket (wss://)
  • Authentication: HTTP Basic Authentication + TLS Server Certificate
  • Encryption: TLS 1.2 or higher
  • Use Case: Production environments with server authentication

Profile 3: TLS with Client-Side Certificates

  • Transport: HTTPS or Secure WebSocket (wss://)
  • Authentication: Mutual TLS (mTLS) with client certificates
  • Encryption: TLS 1.2 or higher
  • Use Case: High-security production environments

General notes

SteVe implements all aspects and operations of the security paper. The configuration of the station (i.e. which security profile a station is using) is the determining factor what kind of requirements and checks SteVe applies with regards to security profile decision. However, the way SteVe is configured will have an affect on the profile(s) that are supported, as well.

The Charge Point details page has these two new fields to configure:

Screenshot 2025-11-20 at 13 07 28

Security Profile menu lists all available options:

Screenshot 2025-11-20 at 13 16 45

As default, all existing stations are migrated to use the profile 0. A password MUST be set for profiles 1 and 2.

If you want to support profiles 2 and 3, one major operational decision relies on whether you are running SteVe standalone or behind a proxy:

  • Standalone: SteVe itself MUST be configured accordingly to support TLS and mTLS.
  • Behind a proxy (or load balancer): Assuming the proxy terminating TLS and SteVe just running on plain HTTP behind proxy, the proxy MUST be configured to do all certificate validation checks (outside of scope of this documentation). Moreover, the proxy MUST be configured to forward the client certificate to SteVe for it to do some additional business-related checks.

The following two configuration groups are introduced or extended. You need to change them and set values according to your setup and configuration.

1. Spring Boot Server SSL config

server:  
  ssl:
    enabled: ${https.enabled}
    key-store-type:
    key-store: ${keystore.path}
    key-store-password: ${keystore.password}
    trust-store-type:
    trust-store:
    trust-store-password:
    enabled-protocols:
      - TLSv1.2
      - TLSv1.3
    ciphers:
    # or 'need' for strict security profile 3 (mTLS)
    client-auth: want

If you want to support profiles 2 and 3 and are not running SteVe behind a proxy or load balancer, these must be set. Essentially, these will give the application the capability to deal with certificates. key-store-prefixed parameters are necessary for profile 2 (i.e. the server certificates). trust-store-prefixed parameters are necessary for profile 3, additionally (i.e. client certificates).

If you are running SteVe behind a proxy or load balancer, the proxy or load balancer MUST be configured accordingly. The configuration above is not necessarily needed for profiles 2 and 3, but key-store-prefixed parameters are still necessary if you want to be able to create and sign certificates locally (related to SignCertificate.req).

2. SteVe config

steve:
  ocpp:
    # OCPP 1.6 Security Profiles Configuration
    # Other settings will be taken from server.ssl
    security:
      # Profile 0: No HTTP basic authentication, no TLS
      # Profile 1: HTTP basic authentication, no TLS
      # Profile 2: HTTP basic authentication, TLS with server-side certificate
      # Profile 3: TLS with client-side certificate, TLS with server-side certificate -> mutual TLS (mTLS)
      profile: 0
      certificate-validity-years: 1
      # See de.rwth.idsg.steve.service.CertificateValidator for documentation
      client-cert-header-from-proxy:
  • profile: This is actually not doing much other than being a criterion whether to enable certificate signing (see CertificateSigningServiceLocal, related to SignCertificate.req). It will be enabled for profiles 2 and 3.
  • certificate-validity-years: If CertificateSigningServiceLocal is enabled (see previous point), this parameter defines the validity time period when creating new certificates.
  • client-cert-header-from-proxy: If you want to support profile 3 (mTLS) and run the app in a setup with reverse proxy (or other components in front of the communication chain) where you terminate TLS, these components need proper and complete configuration regarding mTLS. They MUST validate all aspects of the client cert. Moreover, they MUST be configured to forward the client cert as header to us, because we need to check 2 extra attributes: O (organizationName) of the cert subject has to match CpoName config of the station. CN (commonName) of the cert subject field has to match the serial number of the station. This parameter defines the custom header under which the cert will be forwarded to us from a reverse proxy or load balancer.

Profile 0: Unsecured Transport, no Auth

No config YML changes necessary, if all stations will be using this.

WebSocket URL: ws://your-server:8080/steve/websocket/CentralSystemService/{chargePointId}

Profile 1: Unsecured Transport with Basic Authentication

No config YML changes necessary.

The station MUST be configured to use profile 1 in SteVe (see screenshots above) with an auth password in database.

WebSocket URL: ws://your-server:8080/steve/websocket/CentralSystemService/{chargePointId}

The WebSocket connection request MUST have the following header:

AUTHORIZATION: Basic <Base64 encoded(<ChargePointId>:<AuthorizationKey>)>

Profile 2: TLS with Basic Authentication

One of the both:

a) When running Steve standalone:

# Create server keystore with self-signed certificate (for testing)
keytool -genkeypair -alias steve-server \
  -keyalg RSA -keysize 2048 -validity 365 \
  -keystore server-keystore.jks \
  -storepass changeit \
  -dname "CN=steve.example.com, OU=SteVe, O=Example, L=City, ST=State, C=US"

# OR: Import existing certificate and private key
# (Use openssl to convert PEM to PKCS12, then import to JKS)
# only relevant parts are shown
server:  
  ssl:
    enabled: true
    key-store-type: JKS
    key-store: /opt/steve/certs/server-keystore.jks
    key-store-password: changeit
    enabled-protocols:
      - TLSv1.2
      - TLSv1.3

steve:
  ocpp:
    security:
      profile: 2
      certificate-validity-years: 1

b) When running Steve behind proxy:

Configure the proxy to support server certificates and terminate TLS.

--

The station MUST be configured to use profile 2 in SteVe (see screenshots above) with an auth password in database.

WebSocket URL: wss://your-server:8080/steve/websocket/CentralSystemService/{chargePointId}

The WebSocket connection request MUST have the following header:

AUTHORIZATION: Basic <Base64 encoded(<ChargePointId>:<AuthorizationKey>)>

Charge points must trust the server certificate. Install the CA certificate or server certificate on charge points.

Profile 3: TLS with Client-Side Certificates

One of the both:

a) When running Steve standalone:

# Create server keystore with self-signed certificate (for testing)
keytool -genkeypair -alias steve-server \
  -keyalg RSA -keysize 2048 -validity 365 \
  -keystore server-keystore.jks \
  -storepass changeit \
  -dname "CN=steve.example.com, OU=SteVe, O=Example, L=City, ST=State, C=US"

# OR: Import existing certificate and private key
# (Use openssl to convert PEM to PKCS12, then import to JKS)

# Import CA certificate of the station to truststore
keytool -import -trustcacerts -alias ca-cert \
  -file ca-cert.pem \
  -keystore truststore.jks \
  -storepass changeit -noprompt
# only relevant parts are shown
server:  
  ssl:
    enabled: true
    key-store-type: JKS
    key-store: /opt/steve/certs/server-keystore.jks
    key-store-password: changeit
    trust-store-type: JKS
    trust-store: /opt/steve/certs/truststore.jks
    trust-store-password: changeit
    enabled-protocols:
      - TLSv1.2
      - TLSv1.3
    ciphers:
    client-auth: need

steve:
  ocpp:
    security:
      profile: 3
      certificate-validity-years: 1

b) When running Steve behind proxy:

Configure the proxy to support server AND client certificates and terminate TLS.

Configure the proxy to forward the client cert under some custom header (e.g. X-Client-Cert)

Configure SteVe for the awareness of header by setting:

# only relevant parts are shown
steve:
  ocpp:
    security:
      client-cert-header-from-proxy: "X-Client-Cert"

--

The station MUST be configured to use profile 3 in SteVe (see screenshots above).

WebSocket URL: wss://your-server:8080/steve/websocket/CentralSystemService/{chargePointId}

Charge points must trust the server certificate. Install the CA certificate or server certificate on charge points.


Testing TLS Configuration

Test Server Certificate with OpenSSL

# Test TLS connection
openssl s_client -connect steve.example.com:8443 -showcerts

# Test with client certificate
openssl s_client -connect steve.example.com:8443 \
  -cert client-cp001-cert.pem -key client-cp001-key.pem

Test WebSocket Connection

# Install wscat: npm install -g wscat

# Test Profile 2 (wss://)
wscat -c "wss://steve.example.com:8443/steve/websocket/CentralSystemService/CP001"

# Test Profile 3 (wss:// with client cert)
wscat -c "wss://steve.example.com:8443/steve/websocket/CentralSystemService/CP001" \
  --cert client-cp001.p12 --passphrase changeit