Alle Sicherheitshinweise

gematik lib-vau / lib-vau-csharp

VAU-Handshake führt nur 2 von 6 vorgeschriebenen Server-Schlüssel-Prüfungen aus

lib-vau und lib-vau-csharp sind die Referenzimplementierungen des VAU-Protokolls von gematik — der Sicherheitsschicht, die den Datenverkehr zum Backend der elektronischen Patientenakte (ePA) Deutschlands schützt und seit 2025 für die rund 73 Millionen gesetzlich Versicherten im Einsatz ist, sofern sie nicht widersprochen haben. Beide Referenz-VAU-Clients deserialisieren die SignedPublicVauKeys-Struktur des Servers während Message 2 des Handshakes, lesen jedoch die Felder signatureEs256, certHash und ocspResponse nie aus. Von den sechs Prüfschritten, die gemSpec_Krypt A_24624-01 verlangt (Kettenprüfung, OCSP, Rollen-OID, ES256-Signatur, Schlüsselformat, Ablauf), werden nur Schlüsselformat und Ablauf geprüft. Die Bibliotheken bieten Konsumenten keine API, um die Prüfung selbst einzuhängen. Ein netzwerkpositionierter MITM innerhalb des TI- / Klinik-Netzwerk-Segments vor dem VAU-Endpunkt — also genau an der Grenze, gegen die die innere VAU-Schicht eigentlich härten soll — kann daher eigene ECDH- und Kyber-Public-Keys in Message 2 einschleusen und allen nachfolgenden VAU-Verkehr entschlüsseln oder modifizieren. Erfasst als MS-LIB-VAU-eaea8d (Java) und MS-LIB-VAU-CSHARP-9c69c4 (C#).

Verfasst vonVolker Schönefeld, Simon Weber2026-05-28
SchweregradHochCVSS 7.4CVSS-3.1-VektorAV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:NCWECWE-295 (Improper Certificate Validation)Produktgematik lib-vau / lib-vau-csharpBetroffene Versionenlib-vau 1.0.0 bis einschließlich 1.0.15 (Java); lib-vau-csharp 1.0.0 bis einschließlich 1.0.9 (C#)Behoben inUpstream nicht behoben. Der Hersteller hat den experimentellen Status der Bibliotheken in der README präzisiert und sieht keinen Anlass für ein eigenes Advisory. Konsumenten, die die Bibliotheken produktiv einsetzen, sind selbst dafür verantwortlich, die fehlende Server-Schlüssel-Prüfung zu ergänzen — siehe Mitigation.

Beschreibung

gemSpec_Krypt A_24624-01 verlangt vom VAU-Client sechs Prüfschritte auf den SignedPublicVauKeys des Servers während des Handshakes: Zertifikatsketten-Prüfung gegen den TI-PKI-Root, OCSP-Sperrprüfung (max. 24 h), Rollen-OID-Prüfung (oid_epa_vau), ES256-Signaturprüfung über das Schlüsselmaterial, ECC/Kyber-Schlüsselformat-Validierung und Ablaufprüfung (exp > now). Beide Referenzimplementierungen führen nur die letzten beiden aus.

In der Java-Referenzimplementierung (lib-vau) befindet sich die Lücke im Handshake hier:

lib-vau VauClientStateMachine.java:108-120

SignedPublicVauKeys signedPublicVauKeys;
try {
signedPublicVauKeys = decodeCborMessageToClass(
transferredSignedServerPublicKey, SignedPublicVauKeys.class);
} catch (Exception e) {
throw new IllegalArgumentException(
"Could not CBOR decode Signed Server Public Keys...", e);
}
VauPublicKeys transferredSignedServerPublicKeyList =
signedPublicVauKeys.extractVauKeys();
checkCertificateExpired(
transferredSignedServerPublicKeyList.getExp());
verifyClientMessageIsWellFormed(
transferredSignedServerPublicKeyList.getEcdhPublicKey(),
transferredSignedServerPublicKeyList);
// Proceeds directly to KEM encapsulation with unverified keys.

View source →

Die Datenklasse SignedPublicVauKeys deserialisiert die relevanten Felder, liest sie aber nie aus:

lib-vau SignedPublicVauKeys.java:45-58

@JsonProperty("signature-ES256")
byte[] signatureEs256;
@JsonProperty("cert_hash")
byte[] certHash;
@JsonProperty("ocsp_response")
byte[] ocspResponse;

View source →

Die Klasse stellt eine sign()-Methode für die Server-Seite bereit, jedoch keine entsprechende verify()-Methode, und auch keine Schnittstelle oder Callback, über die Konsumenten eine Prüfung von außen einhängen könnten. Die README erklärt "Es werden keine Zertifikate geprüft." — die Bibliothek bietet keine eingebaute Prüfung und keine Schnittstelle, über die Konsumenten sie einhängen können.

Die C#-Referenzimplementierung (lib-vau-csharp) hat dieselbe Form:

lib-vau-csharp VauClientStateMachine.cs:109-113

SignedPublicVauKeys signedPublicVauKeysClient = SignedPublicVauKeys.fromCbor(transferredSignedServerPublicKey);
VauPublicKeys transferredSignedServerPublicKeyList = signedPublicVauKeysClient.ExtractVauKeys();
KeyUtils.CheckCertificateExpired(transferredSignedServerPublicKeyList.Exp);
KeyUtils.VerifyClientMessageIsWellFormed(transferredSignedServerPublicKeyList.EcdhPublicKey, transferredSignedServerPublicKeyList);
// Proceeds directly to KEM encapsulation with unverified keys.

View source →

lib-vau-csharp SignedPublicVauKeys.cs:33-36

readonly byte[] SignatureEs256;
readonly byte[] CertHash;
readonly byte[] OcspResponse;

View source →

A_24624-01-Umsetzungsstand über beide Bibliotheken:

A_24624-01 Umsetzungsstand

Anforderung Java C#
----------------------------------------------------------
OCSP-Prüfung (max. 24 h) Fehlend Fehlend
Zertifikatskette zum TI-PKI-Root Fehlend Fehlend
Rollen-OID oid_epa_vau Fehlend Fehlend
ES256-Signaturprüfung Fehlend Fehlend
ECC/Kyber-Schlüsselformat-Validierung Vorhanden Vorhanden
Ablaufprüfung (exp > now) Vorhanden Vorhanden

Konsumenten übernehmen die Referenzimplementierung wie sie ist. Die fehlende Prüfung hat sich in produktive Clients fortgepflanzt: med-united/epa4all bindet lib-vau als Submodul ein und übernimmt die fehlende Prüfung unverändert, und oviva-ag/epa4all-client ergänzte zwar ein SignedPublicKeysTrustValidator-Interface, verwarf jedoch den booleschen Rückgabewert von Signature.verify(), sodass ein Fehlschlag der Signaturprüfung den Validator nicht dazu brachte, false zurückzugeben (CVE-2026-44900). gematiks offizieller Beispielcode in epa4all-examples (v0.1.6, inzwischen archiviert) ruft ebenfalls receiveMessage2() ohne jeden Schritt zur Server-Schlüssel-Prüfung auf.

Auswirkung

  • Die innere VAU-Schicht ist dafür konzipiert, Patientendaten gegen die Kompromittierung von TLS-Infrastruktur außerhalb der TEE-Grenze zu schützen. Der Angriff erfordert eine Position innerhalb des TI- / Klinik-Netzwerk-Segments vor dem VAU-Endpunkt — also genau an der Grenze, gegen die die innere VAU-Schicht eigentlich härten soll.
  • Aus dieser Position kann ein netzwerkpositionierter MITM Message 1 des Handshakes abfangen, eine KEM-Encapsulation mit den Schlüsseln des Clients durchführen und mit einer Message 2 antworten, die in der AEAD-Payload angreiferkontrollierte ECDH- und Kyber-Public-Keys trägt. Das Feld signatureEs256 kann beliebige Bytes enthalten.
  • Da der Client weder Signatur noch Zertifikatskette, OCSP-Antwort oder Rollen-OID prüft, leitet er die Session-Keys aus den Schlüsseln des Angreifers ab. Der MITM hält dann beide Hälften der Sitzung und kann jede nachfolgende VAU-Nachricht im Klartext entschlüsseln und neu verschlüsseln: Medikationsdaten, Diagnoseberichte und Verordnungshistorie einer betroffenen Person, gelesen vom ePA-Client, in das ePA-Backend zurückgeschriebene Dokumente sowie die Autorisierungstransaktionen, die den Zugriff auf die Patientenakte steuern.

Abhilfe

Konsumenten, die eine der Bibliotheken produktiv einsetzen, sollten ein SignedPublicKeysTrustValidator-Interface ergänzen (der Name, den Ovivas Fork verwendet, eignet sich gut), das der Konstruktor von VauClientStateMachine *zwingend* vom Konsumenten verlangt. Rufen Sie es in receiveMessage2() vor der KEM-Encapsulation auf. Default-deny — wenn kein Validator bereitgestellt wird, soll der Handshake fehlschlagen, statt ohne Prüfung fortzufahren. Der Validator sollte die vollständige A_24624-01-Kette durchführen: Zertifikat per cert_hash abrufen, OCSP-Antwort validieren, Kette gegen TI-PKI-Roots prüfen, auf die Rollen-OID oid_epa_vau prüfen und die ES256-Signatur über den signierten Schlüsselblock verifizieren. Verwenden Sie den booleschen Rückgabewert von Signature.verify() (Java) bzw. VerifyData() (C#); ein verworfener Rückgabewert entspricht keiner Prüfung.

Checkliste für Betreiber

  • Prüfen, ob Sie lib-vau / lib-vau-csharp ausliefern.

    Wenn Ihr ePA-Client (oder irgendein Produkt, das VAU spricht) lib-vau oder lib-vau-csharp direkt, über einen Fork oder über ein Git-Submodul einbindet, betrifft Sie die Lücke. Zwei produktiv ausgelieferte ePA-Implementierungen haben die fehlende Prüfung unverändert übernommen: med-united/epa4all (lib-vau als Git-Submodul) und oviva-ag/epa4all-client (ein Validator wurde ergänzt, aber falsch verdrahtet — siehe CVE-2026-44900).

  • Einen Trust-Validator mit Default-deny ergänzen.

    Der Konstruktor von VauClientStateMachine muss verpflichtend einen SignedPublicKeysTrustValidator-Parameter erhalten, der in receiveMessage2() vor der KEM-Encapsulation aufgerufen wird. Wenn kein Validator bereitgestellt wird, muss der Handshake fehlschlagen und nicht stillschweigend fortgesetzt werden. Die Konstruktor-Signatur selbst muss geändert werden — Java: einen verpflichtenden Parameter zum bisherigen no-arg-Konstruktor hinzufügen; C#: äquivalente Änderung an VauClientStateMachine().

  • Die vollständige A_24624-01-Kette umsetzen.

    Cert-Hash-Lookup, OCSP innerhalb von 24 h, Kette zum TI-PKI-Root, Rollen-OID oid_epa_vau, ES256-Signaturprüfung über den signierten Schlüsselblock. Alle sechs Prüfungen müssen erfolgreich sein, damit der Handshake fortgesetzt wird. TI-PKI-Root-Zertifikate liegen nicht im Standard-Truststore des Systems — beziehen Sie sie aus der veröffentlichten TI-PKI von gematik (gemSpec_PKI, Anhang — TI-Root-CA) und pinnen Sie sie explizit in Ihrem Validator.

  • Prüfen, dass der boolesche Rückgabewert gelesen wird.

    Sowohl Javas Signature.verify() als auch C#'s VerifyData() liefern bei ungültiger Signatur false zurück, statt eine Exception zu werfen. Ein verworfener Rückgabewert entspricht stillschweigendem Pass. Der Oviva-Vorfall (CVE-2026-44900) ist genau dieser Fall.

  • Den Validator mit einer gefälschten Message 2 prüfen.

    Bauen Sie einen lokalen VAU-Gegenpart, der auf Message 1 mit einer Message 2 antwortet, die wohlgeformte, aber nicht signierte öffentliche Schlüssel sowie beliebige Bytes im Feld signatureEs256 enthält. Der Client muss den Handshake ablehnen. Falls nicht, ist der Validator nicht eingehängt.

Bewertung im Detail

AV:NDas Bedrohungsmodell, gegen das A_24624-01 schreibt, ist ein Netzwerkangreifer auf dem Pfad zwischen ePA-Client und VAU-Endpunkt.AC:HErfordert eine MITM-Position in einem TI-verbundenen Netzwerksegment (lokales Klinik-Netzwerk oder ein vorgelagertes Peering) sowie einen funktionsfähigen VAU-Gegenpart, um den Handshake abzuschließen. Machbar, aber kein Single-Shot-Exploit.PR:NAußer der Netzwerkposition selbst werden keine Credentials benötigt.UI:NDer ePA-Client initiiert den Handshake nach seinem eigenen Zeitplan.S:UDie Auswirkung ist auf den VAU-Kanal und die ePA-Payloads beschränkt, die er transportiert.C:HKlartext-Zugriff auf ePA-Dokumenten-Reads und Metadaten.I:HKlartext-Zugriff auf und Modifikation von ePA-Dokumenten-Writes und Autorisierungsflüssen.A:NKeine Auswirkung auf die Verfügbarkeit.

Referenzen

So können wir helfen

Wer wir sind

Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec-Forscher

Ich evaluiere Ihr SaMD mit derselben branchenprägenden Sicherheitsexpertise, die ich dem BAK MV für die Überarbeitung des B3S-Standards beigetragen habe.

  • Promotion über Krankenhaus-Cybersicherheit
  • Kritische Schwachstellen in Krankenhaussystemen gefunden
  • Alumni der THB MedSec-Forschungsgruppe
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

Als ehemaliger CTO und Entwickler, der zum Pentester wurde, arbeite ich mit Ihrem Team zusammen, um Schwachstellen aufzudecken und Lösungen zu finden, die zu Ihrer Architektur passen.

  • 20+ Jahre als CTO, 50+ Mio. App-Downloads
  • Architektur und Absicherung großer IoT-Flotten
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Penetrationstest gesucht?

Machine Spirits ist spezialisiert auf Sicherheitsbewertungen für Medizinprodukte und Gesundheits-IT. Von MDR-Penetrationstests bis C5-Cloud-Compliance helfen wir MedTech-Unternehmen, regulatorische Anforderungen zu erfüllen.