ChargeLink Native

TLS, PKI and Plug & Charge

Transport security is the most frequent cause of a session that connects but never exchanges a protocol message. This page states exactly what ChargeLink Native requires per protocol and adapter, how to generate and check material, and what the Plug & Charge (PnC) surfaces do.

Requirements per protocol

ProtocolTLSVersionClient (vehicle) certificateAdapter
ISO 15118-20mandatory for every external sessionTLS 1.3 only; plaintext and TLS 1.2 are rejected before SupportedAppProtocol. Named groups secp521r1 and x448 only, cipher suites TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256 only; no session tickets or resumptionrequired and verified against the V2G root (ISO 15118-20 Annex B chain, ECDSA secp521r1 with SHA-512)rustls (--tls --require-ca)
ISO 15118-2optionalSChannel: TLS 1.2 only; rustls: 1.2 or 1.3; the iso2-evcc lane: TLS 1.2 on OpenSSL with the cipher suites of ISO 15118-2not required; --require-ca enables verification on rustlsrustls or Windows SChannel (--external-evcc-windows-tls --tls-pfx)
DIN 70121none———
gRPC Integration APInone (every call requires the per-start access token)——keep on 127.0.0.1; see Production Security

ISO 15118-20 named groups and cipher suites

Both ChargeLink roles use the named groups ISO 15118-20 requires (see Table 7 of the standard): secp521r1 and x448. The EVCC offers both with a key share for each and only the cipher suites TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256; the SECC chooses its own first preference among those groups the EV offers, with a HelloRetryRequest where needed, and refuses a client that offers neither with handshake_failure before any V2G message. Session tickets and resumption are off.

Option (EVSE and EVCC commands)Effect
--iso20-curve-profile secp521r1|x448The group offered first (default secp521r1). The bench runs secp521r1.
--allow-legacy-tls-named-groupsAdds secp256r1 last, for peers that offer only P-256 or x25519. Recorded as a legacy claim boundary in the evidence; on the SECC it also requires --allow-engineering-tls-identity. Not conformant; engineering use only.

The offered groups, key shares and suites, the negotiated group, the handshake kind and the policy are recorded in evse_listener_status.json, tls_handshake_diagnostics.json and evcc_loopback_status.json. A charger and a vehicle from different ChargeLink releases refuse each other: PC1 and PC2 must run the same release. The ISO 15118-2 TLS 1.2 roles and the x448 key agreement use a static OpenSSL 3.5.9 linked into chargelink.exe; its configuration loading is disabled.

Material you need

FileEVSE roleEVCC role
V2G root CA (PEM)--tls-cafile--tls-cafile
SECC certificate chain and private key (PEM)--tls-cert, --tls-key—
Vehicle certificate chain and private key (PEM)—--tls-cert, --tls-key
SECC identity as PKCS#12 (PFX)--tls-pfx with --tls-pfx-password-env (SChannel only)—

Both machines must be provisioned from the same root. The SECC's DNS name is used for SNI only; ISO 15118-20 identity is validated as an Annex B chain, so a missing subjectAltName does not fail the session and the EVCC's --tls-server-name (default chargelink-secc.local) is cosmetic under the strict SNI policy.

Production PKI for ISO 15118-20 must satisfy Annex B: ECDSA on secp521r1 with SHA-512 signatures, the prescribed extended key usages and authority information access, and subject key identifiers computed by method 2. ChargeLink checks the DER profile and the chain signature. Ed448 chains are modelled but fail closed; revocation checking (CRL/OCSP) is exercised by fixtures and negative campaigns but there is no OCSP responder in the engineering lanes.

Generate engineering material

chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse tls-material-init --output-dir "$Data\pki\dev" --protocol iso15118-20 --profile-id iso15118_20_dc_dev_tls
Get-ChildItem "$Data\pki\dev"

