In the application I\'m writing I need to do HKDF to derive two different keys from one password. Searching for examples on how to it in Java I found these two:
No, Hashed Message Authentication Code (HMAC)-based key derivation function (HKDF) has, like most KDFs, no standard implementation in JCA (as of 2020).
There are some implementations embedded in other projects (like you already said):
Also there is, of course, Bouncy Castle which use their own Hmac/Mac implementations and APIs. BC is however a massive dependency, and may be unpractical for e.g. embedded or mobile use case. For this I implemented a standalone, lightweight java lib (passing all of the RFC 5869 test vectors), which works with any javax.crypto.Mac instance:
If you prefer, you could, of course implement it on your own, it's a fairly straight forward spec, when using the built-in JCA Hmac implementation.
From the RFC 5869:
While the 'info' value is optional in the definition of HKDF, it is
often of great importance in applications. Its main objective is to
bind the derived key material to application- and context-specific
information. (...) In particular, it may prevent the derivation of the same keying material for different contexts.
So for example if you want to derive an Secret Key and IV from the same source material you would use the info parameter (using this lib):
//example input
String userInput = "this is a user input with bad entropy";
HKDF hkdf = HKDF.fromHmacSha256();
//extract the "raw" data to create output with concentrated entropy
byte[] pseudoRandomKey = hkdf.extract(staticSalt32Byte, userInput.getBytes(StandardCharsets.UTF_8));
//create expanded bytes for e.g. AES secret key and IV
byte[] expandedAesKey = hkdf.expand(pseudoRandomKey, "aes-key".getBytes(StandardCharsets.UTF_8), 16);
byte[] expandedIv = hkdf.expand(pseudoRandomKey, "aes-iv".getBytes(StandardCharsets.UTF_8), 16);
//Example boilerplate encrypting a simple string with created key/iv
SecretKey key = new SecretKeySpec(expandedAesKey, "AES"); //AES-128 key
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(expandedIv));
byte[] encrypted = cipher.doFinal("my secret message".getBytes(StandardCharsets.UTF_8));