Start of changes for service refresh 6

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.

From service refresh 6, the IBMJCEPlus provider supports HKDF functionality through the KeyGeneration algorithm. The IBMJCEPlus provider allows applications to first specify a parameter specification, then request an HKDF-derived key by using the KeyGenerator mechanism.

The IBMJCEPlus provider supports the following HKDF algorithm parameter specifications, each of which implements the Java™ public interface java.security.spec.AlgorithmParameterSpec.
  • HKDFExtractParameterSpec
  • HKDFExpandParameterSpec
  • HKDFParameterSpec
For more information, see the JCE API reference.
The IBMJCEPlus 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
Note: The IBMJCEPlusFIPS provider does not support HKDF.
The following SampleHKDF.java file shows how to perform an HKDF extract and expand on a secret key by using the IBMJCEPlus provider:
/*
 * %Z%%M%    %I%  %W%  %G%  %U%
 * ========================================================================
 *  IBM SDK, Java(tm) Technology Edition, v8
 *  (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;
        }
End of changes for service refresh 6