Writes evse_server_cert.pem (the SECC chain: leaf, CSO sub-CA2, CSO sub-CA1), evse_server_key.pem (PKCS#8 leaf key), evse_ca_bundle.pem (the engineering V2G root) and TLS_SECURITY_BOUNDARY.md. The material is generated fresh on every run: new keys and certificates each time, never compiled into ChargeLink, ECDSA with SHA-512 on secp521r1 in the ISO 15118-20 Annex B shape. Every certificate carries the subject organisation "ChargeLink ENGINEERING ONLY - not for production". Use --overwrite to regenerate into an existing directory.

The SECC refuses engineering material by default. A server certificate with the engineering marker is rejected at evse start unless you pass --allow-engineering-tls-identity. That flag is for development and test only and must never appear in a production command line. Because the root changes with every generation, copy evse_ca_bundle.pem to every peer (the EVCC on PC2, the vehicle simulator) again after each run of tls-material-init.

chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse start --protocol iso15118-20 --profile-id iso15118_20_dc --tls --require-ca --allow-engineering-tls-identity `
  --tls-cert "$Data\pki\dev\evse_server_cert.pem" --tls-key "$Data\pki\dev\evse_server_key.pem" --tls-cafile "$Data\pki\dev\evse_ca_bundle.pem" `
  --external-evcc-session --host $Advertise --tcp-port 50118

Complete engineering bench PKI for two PCs

chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse tls-material-init --bench-pki --output-dir "$Data\pki\engineering-pki"
Get-ChildItem -Recurse "$Data\pki\engineering-pki"

With --bench-pki one command generates, with new keys every run, the whole engineering PKI of the two-PC ISO 15118-20 bench in the Annex B structure: pc1\secc-cert-chain.pem, pc1\secc-key.pem and pc1\v2g-root-ca.pem for the charger, pc2\vehicle-cert-chain.pem, pc2\vehicle-key.pem and pc2\v2g-root-ca.pem for the vehicle, plus engineering-pki-manifest.json (fingerprints, validity, SHA-256 of every file; no key) and README-ENGINEERING-ONLY.txt. The result reports the generated SECCID and EVCCID, engineering_only true and pc1_requires_allow_engineering_tls_identity true. The root and sub-CA keys are discarded when the command ends, so nothing else can be issued under that root; the output folder must not exist yet; every file is readable by the owner and SYSTEM only. --pfx-password-env NAME also writes pc1\secc-identity.pfx for the Windows SChannel path. Copy the three pc2 files to PC2 after every generation.

Check material before a session

chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse tls-pki-policy
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse tls-pki-doctor --protocol iso15118-20 --profile-id iso15118_20_dc `
  --tls --require-ca --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --write-artifacts
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse certificate-flow --protocol iso15118-20 --profile-id iso15118_20_dc `
  --tls --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem"
chargelink --runtime-root "$Data\runtime" --artifact-root "$Data\artifacts" --json evse tls-listener-preflight --protocol iso15118-20 --profile-id iso15118_20_dc `
  --tls-cert "$Pki\secc-chain.pem" --tls-key "$Pki\secc-key.pem" --tls-cafile "$Pki\v2g-root-ca.pem" --require-ca `
  --host $Advertise --tcp-port 0 --write-artifacts

The doctor reports key/certificate match, chain validity, validity period, EKU and the profile's TLS expectations. The listener preflight prepares (and, with a non-zero port, binds) the rustls server configuration without accepting peers. Always check the clock (w32tm /query /status): an expired or not-yet-valid certificate is the most common false alarm.

Secrets handling

  • Never pass --tls-pfx-password on the command line; use --tls-pfx-password-env NAME and set the variable in the session ($env:NAME = Read-Host 'PFX password'). An unset variable is a configuration error, not a silent fallback.
  • Private keys are classified sensitive_credential with export_allowed=false; they never enter support bundles. Certificate bodies are not exported either; diagnostics carry fingerprints and metadata.
  • Restrict the ACL of C:\ChargeLinkData\pki (see Production Security).

Plug & Charge

SurfaceBehaviour
ISO 15118-20 AuthorizationSetupThe EVSE advertises EIM unless a PnC fixture manifest is configured through CHARGELINK_ISO20_PNC_AUTH_SETUP_FIXTURE_MANIFEST. Generate the manifest with chargelink --json exi iso20-pnc-auth-setup-artifacts --run-id pnc-fixture; the result prints the exact variable to set. A PnC-named profile alone does not advertise PnC on the wire.
ISO 15118-2 PaymentDetails / CertificateInstallationReference responses built from fixed engineering certificates (AC PnC lane). No CertificateUpdate or MeteringReceipt handling in the live lane.
Deterministic TLS/PKI/PnC campaigns12 campaigns with transcript hashes: valid contract, expired contract, untrusted root, wrong EKU, revoked contract, malformed certificate, OCSP soft-fail, redaction boundary, ISO 15118-2 contract install and update, TLS hostname mismatch, private-key export block. See Deterministic Simulation.
Security package v2gtp-sdp-tls-security24 executable engineering cases covering SDP, TCP, TLS and V2GTP negatives plus PnC chain checks and redaction validation.

None of these surfaces connects to a mobility operator or certificate pool. They validate message sequences and certificate policy with engineering material.

Troubleshooting a TLS failure

Symptom (from tls_handshake_diagnostics.json or the probe status)Likely causeAction
Handshake rejected before SupportedAppProtocol on ISO 15118-20Peer offered TLS 1.2 or no client certificatePeer must use TLS 1.3 with a vehicle certificate; do not use the SChannel or dev adapters for ISO 15118-20.
handshake_failure at once, the diagnostics list offered groups without secp521r1 or x448The peer offers only P-256 or x25519 (neither secp521r1 nor x448)Fix the peer, or for engineering use start both roles with --allow-legacy-tls-named-groups (plus --allow-engineering-tls-identity on the SECC).
A ChargeLink charger and vehicle refuse each other after an upgradeDifferent ChargeLink releases on PC1 and PC2Deploy the same release on both PCs; mixed versions fail closed by design.
Unknown CA / chain validation failedDifferent roots on the two machinesProvision both from one root; run the PKI doctor with the same --tls-cafile on both.
Certificate expired or before its validity periodClock or validity periodw32tm /resync; check notBefore/notAfter in the doctor output.
Wrong EKU / Annex B profile check failedCertificate issued for another purpose or wrong curveReissue with the Annex B profile (P-521, SHA-512, correct EKU).
evse start refuses the server certificate; the message names engineering TLS materialThe chain came from tls-material-init and carries the engineering markerDevelopment or test bench: add --allow-engineering-tls-identity. Production: replace the material with operator-issued certificates.
Unknown CA on the peer right after regenerating engineering materialtls-material-init creates a new root every runCopy the new evse_ca_bundle.pem to the peer again.
Handshake passes with dev material, fails with production materialProduction chain has intermediate certificates the file does not containSupply the full chain in --tls-cert; the leaf alone is not enough.