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]
