Agenda
Serie: V2G Security Research
- Teil 1: EV-Laden und der V2G-Protokollstack – Bedrohungsmodell und Protokollübersicht
- Teil 2: Laborumgebung – Docker-basierte V2G-Sicherheitsumgebung
- Teil 3: TLS-Downgrade
- Teil 4: SDP Response Spoofing
- Teil 5: Zertifikatsvalidierung ← Sie befinden sich hier
- Teil 6: Downgrade des Protokoll-Namespace
Agenda
Serie: V2G Security Research
- Teil 1: EV-Laden und der V2G-Protokollstack – Bedrohungsmodell und Protokollübersicht
- Teil 2: Laborumgebung – Docker-basierte V2G-Sicherheitsumgebung
- Teil 3: TLS-Downgrade
- Teil 4: SDP Response Spoofing
- Teil 5: Zertifikatsvalidierung ← Sie befinden sich hier
- Teil 6: Downgrade des Protokoll-Namespace
Agenda
Serie: V2G Security Research
- Teil 1: EV-Laden und der V2G-Protokollstack – Bedrohungsmodell und Protokollübersicht
- Teil 2: Laborumgebung – Docker-basierte V2G-Sicherheitsumgebung
- Teil 3: TLS-Downgrade
- Teil 4: SDP Response Spoofing
- Teil 5: Zertifikatsvalidierung ← Sie befinden sich hier
- Teil 6: Downgrade des Protokoll-Namespace
TLS ist nur so sicher wie die zugrunde liegende Zertifikatsprüfung. Die Verschlüsselung des Datenverkehrs bringt wenig, wenn ein Client gefälschte oder fehlerhafte Zertifikate akzeptiert. Jeder Angreifer, der dem EVCC ein manipuliertes Zertifikat präsentieren kann, übernimmt damit potenziell die Sitzung. In diesem Szenario untersuchen wir daher die entscheidende Frage: Welche Zertifikatsfehler erkennt das Josev-EVCC und bei welchen Mängeln würde die Verbindung dennoch akzeptiert?
Dafür führen wir drei Teil-Szenarien durch. In jedem Szenario präsentiert der SECC dem EVCC einen anderen Zertifikatstyp. Anschließend analysieren wir, wie sich dies auf den TLS-Handshake auswirkt.
Anforderung gemäß ISO 15118-2 (§ 8.7.4): Wird TLS eingesetzt, muss das EVCC das Zertifikat des SECC gegen eine vertrauenswürdige V2G-Root-CA validieren und die Sitzung abbrechen, falls die Validierung fehlschlägt. Die Spezifikation schreibt keine Hostnamenprüfung vor. Das Zertifikat muss jedoch auf eine vertrauenswürdige Root-CA zurückführbar sein und darf nicht abgelaufen sein. [05_Certifi...Validation | Word]
Testzertifikate erstellen
Für diesen Test benötigen wir zunächst drei verschiedene Zertifikatsvarianten. Geprüft werden die folgenden Szenarien:
| Nr. | Teil-Szenario | Zertifikatsfehler des SECC | Erwartetes Ergebnis |
|---|---|---|---|
| A | valid | Kein Fehler (von vertrauenswürdiger Research-Lab-CA signiert) | TLS erfolgreich, Sitzung wird aufgebaut |
| B | self_signed | Selbstsigniert, keine Zertifikatskette zu einer CA | TLS wird abgelehnt |
| C | expired | Abgelaufen (notAfter = 2. Januar 2020) | TLS wird abgelehnt |
Zur Vereinfachung der Zertifikatsgenerierung verwenden wir das Skript make-certs.sh. Dieses erstellt zunächst eine selbstsignierte Root-CA für das Forschungslabor und generiert anschließend drei Varianten des SECC-Zertifikats:
- Ein gültiges Zertifikat
- Ein abgelaufenes Zertifikat (auf Januar 2020 zurückdatiert)
- Ein selbstsigniertes Zertifikat ohne CA-Kette
- ca/ enthält die Root-CA
- certs/cert_validation/valid/
- certs/cert_validation/self_signed/
- certs/cert_validation/expired/
- evcc/ enthält den Truststore des EVCC
- SECC_SCENARIO wird an beide Container übergeben und bestimmt die zu ladende Konfiguration.
- EVCC_PKI_PATH verweist auf den Truststore des EVCC und stellt Josev die Root-CA des Forschungslabors zur Verfügung.
Zusätzlich wird ein EVCC-Truststore erzeugt, der ausschließlich die Root-CA des Forschungslabors enthält. Dadurch verwendet das EVCC in allen Tests dieselbe Vertrauensbasis.
Alle Dateien werden im Verzeichnis certs/ abgelegt.
bash make-certs.sh
![]()
Nach Abschluss der Ausführung liegt folgende Struktur vor:
Da im Truststore ausschließlich die Root-CA des Forschungslabors gespeichert ist, erfolgt die Validierung in allen Teil-Szenarien gegen dieselbe vertrauenswürdige Zertifizierungsstelle. [05_Certifi...Validation | Word]
Ausführung eines Teil-Szenarios
Jedes Szenario wird über zwei Umgebungsvariablen ausgewählt, bevor docker compose up ausgeführt wird:
In allen Konfigurationen ist SECC_ENFORCE_TLS=True gesetzt. TLS wird also immer angeboten. Der einzige Unterschied zwischen den Szenarien sind die jeweils präsentierten Zertifikate.
Für eine bessere Lesbarkeit der Wireshark-Mitschnitte verwenden alle Szenarien dieselben festen IPv6-Adressen:
SECC → fe80::ff:fe00:1 (MAC 02:00:00:00:00:01)
EVCC → fe80::ff:fe00:2 (MAC 02:00:00:00:00:02)
Mitschnitte und Logs analysieren
Die PCAP-Dateien werden automatisch unter folgendem Pfad gespeichert:
./captures/cert_validation_<name>/
Für die Analyse des TLS-Handshakes in Wireshark empfiehlt sich folgender Filter:
tls || udp.port == 15118
Beginnen wir mit dem Positivtest: einem gültigen Zertifikat, das auf die vertrauenswürdige Root-CA zurückgeführt werden kann.
Teil-Szenario A: Gültiges Zertifikat (Referenzfall)
Bevor mögliche Angriffe getestet werden, müssen wir sicherstellen, dass die Laborumgebung mit einem gültigen Zertifikat korrekt funktioniert.
Wenn das EVCC bereits ein korrektes Zertifikat ablehnt, liegt ein Konfigurationsfehler und kein Sicherheitsbefund vor.
Die Konfiguration befindet sich in:
secc/config/cert_validation/valid.env
PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
FREE_CHARGING_SERVICE=False
LOG_LEVEL=DEBUG
PKI_PATH=/certs/cert_validation/valid/
Vor dem Test kann das Zertifikat mit OpenSSL überprüft werden:
openssl x509 -noout -subject -issuer -dates \
-in certs/cert_validation/valid/iso15118_2/certs/cpoCertChain.pem
![]()
Anschließend wird die Laborumgebung gestartet:
# Cleanup: stop and remove any containers and volumes from a previous run
docker compose down -v
docker rm -f attacker 2>/dev/null || true
export SECC_SCENARIO=cert_validation_valid
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc
![]()
Traffic-Analyse
SDP-Anfrage
Wie bereits im vorherigen Beitrag beschrieben, sendet das EVCC zunächst eine SDP-Anfrage und fordert dabei eine TLS-gesicherte Verbindung an.
![]()
SDP-Antwort
Der SECC antwortet mit seiner IP-Adresse, dem TCP-Port sowie Security = 0x00. Damit bestätigt er, dass TLS verwendet wird.
![]()
TCP Three-Way Handshake
Mit den erhaltenen Verbindungsinformationen baut das EVCC eine TCP-Verbindung auf.
EVCC → SECC: SYN
SECC → EVCC: SYN-ACK
EVCC → SECC: ACK
![]()
TLS ClientHello
Direkt nach Aufbau der TCP-Verbindung sendet das EVCC seine TLS-ClientHello-Nachricht.
![]()
TLS ServerHello und Zertifikatsübermittlung
Der SECC präsentiert nun sein Zertifikat. Weitere Details finden wir im Zertifikatsrahmen in Wireshark:
TLSv1.2 Server Hello, Certificate, Server Key Exchange, Server Hello Done
Selected cipher: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Certificate: CN=secc.v2g.research (signed by V2G Root CA)
![]()
TLS ClientKeyExchange
Das EVCC validiert die Zertifikatskette erfolgreich und sendet seine Schlüsselinformationen.
Danach wird der TLS-Handshake erfolgreich abgeschlossen und alle weiteren V2G-Nachrichten werden verschlüsselt übertragen.
![]()
Auch die EVCC-Logs bestätigen den erfolgreichen Sitzungsaufbau.
![]()
Der Referenzfall ist damit bestätigt: Das EVCC akzeptiert ein korrekt ausgestelltes Zertifikat einer vertrauenswürdigen Zertifizierungsstelle. [05_Certifi...Validation | Word]
Teil-Szenario B: Selbstsigniertes Zertifikat
Kommen wir nun zum nächsten Teil-Szenario. Was passiert, wenn der SECC ein Zertifikat präsentiert, das er selbst signiert hat und das keine Zertifikatskette zu einer vertrauenswürdigen Zertifizierungsstelle besitzt?
Für diesen Test setzen wir SECC_SCENARIO=cert_validation_self_signed, sodass der SECC die Konfiguration aus secc/config/cert_validation/self_signed.env verwendet:
PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
PKI_PATH=./certs/cert_validation/self_signed/
Am EVCC wird keine Änderung vorgenommen. Der Trust Store bleibt unter EVCC_PKI_PATH=/certs/evcc konfiguriert, sodass weiterhin ausschließlich die Root-CA des Forschungslabors als vertrauenswürdig hinterlegt ist.
Der SECC lädt nun ein Zertifikat, das mit seinem eigenen Schlüssel signiert wurde. Es existiert keinerlei CA-Kette. Wie zuvor können wir das Zertifikat vor dem Test mit OpenSSL inspizieren:
openssl x509 -noout -subject -issuer -dates \
-in certs/cert_validation/self_signed/iso15118_2/certs/cpoCertChain.pem
![]()
Auffällig ist, dass Subject und Issuer identisch sind. Das Zertifikat wurde also nicht von einer Zertifizierungsstelle signiert, sondern signiert sich selbst.
Wir sind bereit und können die Container starten:
export SECC_SCENARIO=cert_validation_self_signed
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc --abort-on-container-exit
Der TLS-Handshake wird nicht erfolgreich abgeschlossen. Im EVCC-Log ist zu erkennen, dass die Ablehnung unmittelbar nach der Präsentation des Zertifikats durch den SECC erfolgt.
![]()
Auch im PCAP-Mitschnitt ist das Verhalten gut sichtbar. Nach dem TCP-Handshake sendet der SECC sein ServerHello samt Zertifikat. Das EVCC antwortet jedoch nicht mit ClientKeyExchange, sondern bestätigt zunächst den Empfang mit ACK und beendet anschließend die Verbindung per FIN/ACK.
![]()
Damit wird bestätigt, dass keinerlei ISO-15118-2-Anwendungsdaten ausgetauscht werden.
Teil-Szenario C: Abgelaufenes Zertifikat
Als Nächstes betrachten wir ein Zertifikat, das ursprünglich von unserer vertrauenswürdigen Root-CA ausgestellt wurde, dessen Gültigkeitszeitraum jedoch bereits vor sechs Jahren abgelaufen ist. Mit diesem Test prüfen wir, ob das EVCC das Feld notAfter korrekt validiert.
Die SECC-Konfiguration für dieses Szenario befindet sich in:
PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
PKI_PATH=./certs/cert_validation/expired/
Vor dem Start prüfen wir erneut das Zertifikat:
openssl x509 -noout -subject -issuer -dates \
-in certs/cert_validation/expired/iso15118_2/certs/cpoCertChain.pem
Im Gegensatz zum selbstsignierten Zertifikat verweist der Issuer hier auf die Root-CA des Forschungslabors. Die Zertifikatskette ist also kryptografisch gültig. Das einzige Problem ist der Gültigkeitszeitraum: Das Feld notAfter ist auf den 2. Januar 2020 gesetzt und liegt damit mehr als sechs Jahre in der Vergangenheit.
![]()
Nun setzen wir SECC_SCENARIO=cert_validation_expired, damit der SECC die entsprechende Konfiguration lädt, und konfigurieren weiterhin EVCC_PKI_PATH=/certs/evcc, sodass das EVCC dieselbe Root-CA zur Validierung verwendet. Anschließend starten wir beide Container:
export SECC_SCENARIO=cert_validation_expired
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc --abort-on-container-exit
Dieses Szenario ist etwas subtiler als der vorherige Fall. Das Zertifikat besitzt eine gültige Signatur unserer vertrauenswürdigen CA, sodass die Prüfung der Zertifikatskette erfolgreich verläuft. Das EVCC muss jedoch einen weiteren Schritt durchführen und die Gültigkeit des Zertifikats überprüfen. Genau an dieser Stelle schlägt der Handshake fehl. Da das Feld notAfter mehr als sechs Jahre in der Vergangenheit liegt, lehnt das EVCC die Verbindung ab.
Das bedeutet: Die Zertifikatskette ist gültig, doch das EVCC erkennt den abgelaufenen Zeitstempel korrekt.
![]()
Das Verhalten auf Netzwerkebene entspricht exakt dem aus Teil-Szenario B. Nachdem der SECC sein ServerHello und das abgelaufene Zertifikat übermittelt hat, bestätigt das EVCC den Empfang zunächst mit ACK und beendet anschließend die Verbindung unmittelbar per FIN/ACK. Es erfolgt weder ein ClientKeyExchange noch ein Austausch von Anwendungsdaten oder der Aufbau einer Sitzung.
![]()
Am Ende dieses Teil-Szenarios können wir festhalten, dass das EVCC sowohl selbstsignierte als auch abgelaufene Zertifikate korrekt zurückweist, während gültige Zertifikate einer vertrauenswürdigen Zertifizierungsstelle akzeptiert werden. Damit bestätigt sich, dass das Josev-EVCC die von ISO 15118-2 § 8.7.4 geforderte Zertifikatsvalidierung ordnungsgemäß umsetzt.
Damit endet dieser Beitrag. Im nächsten Artikel untersuchen wir den Angriff auf den Protokoll-Namespace und analysieren, ob sich V2G-Kommunikation durch ein Downgrade unterstützter Protokollversionen beeinflussen lässt.
Weiterlesen: Zur V2G-Security Research
Teil 2: Laborumgebung – Docker-basierte V2G-Sicherheitsumgebung
Teil 3: TLS-Downgrade
Teil 4: SDP Response Spoofing
Teil 5: Zertifikatsvalidierung ← Sie befinden sich hier
Teil 6: Downgrade des Protokoll-Namespace