Adversaries are recording encrypted Alfresco traffic today, betting that a future quantum computer will decrypt it. This post introduces an open-source, runnable ACS 26.1 deployment with hybrid post-quantum TLS (X25519MLKEM768) on every service path: usable today on JDK 17/21 via Bouncy Castle, native on JDK 27.
Jump to: The threat · Attack surface · What this delivers · How it works today · After JDK 27 · Verifying · Gaps · Collaborate
I've built a runnable Alfresco deployment that closes the post-quantum TLS gap on every service path. It works today on JDK 17/21, it's on GitHub, and I'm publishing it on Connect because the only way it stops being one engineer project and starts being a community asset is if partners and customers run it on workloads I haven't tested. This is not a Hyland reference architecture and not a product announcement, it's an engineering starting point that's runnable, documented, and easy to fork.
Quantum computers capable of breaking public-key cryptography do not exist today. The threat, however, is already active.
Adversaries (nation-states, sophisticated criminal organizations) are recording encrypted network traffic now under a strategy known as Store-Now-Decrypt-Later (SNDL). When a Cryptographically-Relevant Quantum Computer (CRQC) becomes available, the archived traffic can be decrypted retroactively. NIST IR 8547 calls for vulnerable algorithms to be deprecated for new systems by 2030 and the transition to be complete by 2035; NSA CNSA 2.0 requires quantum-resistant algorithms in national-security systems by 2030.
For any Alfresco deployment that holds healthcare records (50-year retention), legal documents (indefinite), financial records (7–20 years), classified government files, or competitively-valued IP, the SNDL exploitation window is already open.
A note on RSA-4096. Larger RSA keys do not buy meaningful quantum resistance. Shor algorithm scales linearly with key size in qubits, not exponentially: breaking RSA-4096 needs roughly twice the qubits of RSA-2048: a hardware increment, not a research breakthrough.
Increasing the RSA key size delays quantum attacks by months, not years.
What is safe? AES-256, SHA-256/384/512, and SCRAM-SHA-256 are all post-quantum-secure: Grover algorithm only halves their effective security, and 128 bits of post-quantum margin is comfortable through at least 2050. The migration target is entirely the asymmetric cryptography in TLS handshakes and certificate signatures.
A default Alfresco deployment performs at least seven TLS handshakes that all rely on Shor-vulnerable algorithms:
Every one of these uses RSA, ECDH, or X25519 for key exchange and RSA for certificate signatures. Every one is an SNDL target. The browser-to-nginx path is by far the most valuable to record, it carries authenticated user traffic across untrusted networks, but every internal handshake also leaks session keys to a future CRQC. Worse, the traditional Alfresco metadata-encryption default uses DESede, which provides 112 bits of effective security and is deprecated by NIST SP 800-131A since 2015. The quantum threat is the reason to act on TLS now; DESede is the reason we should already have acted.
DESede provides 112 bits of effective security and is deprecated by NIST SP 800-131A since 2015.
A runnable ACS 26.1 stack, two Docker variants on a shared certificate infrastructure. The browser-to-nginx ingress is fully post-quantum protected today via OQS-patched nginx. Every Java internal path is wired to upgrade to hybrid X25519MLKEM768 the day JDK 27 ships, with no application code change.
| Path | Default Alfresco | This project (today) |
|---|---|---|
| Browser → nginx | TLS 1.2/1.3, X25519 ECDH | TLS 1.3 only, X25519MLKEM768 |
| nginx → Alfresco :8443 | Plain HTTP proxy | mTLS, TLS 1.3, X25519MLKEM768 |
| Alfresco → PostgreSQL | TLS optional, TLS 1.2 allowed | TLS 1.3 minimum, client cert required |
| Alfresco → Solr | mTLS, TLS 1.2, X25519 | mTLS, TLS 1.3, X25519MLKEM768 |
| Alfresco → Transform | HTTPS, TLS 1.2 locked | mTLS, TLS 1.3, X25519MLKEM768 |
| Alfresco → ActiveMQ | SSL, no client cert check | mTLS, TLS 1.3, X25519MLKEM768 |
| Metadata at rest | DESede (112-bit, broken classically) | AES-256 (128-bit PQ margin) |
| Certificate signatures | RSA-2048 | RSA-4096 (transition) or ML-DSA-65 (FIPS 204) (pq-hybrid) |
Honest framing. This is a community engineering project, not an official Hyland reference architecture. Production deployment requires the same review as any third-party hardening pattern.
JEP 527 (the OpenJDK proposal that adds X25519MLKEM768 as a default named group in SunJSSE) ships in JDK 27. Until then, X25519MLKEM768 simply does not exist in the JDK 17/21 TLS stack that ACS 26.1 base images use. Bouncy Castle JSSE 1.84 fills that gap by providing its own implementation of javax.net.ssl and reading the jdk.tls.namedGroups JVM property to advertise hybrid groups in the ClientHello.
Each Java service receives four BC JARs and a one-line JVM flag
# JVM property — same on every service
-Djdk.tls.namedGroups=X25519MLKEM768,x25519,secp256r1
# Bouncy Castle 1.84 JARs added to the classpath
bctls-jdk18on-1.84.jar
bcprov-jdk18on-1.84.jar
bcutil-jdk18on-1.84.jar
bcpkix-jdk18on-1.84.jar
The most important detail is provider ordering: BC JSSE is registered as security provider 2, not 1.
BC JSSE is registered as provider 2, not 1, to avoid PKCS12 keystore conflicts with SunJSSE.
Putting BC JSSE at position 1 makes Tomcat fail to load PKCS12 keystores with UnrecoverableKeyException, because BC PKCS12 implementation has different password-verification semantics than Sun. At position 2, SunJSSE handles keystore unlock (which it does well) and BC JSSE handles TLS group negotiation (which SunJSSE on JDK 17/21/24 cannot). It sounds simple, but I lost a couple of days finding it; the trade-off is documented in docs/jdk24-approach.md so nobody else has to.
The classpath injection differs per service: Tomcat's lib/, Solr's server/lib/ext/, Transform's Spring Boot CLASSPATH env var, and ActiveMQ's lib/. Each is a one-liner in the per-service Dockerfile. The Bouncy Castle entry and the JVM flag are the only two real changes: application code, Alfresco configuration, and existing keystores are unchanged.
It is also worth distinguishing JEP 496 from JEP 527. JEP 496 (delivered in JDK 24) added the ML-KEM cryptographic primitive: KeyPairGenerator.getInstance("ML-KEM-768"). JEP 527 wires that primitive into the TLS stack. With JDK 24 alone, you have the building blocks but no TLS integration. BC JSSE 1.84 supplies that integration today.
JDK 27 GA is expected in September 2026. Once Alfresco publishes base images on it (typically 3–6 months later, projected Q1 2027), every internal Java handshake automatically negotiates X25519MLKEM768. The migration is three steps:
jdk27 tag.Dockerfile.-Djdk.tls.namedGroups JVM properties.No application code changes. The certificate infrastructure is shared between the two variants: scripts/generate-certs.sh and the contents of docker/keystore/ work unchanged. Adopting docker-jdk24/ today buys SNDL protection on the externally-recorded path and commits zero technical debt for the JDK 27 migration.
A "post-quantum-ready" deployment that silently downgrades to classical TLS is worse than useless, it gives operators a false sense of security. The project ships an automated posture check, scripts/verify-pq.sh:
✅ nginx:443 — TLS 1.3 negotiated
✅ nginx:443 — TLS 1.2 rejected (protocol_version alert)
✅ nginx — HSTS, X-Frame-Options, CSP headers present
✅ nginx→Alfresco:8443 mTLS proxy — HTTP 200 (readyProbe: Success)
✅ Alfresco — No DESede in logs (AES-256 metadata encryption confirmed)
✅ ActiveMQ:61616 — needClientAuth=true (mTLS enforced)
✅ ActiveMQ:61616 — TLS 1.3 enforced
✅ PostgreSQL:5432 — ssl_min_protocol_version=TLSv1.3
✅ alfresco→solr:8983 — X25519MLKEM768 negotiated
✅ alfresco→transform:8090 — X25519MLKEM768 negotiated
⚠️ Certificate signatures — RSA-4096 (ML-DSA-65 via --profile pq-hybrid)
The single ⚠️ is intentional. Certificates are signed with RSA-4096 by default because PostgreSQL JDBC 42.7.x cannot parse ML-DSA-65 private keys for the sslkey= parameter, and Java keytool cannot import ML-DSA-65 keys into the JKS format ActiveMQ expects. Switching to --profile pq-hybrid gives ML-DSA-65 signatures on the paths that support them, but breaks the two that do not. Once those drivers update, flipping the profile is a one-flag rebuild: no data re-encryption needed.
Fallback must be observable. Otherwise a supposedly post-quantum-ready environment could silently operate in classical-only mode.
A second script, scripts/scan-tls.sh, gives a full TLS inventory across every service port: protocol version, cipher suite, negotiated group, certificate expiry. Both scripts are intended to run in CI as well as on a fresh deployment.
The project is honest about what it does not yet cover. The gaps are mostly ecosystem dependencies, not project decisions:
| Gap | Current state | Waiting on |
|---|---|---|
| Java service-to-service hybrid KEX without BC JSSE | BC JSSE 1.84 bridge | JDK 27 GA (~Sept 2026) |
| ML-DSA-65 signatures across the whole stack | Available via --profile pq-hybrid for nginx and controlled endpoints |
PostgreSQL JDBC ≥ 42.8 with ML-DSA support; ActiveMQ PKCS12 + ML-DSA |
| Dual-signed (RSA + ML-DSA) certificates for transition | Not implemented | IETF draft-ietf-lamps-pq-composite-sigs |
| Search Enterprise (OpenSearch / Elasticsearch) coverage | Not exercised | Community testing |
| Identity Service (Keycloak) and S3 connector coverage | Not exercised | Community testing |
The repo deliberately describes itself as post-quantum-ready, not post-quantum-compliant. The browser-to-nginx ingress is fully PQ-protected today, and that genuinely closes the highest-value SNDL target in a typical Alfresco deployment. The internal Java paths are mitigated as far as a JDK 17/21 base image will allow, with a clean three-step path to native hybrid TLS once JDK 27 ships. The project under-claims on purpose, and the language in your own communications should match.
This becomes a community asset only if other people run it
--profile pq-hybrid against newer driver stacks. If your PostgreSQL JDBC or ActiveMQ build has progressed past the 42.7.x / JKS limits documented in the repo, that is a data point worth publishing. Paste a verify-pq.sh output into a discussion thread.alfresco-ssl-generator. The current generator is RSA-key-size-centric: it asks for "key size 2048 or 4096" rather than "algorithm profile." Replacing that with a cryptoProfile model (transition-hybrid, pq-hybrid, strict-pq) is the single largest crypto-agility blocker in the Alfresco ecosystem, and it is the foundation everything else has to build on.The repo is at github.com/aborroy/quantum-resistant-alfresco-tls. Open an issue, send a pull request, or paste your verify-pq.sh output into a discussion thread. The fastest way to find what is broken is to run it where it has never been run.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.