All Advisories

Oviva epa4all-client

VAU Signature Verification Bypass

In SignedPublicKeysTrustValidatorImpl.isTrusted(), the ECDSA verification of the VAU server's signed public keys discards the boolean return value of Signature.verify(). The method falls through to return true regardless of whether the signature is valid, allowing a network attacker to substitute their own keys into the VAU handshake.

Authored byChiara Fliegner, Volker Schönefeld, Simon WeberDisclosed 2026-05-05Fully disclosed 2026-05-28
SeverityHighCVSS 8.1CVSS 3.1 VectorAV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:NCWECWE-295 (Improper Certificate Validation)ProductOviva epa4all-clientAffected VersionsAll versions through 1.2.0 (introduced in the initial commit of the trust validator)Fixed In1.2.1 (incorporates pull request #34)CVECVE-2026-44900GHSAGHSA-g8r3-5hwf-qp96

Description

The trust validator computes everything needed to authenticate the server's signed public keys, then drops the one boolean that matters:

SignedPublicKeysTrustValidatorImpl.java:39-56

var ecdsa = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
ecdsa.initVerify(certs.cert());
ecdsa.update(signedPublicKeys.signedPubKeys());
var signatureDer = ECDSA.transcodeSignatureToDER(signedPublicKeys.signatureEs256());
ecdsa.verify(signatureDer);
// falls through to:
return true;

View source →

The same codebase already contains the correct pattern in a sibling verifier:

BrainpoolJwsVerifier.java:53

return sig.verify(derSignature);

View source →

Because the certificate chain and OCSP are genuinely validated, a network MITM relays the server's real certificate and OCSP response upstream and substitutes only the VAU public keys, signing them with a throwaway key. The chain and OCSP pass; the wrong signature on the substituted keys is the only thing that should fail, and that result is discarded. The same root cause (a discarded server-key signature check) appears across the ePA-VAU ecosystem; see gematik: VAU Handshake Performs Only 2 of 6 Required Server-Key Checks.

Impact

  • An attacker on the same network as an ePA client (for example, the local clinic network where the ePA connector lives) substitutes their own ECDH and Kyber public keys into the VAU handshake. The client derives session keys from the attacker's keys, granting full plaintext access to ePA traffic: document writes, document reads, and authorization flows.
  • During testing, a single relay captured the upstream server's genuine certificate and OCSP response, presented them alongside attacker-controlled VAU keys whose signature did not verify, and completed the M1-M4 handshake; it then read one patient's consent decisions, modified a second response, and injected a third patient's record over the one hijacked channel.

Mitigation

Update epa4all-client to 1.2.1 or later. The fix reads the boolean result of Signature.verify() and returns false when the signature does not match, instead of discarding it. Add a unit test that asserts isTrusted() returns false for an invalid signature; at the time of disclosure the handshake tests bypassed signature verification with a permissive s -> true lambda, so the discard was never exercised.

Defender's Checklist

  • Update epa4all-client to 1.2.1 or later.

    Every published version through 1.2.0 is affected. The fix is pull request #34: read the Signature.verify() result and fail closed.

  • Grep your own forks for discarded verify() calls.

    Any verify(...) whose boolean result is not read is equivalent to no check. Search for Signature.verify, .verify( on JWS/JWT verifiers, and similar across your VAU and IDP code paths.

  • Add a negative test for the trust validator.

    Assert that isTrusted() returns false for a structurally valid but wrong signature. Tests that stub trust with a s -> true lambda never exercise the real validator and will pass against the vulnerable code.

  • Test with a self-signed peer.

    Stand up a local VAU peer that returns attacker-controlled public keys with a signature that does not match. The client must refuse the handshake.

Severity Reasoning

AV:AThe attacker must sit on the network path between the ePA client and the backend. In the deployment model that is the local clinic network where the ePA connector lives, an adjacent-network position.AC:LA MITM naturally relays the genuine upstream certificate and OCSP response; only the signature over the substituted keys is forged, and that check's result is discarded unconditionally. No timing or environmental conditions.PR:NNo credentials required beyond the network position.UI:NThe client performs the VAU handshake on its own schedule; no user interaction.S:UImpact is bounded to the VAU channel and the ePA payloads it carries.C:HPlaintext access to ePA document reads and authorization material for the patient the client is acting on.I:HPlaintext access to and modification of ePA document writes and authorization flows on the hijacked channel.A:NNo availability impact.

References

How We Can Help

Who We Are

The security researchers behind this advisory.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec Researcher

I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.

  • PhD on Hospital Cybersecurity
  • Critical vulnerabilities found in hospital systems
  • Alumni of THB MedSec Research Group
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.

  • 20+ years as CTO, 50M+ app downloads
  • Architected and secured large-scale IoT fleets
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Looking for a Penetration Test?

Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.