Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b7rnC7jppGGdHGESvy
kdf
Companion code for the ankurm.com article Key Derivation Function API in Java (JEP 510): HKDF Explained with Real Examples.
javax.crypto.KDF was a preview API in JDK 24 (JEP 478) and is final in JDK 25 (JEP 510): no --enable-preview. The JDK ships exactly three algorithms,
all HKDF (RFC 5869), all from the SunJCE provider: HKDF-SHA256, HKDF-SHA384, HKDF-SHA512. Everything here ran on Temurin 25.0.5+8; no dependencies except
the comparison tools (Python cryptography 50.0.1 and the openssl 3.0.13 command line) for the interop steps.
| File | What it shows | Output |
|---|---|---|
| (script) | javap of KDF, KDFParameters, KDFSpi, HKDFParameterSpec and its nested types; proof it is not a preview API |
output/01-api-shape.txt |
src/ApiTour.java |
registered algorithms, getInstance failures, the three spec shapes (extract+expand, extract only, expand only), several IKM pieces, deriveKey vs Cipher.init key sizes |
output/02-api-tour.txt |
src/Rfc5869Vectors.java |
RFC 5869 appendix A cases 1 to 3 (SHA-256) asserted three ways each: OKM, PRK, expand-only from the PRK | output/03-rfc5869-vectors.txt |
src/RefHkdf.java, src/RefCheck.java, src/Interop.java, interop/cases.tsv |
HKDF written by hand from the RFC with javax.crypto.Mac, checked against the RFC, then JDK vs hand-written vs Python cryptography on 300 generated cases (SHA-256/384/512) |
output/04-interop-java-vs-python-vs-reference.txt |
interop/gen_cases.py, interop/openssl_check.py |
how cases.tsv was made (fixed seed, regenerates byte-identically) and a third opinion from openssl kdf |
output/05-interop-python-openssl.txt |
src/Tenants.java |
per-tenant, per-purpose, per-version keys from one master key; length-prefixed info; AES-GCM isolation |
output/06-tenants.txt |
src/Mistakes.java |
same info twice, ambiguous concatenation, salt facts, the 255 x HashLen limit, a 4-digit PIN through HKDF vs PBKDF2 |
output/07-mistakes.txt |
JDK25=/path/to/jdk-25 ./run.sh # needs python3 with `cryptography`, and openssl, for output/05
The demos assert as they go (PASS/FAIL lines, non-zero exit on failure), following the other standalone modules in this repo; there is no JUnit here. Output is byte-stable.
Not reproduced: JDK 24's preview behaviour (no JDK 24 was installed), and openssl was only run with 3.0.13.