OCPP lets a charger prove who it is when it opens the WebSocket, using one of three security profiles. The CSMS has to enforce them, and the only way to find out whether yours does is to connect with the right credentials, the wrong ones and none. Doing that with real chargers means reconfiguring hardware for every case. A simulated station takes its credentials from the create request, so each case is one API call.

The profiles and the field that selects them

A station created without a security object connects with no credentials, which is the unsecured profile (Profile 0 in SimItNow's terms). The other three are chosen by the URL scheme and by what you put in security:

ProfilecsmsUrlWhat the station sendssecurity field
1ws://HTTP Basic AuthbasicAuth
2wss://HTTP Basic Auth over TLSbasicAuth
3wss://A TLS client certificateclientCert

Profile 1 sends the password in clear text, so use it only against a CSMS on a network you trust. The background on how the profiles differ between OCPP versions is in OCPP 1.6 vs 2.0.1.

Basic Auth

curl -X PUT https://api.simitnow.com/v2/cs/CP-001 \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"csmsUrl":"wss://csms.example.com/ocpp","protocol":"ocpp2.0.1",
       "security":{"basicAuth":{"password":"the-station-password"}}}'

The username defaults to the station id, which is what the OCPP specifications require. Set basicAuth.username only when you want to test a CSMS that expects something else. The password is the one your CSMS holds for that station.

Client certificate

For Profile 3 the certificate and private key go into the request as PEM text. Building the JSON by hand is error prone, so let jq read the files:

jq -n --arg cert "$(cat station.crt)" --arg key "$(cat station.key)" \
  '{csmsUrl:"wss://csms.example.com/ocpp", protocol:"ocpp2.0.1",
    security:{clientCert:{certificate:$cert, privateKey:$key}}}' |
curl -X PUT https://api.simitnow.com/v2/cs/CP-001 \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d @-

basicAuth and clientCert cannot be combined, and clientCert needs a wss:// URL. A request that breaks either rule, or leaves out the password, the certificate or the key, is refused with 400 and nothing is dialled. So is a csmsUrl that is not a WebSocket address, such as one without the ws:// or wss:// start.

SimItNow checks the CSMS server certificate against the usual public authorities. A CSMS with a self-signed certificate or a private CA fails that check. For a test setup, set "skipServerCertVerify": true inside security to connect anyway. It only has an effect on wss:// URLs, and it removes the check that protects Profile 2 and 3 from a man in the middle, so leave it off wherever the CSMS has a real certificate.

Check the credentials before you create the station

The connection probe accepts the same security object and runs one real handshake with it:

curl -X POST https://api.simitnow.com/v1/cs/probe \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"csmsUrl":"wss://csms.example.com/ocpp","id":"CP-001","protocol":"ocpp2.0.1",
       "security":{"basicAuth":{"password":"the-station-password"}}}'

A wrong password or an unknown station comes back as credentials_refused (the CSMS answered 401 or 403) or path_not_found (404). A certificate problem during TLS shows as certificate_untrusted or tls_failed. The probe sends no BootNotification, so you can run it as often as you like within the limit of 20 a minute.

The negative tests

The positive case, a valid credential that connects, is the one everyone tests. These are the ones that find holes:

The station's credentials are fixed when it is created. To test a second password, create another station under a different id.

Reporting a security event

A station reports tampering, a firmware update or a certificate problem with a SecurityEventNotification. Nothing on the CSMS side can ask for one, so SimItNow has an endpoint that makes the station send it:

curl -X POST https://api.simitnow.com/v2/cs/CP-001/security-event \
  -H "SIMITNOW-API-KEY: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"TamperDetectionActivated","techInfo":"enclosure sensor 3"}'

The type can be any string up to 50 characters, so you can send the standard ones and vendor-specific ones. The endpoint works on every protocol version and sends that version's SecurityEventNotification. Check that your CSMS stores the event, alerts on the serious ones and does not close the connection on a type it has not seen.

The hosted CSMS in private mode checks HTTP Basic Auth on a wss:// address, which is Profile 2, so it is a quick way to see a working Basic Auth setup before you test your own. Create a free account to run these tests.