HMAC-based extract-then-expand key derivation function (HKDF)
Hashed message authentication code (HMAC)-based key derivation function (HKDF) was designed as a standard KDF for use in various protocols and applications. You can use HKDF to extract, then expand, a key as a sequence of two operations, or to extract and expand a key in a single operation.
The OpenJCEPlus provider supports HKDF functionality through the KeyGeneration algorithm. The OpenJCEPlus provider allows applications to first specify a parameter specification, then request an HKDF-derived key by using the KeyGenerator mechanism.
The OpenJCEPlus provider supports the
following HKDF algorithm parameter specifications, each of which implements the Java™ public interface java.security.spec.AlgorithmParameterSpec.
- HKDFExtractParameterSpec
- HKDFExpandParameterSpec
- HKDFParameterSpec
The OpenJCEPlus provider supports the
following HKDF algorithm names:
- kda-hkdf-with-sha1, kda-hkdf-with-sha-1
- kda-hkdf-with-sha224, kda-hkdf-with-sha-224
- kda-hkdf-with-sha256, kda-hkdf-with-sha-256
- kda-hkdf-with-sha384, kda-hkdf-with-sha-384
- kda-hkdf-with-sha512, kda-hkdf-with-sha-512
The following
SampleHKDF.java
file shows how to perform an HKDF extract and
expand on a secret key by using the OpenJCEPlus provider:/*
* %Z%%M% %I% %W% %G% %U%
* ========================================================================
* IBM Semeru Runtime Certified Edition for z/OS
* (C) Copyright IBM Corp. 2023 All Rights Reserved
*
* The source code has been provided for illustrative purposes only.
* ===========================================================================
*/
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import ibm.security.internal.spec.HKDFExpandParameterSpec;
import ibm.security.internal.spec.HKDFExtractParameterSpec;
final class SampleHKDF {
private String hkdfAlg=null;
static Provider provider = null;
/**
* Create an HDKF object, specifying the underlying message digest algorithm.
*
* @param hashAlg a standard name corresponding to a supported message digest
* algorithm.
*
* @throws NoSuchAlgorithmException if that message digest algorithm does not
* have an HMAC variant supported on any
* available provider.
*/
SampleHKDF(String hashAlg, Provider provider) throws NoSuchAlgorithmException {
this.hkdfAlg = "kda-hkdf-with-" + hashAlg.toLowerCase();
this.provider = provider;
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value as cleartext bytes. A {@code null} value is
* allowed, which will internally use an array of zero bytes the
* same size as the underlying hash output length.
* @param inputKey the input keying material provided as a {@code SecretKey}.
* @param keyAlg the algorithm name assigned to the resulting
* {@code SecretKey} object.
*
* @return a {@code SecretKey} that is the result of the HKDF extract operation.
*
* @throws InvalidKeyException if the {@code salt} parameter cannot be used to
* initialize the underlying HMAC.
*/
SecretKey extract(SecretKey inputKey, byte[] salt, String keyAlg) throws InvalidKeyException {
KeyGenerator hkdfExtractGenerator;
try {
hkdfExtractGenerator = KeyGenerator.getInstance(hkdfAlg, this.provider);
} catch (NoSuchAlgorithmException e) {
throw new InvalidKeyException(e.getMessage());
}
HKDFExtractParameterSpec extractSpec = new HKDFExtractParameterSpec(inputKey.getEncoded(), salt, keyAlg);
try {
hkdfExtractGenerator.init(extractSpec);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException(e.getMessage());
}
return (hkdfExtractGenerator.generateKey());
}
/**
* Perform the HKDF-Expand derivation for a single-key output.
*
* @param pseudoRandKey the pseudo random key (PRK).
* @param info optional context-specific info. A {@code null} value is
* allowed in which case a zero-length byte array will be
* used.
* @param outLen the length of the resulting {@code SecretKey}
* @param keyAlg the algorithm name applied to the resulting
* {@code SecretKey}
*
* @return the resulting key derivation as a {@code SecretKey} object
*
* @throws InvalidKeyException if the underlying HMAC operation cannot be
* initialized using the provided
* {@code pseudoRandKey} object.
*/
SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen, String keyAlg) throws InvalidKeyException {
KeyGenerator hkdfExpandGenerator;
try {
hkdfExpandGenerator = KeyGenerator.getInstance(hkdfAlg, this.provider);
} catch (NoSuchAlgorithmException e) {
throw new InvalidKeyException(e.getMessage());
}
HKDFExpandParameterSpec expandSpec = new HKDFExpandParameterSpec(pseudoRandKey.getEncoded(), info, outLen,
keyAlg);
try {
hkdfExpandGenerator.init(expandSpec);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException(e.getMessage());
}
return (hkdfExpandGenerator.generateKey());
}
public static void main(String[] args) throws Exception {
byte[] salt = hexStringToByteArray("000102030405060708090a0b0c");
byte[] info = hexStringToByteArray("f0f1f2f3f4f5f6f7f8f9");
provider = loadProviderIBMJCEPlus();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey psk = keyGen.generateKey();
SampleHKDF sampleHKDF = new SampleHKDF("SHA256", provider);
SecretKey extractKey = sampleHKDF.extract(psk,salt,"AES");
SecretKey expandKey = sampleHKDF.expand(extractKey, info, 42, "AES");
System.out.println("extractKey = " + extractKey);
System.out.println("expandKey = " + expandKey);
}
public static Provider loadProviderIBMJCEPlus() throws Exception {
Provider provider = java.security.Security.getProvider("IBMJCEPlus");
if (provider == null) {
provider = (Provider) Class.forName("com.ibm.crypto.plus.provider.IBMJCEPlus").newInstance();
}
return provider;
}
public static byte[] hexStringToByteArray(String string) {
String s = string.trim().replaceAll(" +", ""); // remove all spaces
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}