jamesnetherton opened a new pull request, #558:
URL: https://github.com/apache/camel-quarkus-examples/pull/558

   ## Problem
   
   `HybridPqcX509TrustManager` performed its own certificate checks instead of 
delegating. Because registering a custom `X509TrustManager` *replaces* the one 
Quarkus builds from the configured truststore — JSSE consults only the trust 
manager it is given — client authentication was effectively unauthenticated:
   
   - Any self-signed certificate carrying the three Chimera extensions was 
accepted. `CertificatesUtil` did `cert.verify(cert.getPublicKey())`, a 
self-signature check that establishes nothing, and `getAcceptedIssuers()` 
returned an empty array. The truststore configured in `application.properties` 
was never consulted.
   - Expired certificates were accepted; nothing checked the validity period.
   - `checkServerTrusted` had an empty body — the textbook trust-all trust 
manager, ready to be copied into an outbound client.
   - The ML-DSA-65 signature was computed over the subject DN only, so the PQC 
extensions could be lifted onto another certificate along with that DN by 
someone who never held the ML-DSA key.
   
   This matters because the example's whole subject is TLS security, so its 
trust manager is exactly the code a reader will lift into their own project. I 
reproduced each of the four points against the running application before 
changing anything.
   
   ## Changes
   
   - **`HybridPqcTrustManagerCustomizer`** retrieves the trust manager Quarkus 
built from `quarkus.http.ssl.certificate.trust-store-file` and passes it to 
`HybridPqcX509TrustManager` as a delegate instead of discarding it. Startup 
fails if no truststore is configured, rather than leaving the PQC check as the 
only barrier.
   - **`HybridPqcX509TrustManager`** delegates chain, trust anchor and 
validity-period validation, then verifies the ML-DSA-65 signature on each 
certificate in the chain. `checkServerTrusted` is implemented and 
`getAcceptedIssuers` returns the configured anchors. The class Javadoc explains 
why the ordering matters, since that is the part worth copying.
   - **`CertificatesUtil`** verifies the alternative signature with the 
**issuer's** ML-DSA-65 public key via 
`X509CertificateHolder.isAlternativeSignatureValid`, which covers the whole 
`TBSCertificate`. Verifying against a key carried by the certificate under test 
would authenticate nothing.
   - **`HybridCertificateGenerator`** adds a hybrid CA holding both an RSA and 
an ML-DSA-65 keypair, and issues the server and client certificates from it, 
signing each with both keys. The truststores hold the CA. This is what gives 
the post-quantum signature meaning: an attacker who could forge RSA still could 
not mint an accepted certificate without the CA's ML-DSA-65 key. BouncyCastle's 
`build(signer, isCritical, altSigner)` produces the spec-correct extensions, 
replacing the hand-rolled version.
   - **Docs** correct the claims about what is validated and what is 
quantum-safe (the transport remains classical on Java 17 — only the certificate 
authentication is hybrid), switch `curl -k` to `--cacert`, and drop the pinned 
BouncyCastle version.
   
   The certificates were previously self-signed while declaring `Issuer: CN=PQC 
Hybrid CA`, which is the hierarchy `PQC-EXPLANATION.adoc` already described. 
The CA makes the code match the documentation rather than the other way round.
   
   ## Tests
   
   13 → 18 tests. The RSA-only client certificate is now issued by the same CA, 
so it passes chain validation and the missing ML-DSA-65 signature is the only 
fault — previously that test could have passed with the PQC check entirely 
broken. New cases cover an untrusted hybrid certificate, an expired one, one 
issued by a different CA, one whose issuer publishes no PQC key, and one 
carrying extensions lifted from another certificate. Cases that chain 
validation would reject first are unit-tested against `CertificatesUtil` 
directly, so they exercise the signature check rather than passing incidentally.
   
   ## Verification
   
   - `mvn clean verify` on Java 17: 18/18 tests pass
   - `mvn clean verify -Dnative` (Mandrel 25): native build plus 5 native 
integration tests pass
   - `mvn license:check formatter:validate impsort:check` clean
   - Both `curl` commands in the README run against a live `quarkus:dev` 
server, including the documented failure case
   
   ---
   _Claude Code on behalf of James Netherton_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to