$ java -cp classes ApiTour (25.0.5+8-LTS) == KDF algorithms registered by the providers of this JDK SunJCE HKDF-SHA256 SunJCE HKDF-SHA384 SunJCE HKDF-SHA512 == getInstance: what works and what does not algorithm=HKDF-SHA256 provider=SunJCE parameters=null HKDF -> NoSuchAlgorithmException: No available provider supports the specified algorithm HKDF-SHA1 -> NoSuchAlgorithmException: No available provider supports the specified algorithm hkdf-sha256 -> available PBKDF2WithHmacSHA256 -> NoSuchAlgorithmException: No available provider supports the specified algorithm Argon2 -> NoSuchAlgorithmException: No available provider supports the specified algorithm == Shape 1: extract then expand in one call (the common case) deriveData -> byte[42] 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865 deriveKey -> algorithm=AES format=RAW bytes=32 == Shape 2: extract only (returns the pseudorandom key, PRK) PRK = 077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5 == Shape 3: expand only (you already hold a uniformly random key) PASS extractOnly + expandOnly equals the one-call result == The same KDF object can be used again, and gives the same bytes PASS second call with the same spec is identical == Several IKM or salt pieces are concatenated in the order added PASS addIKM(first 10) + addIKM(last 12) == addIKM(all 22) == deriveKey does not check key sizes; Cipher.init does deriveKey("AES") length 16 -> ok, 16 bytes Cipher.init -> ok deriveKey("AES") length 24 -> ok, 24 bytes Cipher.init -> ok deriveKey("AES") length 32 -> ok, 32 bytes Cipher.init -> ok deriveKey("AES") length 20 -> ok, 20 bytes Cipher.init -> InvalidKeyException: Invalid AES key length: 20 bytes ALL CHECKS PASSED exit=0