8353578: Refactor existing usage of internal HKDF impl to use the KDF API

Co-authored-by: Kevin Driver <kdriver@openjdk.org>
Reviewed-by: djelinski, wetmore, mullan, kdriver, weijun
This commit is contained in:
Valerie Peng 2025-05-13 22:43:31 +00:00
parent 10dcdf1b47
commit 4c0a0ab6bc
24 changed files with 368 additions and 782 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,11 +37,13 @@ import java.util.Arrays;
import java.util.Objects;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.HKDFParameterSpec;
import sun.security.jca.JCAUtil;
import sun.security.ssl.HKDF;
import sun.security.util.*;
import jdk.internal.access.SharedSecrets;
// Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html,
// without the AuthEncap and AuthDecap functions
public class DHKEM implements KEMSpi {
@ -153,19 +155,19 @@ public class DHKEM implements KEMSpi {
private enum Params {
P256(0x10, 32, 32, 2 * 32 + 1,
"ECDH", "EC", CurveDB.P_256, "SHA-256"),
"ECDH", "EC", CurveDB.P_256, "HKDF-SHA256"),
P384(0x11, 48, 48, 2 * 48 + 1,
"ECDH", "EC", CurveDB.P_384, "SHA-384"),
"ECDH", "EC", CurveDB.P_384, "HKDF-SHA384"),
P521(0x12, 64, 66, 2 * 66 + 1,
"ECDH", "EC", CurveDB.P_521, "SHA-512"),
"ECDH", "EC", CurveDB.P_521, "HKDF-SHA512"),
X25519(0x20, 32, 32, 32,
"XDH", "XDH", NamedParameterSpec.X25519, "SHA-256"),
"XDH", "XDH", NamedParameterSpec.X25519, "HKDF-SHA256"),
X448(0x21, 64, 56, 56,
"XDH", "XDH", NamedParameterSpec.X448, "SHA-512"),
"XDH", "XDH", NamedParameterSpec.X448, "HKDF-SHA512"),
;
private final int kem_id;
@ -247,10 +249,17 @@ public class DHKEM implements KEMSpi {
private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context)
throws NoSuchAlgorithmException, InvalidKeyException {
HKDF kdf = new HKDF(hkdfAlgorithm);
SecretKey eae_prk = LabeledExtract(kdf, suiteId, null, EAE_PRK, dh);
return LabeledExpand(kdf, suiteId, eae_prk, SHARED_SECRET,
kem_context, Nsecret);
KDF hkdf = KDF.getInstance(hkdfAlgorithm);
SecretKey eae_prk = LabeledExtract(hkdf, suiteId, EAE_PRK, dh);
try {
return LabeledExpand(hkdf, suiteId, eae_prk, SHARED_SECRET,
kem_context, Nsecret);
} finally {
if (eae_prk instanceof SecretKeySpec s) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(s);
}
}
}
private PublicKey getPublicKey(PrivateKey sk)
@ -277,31 +286,41 @@ public class DHKEM implements KEMSpi {
// For KAT tests only. See RFC9180DeriveKeyPairSR.
public KeyPair deriveKeyPair(byte[] ikm) throws Exception {
HKDF kdf = new HKDF(hkdfAlgorithm);
SecretKey dkp_prk = LabeledExtract(kdf, suiteId, null, DKP_PRK, ikm);
if (isEC()) {
NamedCurve curve = (NamedCurve) spec;
BigInteger sk = BigInteger.ZERO;
int counter = 0;
while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) {
if (counter > 255) {
throw new RuntimeException();
KDF hkdf = KDF.getInstance(hkdfAlgorithm);
SecretKey dkp_prk = LabeledExtract(hkdf, suiteId, DKP_PRK, ikm);
try {
if (isEC()) {
NamedCurve curve = (NamedCurve) spec;
BigInteger sk = BigInteger.ZERO;
int counter = 0;
while (sk.signum() == 0 ||
sk.compareTo(curve.getOrder()) >= 0) {
if (counter > 255) {
throw new RuntimeException();
}
byte[] bytes = LabeledExpand(hkdf, suiteId, dkp_prk,
CANDIDATE, I2OSP(counter, 1), Nsk);
// bitmask is defined to be 0xFF for P-256 and P-384,
// and 0x01 for P-521
if (this == Params.P521) {
bytes[0] = (byte) (bytes[0] & 0x01);
}
sk = new BigInteger(1, (bytes));
counter = counter + 1;
}
byte[] bytes = LabeledExpand(kdf, suiteId, dkp_prk,
CANDIDATE, I2OSP(counter, 1), Nsk);
// bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521
if (this == Params.P521) {
bytes[0] = (byte) (bytes[0] & 0x01);
}
sk = new BigInteger(1, (bytes));
counter = counter + 1;
PrivateKey k = DeserializePrivateKey(sk.toByteArray());
return new KeyPair(getPublicKey(k), k);
} else {
byte[] sk = LabeledExpand(hkdf, suiteId, dkp_prk, SK, EMPTY,
Nsk);
PrivateKey k = DeserializePrivateKey(sk);
return new KeyPair(getPublicKey(k), k);
}
} finally {
if (dkp_prk instanceof SecretKeySpec s) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(s);
}
PrivateKey k = DeserializePrivateKey(sk.toByteArray());
return new KeyPair(getPublicKey(k), k);
} else {
byte[] sk = LabeledExpand(kdf, suiteId, dkp_prk, SK, EMPTY, Nsk);
PrivateKey k = DeserializePrivateKey(sk);
return new KeyPair(getPublicKey(k), k);
}
}
@ -380,18 +399,32 @@ public class DHKEM implements KEMSpi {
}
}
private static SecretKey LabeledExtract(HKDF kdf, byte[] suite_id,
byte[] salt, byte[] label, byte[] ikm) throws InvalidKeyException {
return kdf.extract(salt,
new SecretKeySpec(concat(HPKE_V1, suite_id, label, ikm), "IKM"),
"HKDF-PRK");
private static SecretKey LabeledExtract(KDF hkdf, byte[] suite_id,
byte[] label, byte[] ikm) throws InvalidKeyException {
SecretKeySpec s = new SecretKeySpec(concat(HPKE_V1, suite_id, label,
ikm), "IKM");
try {
HKDFParameterSpec spec =
HKDFParameterSpec.ofExtract().addIKM(s).extractOnly();
return hkdf.deriveKey("Generic", spec);
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException e) {
throw new InvalidKeyException(e.getMessage(), e);
} finally {
SharedSecrets.getJavaxCryptoSpecAccess().clearSecretKeySpec(s);
}
}
private static byte[] LabeledExpand(HKDF kdf, byte[] suite_id,
private static byte[] LabeledExpand(KDF hkdf, byte[] suite_id,
SecretKey prk, byte[] label, byte[] info, int L)
throws InvalidKeyException {
byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1,
suite_id, label, info);
return kdf.expand(prk, labeled_info, L, "NONE").getEncoded();
byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, suite_id, label,
info);
try {
return hkdf.deriveData(HKDFParameterSpec.expandOnly(
prk, labeled_info, L));
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException(iape.getMessage(), iape);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -77,22 +77,20 @@ final class ChangeCipherSpec {
try {
writeAuthenticator = Authenticator.valueOf(
hc.negotiatedProtocol, ncs.macAlg,
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
tkd.deriveKey(hc.sslConfig.isClientMode ?
"clientMacKey" : "serverMacKey"));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
// unlikely
throw new SSLException("Algorithm missing: ", e);
}
}
SecretKey writeKey =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"clientWriteKey" : "serverWriteKey");
SecretKey writeIv =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"clientWriteIv" : "serverWriteIv");
SecretKey writeKey = tkd.deriveKey(hc.sslConfig.isClientMode ?
"clientWriteKey" : "serverWriteKey");
byte[] writeIv = tkd.deriveData(hc.sslConfig.isClientMode ?
"clientWriteIv" : "serverWriteIv");
IvParameterSpec iv = (writeIv == null) ? null :
new IvParameterSpec(writeIv.getEncoded());
new IvParameterSpec(writeIv);
SSLWriteCipher writeCipher;
try {
writeCipher = ncs.bulkCipher.createWriteCipher(
@ -173,7 +171,7 @@ final class ChangeCipherSpec {
try {
readAuthenticator = Authenticator.valueOf(
hc.negotiatedProtocol, ncs.macAlg,
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
tkd.deriveKey(hc.sslConfig.isClientMode ?
"serverMacKey" : "clientMacKey"));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
// unlikely
@ -181,14 +179,12 @@ final class ChangeCipherSpec {
}
}
SecretKey readKey =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"serverWriteKey" : "clientWriteKey");
SecretKey readIv =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"serverWriteIv" : "clientWriteIv");
SecretKey readKey = tkd.deriveKey(hc.sslConfig.isClientMode ?
"serverWriteKey" : "clientWriteKey");
byte[] readIv = tkd.deriveData(hc.sslConfig.isClientMode ?
"serverWriteIv" : "clientWriteIv");
IvParameterSpec iv = (readIv == null) ? null :
new IvParameterSpec(readIv.getEncoded());
new IvParameterSpec(readIv);
SSLReadCipher readCipher;
try {
readCipher = ncs.bulkCipher.createReadCipher(

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1199,11 +1199,13 @@ enum CipherSuite {
final String name;
final int hashLength;
final int blockSize;
final String hkdfAlgorithm;
HashAlg(String hashAlg, int hashLength, int blockSize) {
this.name = hashAlg;
this.hashLength = hashLength;
this.blockSize = blockSize;
this.hkdfAlgorithm = "HKDF-" + hashAlg.replace("-", "");
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -206,8 +206,7 @@ final class DHClientKeyExchange {
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
@ -302,8 +301,7 @@ final class DHClientKeyExchange {
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -218,8 +218,7 @@ final class ECDHClientKeyExchange {
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
@ -338,8 +337,7 @@ final class ECDHClientKeyExchange {
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
@ -418,8 +416,7 @@ final class ECDHClientKeyExchange {
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
@ -522,8 +519,7 @@ final class ECDHClientKeyExchange {
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -32,14 +32,14 @@ import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.text.MessageFormat;
import java.util.Locale;
import javax.crypto.KDF;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLPeerUnverifiedException;
import jdk.internal.event.EventHelper;
@ -47,11 +47,11 @@ import jdk.internal.event.TLSHandshakeEvent;
import sun.security.internal.spec.TlsPrfParameterSpec;
import sun.security.ssl.CipherSuite.HashAlg;
import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
import sun.security.ssl.SSLBasicKeyDerivation.SecretSizeSpec;
import sun.security.ssl.SSLCipher.SSLReadCipher;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.util.HexDumpEncoder;
import sun.security.util.KeyUtil;
/**
* Pack of the Finished handshake message.
@ -338,12 +338,8 @@ final class Finished {
SecretKey secret = isValidation ?
context.baseReadSecret : context.baseWriteSecret;
SSLBasicKeyDerivation kdf = new SSLBasicKeyDerivation(
secret, hashAlg.name,
hkdfLabel, hkdfContext, hashAlg.hashLength);
AlgorithmParameterSpec keySpec =
new SecretSizeSpec(hashAlg.hashLength);
SecretKey finishedSecret =
kdf.deriveKey("TlsFinishedSecret", keySpec);
secret, hashAlg, hkdfLabel, hkdfContext);
SecretKey finishedSecret = kdf.deriveKey("TlsFinishedSecret");
String hmacAlg =
"Hmac" + hashAlg.name.replace("-", "");
@ -354,6 +350,8 @@ final class Finished {
} catch (NoSuchAlgorithmException |InvalidKeyException ex) {
throw new ProviderException(
"Failed to generate verify_data", ex);
} finally {
KeyUtil.destroySecretKeys(finishedSecret);
}
}
}
@ -717,17 +715,14 @@ final class Finished {
try {
// update the application traffic read keys.
SecretKey writeSecret = kd.deriveKey(
"TlsClientAppTrafficSecret", null);
SecretKey writeSecret =
kd.deriveKey("TlsClientAppTrafficSecret");
SSLKeyDerivation writeKD =
kdg.createKeyDerivation(chc, writeSecret);
SecretKey writeKey = writeKD.deriveKey(
"TlsKey", null);
SecretKey writeIvSecret = writeKD.deriveKey(
"TlsIv", null);
SecretKey writeKey = writeKD.deriveKey("TlsKey");
IvParameterSpec writeIv =
new IvParameterSpec(writeIvSecret.getEncoded());
new IvParameterSpec(writeKD.deriveData("TlsIv"));
SSLWriteCipher writeCipher =
chc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
Authenticator.valueOf(chc.negotiatedProtocol),
@ -754,7 +749,7 @@ final class Finished {
// it can be used after the handshake is completed.
SSLSecretDerivation sd = ((SSLSecretDerivation) kd).forContext(chc);
SecretKey resumptionMasterSecret = sd.deriveKey(
"TlsResumptionMasterSecret", null);
"TlsResumptionMasterSecret");
chc.handshakeSession.setResumptionMasterSecret(
resumptionMasterSecret);
@ -803,33 +798,29 @@ final class Finished {
shc.negotiatedProtocol);
}
// derive salt secret
SecretKey saltSecret = null;
try {
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
// derive salt secret
saltSecret = kd.deriveKey("TlsSaltSecret");
// derive application secrets
HashAlg hashAlg = shc.negotiatedCipherSuite.hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
byte[] zeros = new byte[hashAlg.hashLength];
SecretKeySpec sharedSecret =
new SecretKeySpec(zeros, "TlsZeroSecret");
SecretKey masterSecret =
hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
SecretKey masterSecret = hkdf.deriveKey("TlsMasterSecret",
HKDFParameterSpec.ofExtract().addSalt(saltSecret)
.addIKM(zeros).extractOnly());
SSLKeyDerivation secretKD =
new SSLSecretDerivation(shc, masterSecret);
// update the handshake traffic write keys.
SecretKey writeSecret = secretKD.deriveKey(
"TlsServerAppTrafficSecret", null);
"TlsServerAppTrafficSecret");
SSLKeyDerivation writeKD =
kdg.createKeyDerivation(shc, writeSecret);
SecretKey writeKey = writeKD.deriveKey(
"TlsKey", null);
SecretKey writeIvSecret = writeKD.deriveKey(
"TlsIv", null);
SecretKey writeKey = writeKD.deriveKey("TlsKey");
IvParameterSpec writeIv =
new IvParameterSpec(writeIvSecret.getEncoded());
new IvParameterSpec(writeKD.deriveData("TlsIv"));
SSLWriteCipher writeCipher =
shc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
Authenticator.valueOf(shc.negotiatedProtocol),
@ -852,6 +843,8 @@ final class Finished {
} catch (GeneralSecurityException gse) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive application secrets", gse);
} finally {
KeyUtil.destroySecretKeys(saltSecret);
}
/*
@ -960,34 +953,31 @@ final class Finished {
engineGetClientSessionContext()).
put(chc.handshakeSession);
}
// derive salt secret
SecretKey saltSecret = null;
try {
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
// derive salt secret
saltSecret = kd.deriveKey("TlsSaltSecret");
// derive application secrets
HashAlg hashAlg = chc.negotiatedCipherSuite.hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
byte[] zeros = new byte[hashAlg.hashLength];
SecretKeySpec sharedSecret =
new SecretKeySpec(zeros, "TlsZeroSecret");
SecretKey masterSecret =
hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
SecretKey masterSecret = hkdf.deriveKey("TlsMasterSecret",
HKDFParameterSpec.ofExtract()
.addSalt(saltSecret)
.addIKM(zeros).extractOnly());
SSLKeyDerivation secretKD =
new SSLSecretDerivation(chc, masterSecret);
// update the handshake traffic read keys.
SecretKey readSecret = secretKD.deriveKey(
"TlsServerAppTrafficSecret", null);
"TlsServerAppTrafficSecret");
SSLKeyDerivation writeKD =
kdg.createKeyDerivation(chc, readSecret);
SecretKey readKey = writeKD.deriveKey(
"TlsKey", null);
SecretKey readIvSecret = writeKD.deriveKey(
"TlsIv", null);
SecretKey readKey = writeKD.deriveKey("TlsKey");
IvParameterSpec readIv =
new IvParameterSpec(readIvSecret.getEncoded());
new IvParameterSpec(writeKD.deriveData("TlsIv"));
SSLReadCipher readCipher =
chc.negotiatedCipherSuite.bulkCipher.createReadCipher(
Authenticator.valueOf(chc.negotiatedProtocol),
@ -1009,6 +999,8 @@ final class Finished {
} catch (GeneralSecurityException gse) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive application secrets", gse);
} finally {
KeyUtil.destroySecretKeys(saltSecret);
}
//
@ -1084,16 +1076,13 @@ final class Finished {
try {
// update the application traffic read keys.
SecretKey readSecret = kd.deriveKey(
"TlsClientAppTrafficSecret", null);
"TlsClientAppTrafficSecret");
SSLKeyDerivation readKD =
kdg.createKeyDerivation(shc, readSecret);
SecretKey readKey = readKD.deriveKey(
"TlsKey", null);
SecretKey readIvSecret = readKD.deriveKey(
"TlsIv", null);
SecretKey readKey = readKD.deriveKey("TlsKey");
IvParameterSpec readIv =
new IvParameterSpec(readIvSecret.getEncoded());
new IvParameterSpec(readKD.deriveData("TlsIv"));
SSLReadCipher readCipher =
shc.negotiatedCipherSuite.bulkCipher.createReadCipher(
Authenticator.valueOf(shc.negotiatedProtocol),
@ -1115,8 +1104,8 @@ final class Finished {
shc.handshakeHash.update();
SSLSecretDerivation sd =
((SSLSecretDerivation)kd).forContext(shc);
SecretKey resumptionMasterSecret = sd.deriveKey(
"TlsResumptionMasterSecret", null);
SecretKey resumptionMasterSecret =
sd.deriveKey("TlsResumptionMasterSecret");
shc.handshakeSession.setResumptionMasterSecret(
resumptionMasterSecret);
} catch (GeneralSecurityException gse) {

View File

@ -1,185 +0,0 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import java.util.Objects;
/**
* An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
* specific to the needs of TLS 1.3 key derivation in JSSE. This is not a
* general purpose HKDF implementation and is suited only to single-key output
* derivations.
*
* HKDF objects are created by specifying a message digest algorithm. That
* digest algorithm will be used by the HMAC function as part of the HKDF
* derivation process.
*/
public final class HKDF {
private final Mac hmacObj;
private final int hmacLen;
/**
* 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.
*/
public HKDF(String hashAlg) throws NoSuchAlgorithmException {
Objects.requireNonNull(hashAlg,
"Must provide underlying HKDF Digest algorithm.");
String hmacAlg = "Hmac" + hashAlg.replace("-", "");
hmacObj = Mac.getInstance(hmacAlg);
hmacLen = hmacObj.getMacLength();
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value, implemented as a {@code SecretKey}. 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.
*/
public SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
}
hmacObj.init(salt);
return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
keyAlg);
}
/**
* 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.
*/
public SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new byte[hmacLen];
}
return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
}
/**
* 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.
*/
public SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
String keyAlg) throws InvalidKeyException {
byte[] kdfOutput;
// Calculate the number of rounds of HMAC that are needed to
// meet the requested data. Then set up the buffers we will need.
Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
// Output from the expand operation must be <= 255 * hmac length
if (outLen > 255 * hmacLen) {
throw new IllegalArgumentException("Requested output length " +
"exceeds maximum length allowed for HKDF expansion");
}
hmacObj.init(pseudoRandKey);
if (info == null) {
info = new byte[0];
}
int rounds = (outLen + hmacLen - 1) / hmacLen;
kdfOutput = new byte[rounds * hmacLen];
int offset = 0;
int tLength = 0;
for (int i = 0; i < rounds ; i++) {
// Calculate this round
try {
// Add T(i). This will be an empty string on the first
// iteration since tLength starts at zero. After the first
// iteration, tLength is changed to the HMAC length for the
// rest of the loop.
hmacObj.update(kdfOutput,
Math.max(0, offset - hmacLen), tLength);
hmacObj.update(info); // Add info
hmacObj.update((byte)(i + 1)); // Add round number
hmacObj.doFinal(kdfOutput, offset);
tLength = hmacLen;
offset += hmacLen; // For next iteration
} catch (ShortBufferException sbe) {
// This really shouldn't happen given that we've
// sized the buffers to their largest possible size up-front,
// but just in case...
throw new RuntimeException(sbe);
}
}
return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,15 +24,18 @@
*/
package sun.security.ssl;
import javax.crypto.KDF;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.HKDFParameterSpec;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import sun.security.util.KeyUtil;
/**
* A common class for creating various KeyDerivation types.
@ -55,29 +58,26 @@ public class KAKeyDerivation implements SSLKeyDerivation {
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
public SecretKey deriveKey(String type) throws IOException {
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
return t12DeriveKey(algorithm, params);
return t12DeriveKey();
} else {
return t13DeriveKey(algorithm, params);
return t13DeriveKey(type);
}
}
/**
* Handle the TLSv1-1.2 objects, which don't use the HKDF algorithms.
*/
private SecretKey t12DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
private SecretKey t12DeriveKey() throws IOException {
SecretKey preMasterSecret = null;
try {
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey preMasterSecret
= ka.generateSecret("TlsPremasterSecret");
SSLMasterKeyDerivation mskd
= SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
preMasterSecret = ka.generateSecret("TlsPremasterSecret");
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(context.negotiatedProtocol);
if (mskd == null) {
// unlikely
throw new SSLHandshakeException(
@ -86,45 +86,55 @@ public class KAKeyDerivation implements SSLKeyDerivation {
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
return kd.deriveKey("MasterSecret");
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
} finally {
KeyUtil.destroySecretKeys(preMasterSecret);
}
}
/**
* Handle the TLSv1.3 objects, which use the HKDF algorithms.
*/
private SecretKey t13DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
private SecretKey t13DeriveKey(String type)
throws IOException {
SecretKey sharedSecret = null;
SecretKey earlySecret = null;
SecretKey saltSecret = null;
try {
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey sharedSecret
= ka.generateSecret("TlsPremasterSecret");
sharedSecret = ka.generateSecret("TlsPremasterSecret");
CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
SSLKeyDerivation kd = context.handshakeKeyDerivation;
HKDF hkdf = new HKDF(hashAlg.name);
if (kd == null) { // No PSK is in use.
// If PSK is not in use Early Secret will still be
// If PSK is not in use, Early Secret will still be
// HKDF-Extract(0, 0).
byte[] zeros = new byte[hashAlg.hashLength];
SecretKeySpec ikm
= new SecretKeySpec(zeros, "TlsPreSharedSecret");
SecretKey earlySecret
= hkdf.extract(zeros, ikm, "TlsEarlySecret");
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
earlySecret = hkdf.deriveKey("TlsEarlySecret",
HKDFParameterSpec.ofExtract().addSalt(zeros)
.addIKM(zeros).extractOnly());
kd = new SSLSecretDerivation(context, earlySecret);
}
// derive salt secret
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
saltSecret = kd.deriveKey("TlsSaltSecret");
// derive handshake secret
return hkdf.extract(saltSecret, sharedSecret, algorithm);
// NOTE: do not reuse the HKDF object for "TlsEarlySecret" for
// the handshake secret key derivation (below) as it may not
// work with the "sharedSecret" obj.
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
return hkdf.deriveKey(type, HKDFParameterSpec.ofExtract()
.addSalt(saltSecret).addIKM(sharedSecret).extractOnly());
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
} finally {
KeyUtil.destroySecretKeys(sharedSecret, earlySecret, saltSecret);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -214,11 +214,11 @@ final class KeyUpdate {
Alert.INTERNAL_ERROR, "no key derivation");
}
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1");
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
SecretKey key = kd.deriveKey("TlsKey", null);
IvParameterSpec ivSpec = new IvParameterSpec(
kd.deriveKey("TlsIv", null).getEncoded());
SecretKey key = kd.deriveKey("TlsKey");
IvParameterSpec ivSpec =
new IvParameterSpec(kd.deriveData("TlsIv"));
try {
SSLReadCipher rc =
hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
@ -293,11 +293,11 @@ final class KeyUpdate {
Alert.INTERNAL_ERROR, "no key derivation");
}
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1");
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
SecretKey key = kd.deriveKey("TlsKey", null);
IvParameterSpec ivSpec = new IvParameterSpec(
kd.deriveKey("TlsIv", null).getEncoded());
SecretKey key = kd.deriveKey("TlsKey");
IvParameterSpec ivSpec =
new IvParameterSpec(kd.deriveData("TlsIv"));
SSLWriteCipher wc;
try {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,7 +31,10 @@ import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import javax.crypto.KDF;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeMode;
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
@ -286,11 +289,16 @@ final class NewSessionTicket {
private static SecretKey derivePreSharedKey(CipherSuite.HashAlg hashAlg,
SecretKey resumptionMasterSecret, byte[] nonce) throws IOException {
try {
HKDF hkdf = new HKDF(hashAlg.name);
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
"tls13 resumption".getBytes(), nonce, hashAlg.hashLength);
return hkdf.expand(resumptionMasterSecret, hkdfInfo,
hashAlg.hashLength, "TlsPreSharedKey");
// SSLSessionImpl.write() uses the PreSharedKey encoding for
// the stateless session ticket; use SecretKeySpec instead of opaque
// Key objects
return new SecretKeySpec(hkdf.deriveData(
HKDFParameterSpec.expandOnly(resumptionMasterSecret,
hkdfInfo, hashAlg.hashLength)), "TlsPreSharedKey");
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not derive PSK", gse);
}

View File

@ -29,8 +29,10 @@ import java.nio.ByteBuffer;
import java.security.*;
import java.text.MessageFormat;
import java.util.*;
import javax.crypto.KDF;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
@ -40,6 +42,7 @@ import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
import sun.security.util.HexDumpEncoder;
import sun.security.util.KeyUtil;
import static sun.security.ssl.SSLExtension.*;
@ -559,11 +562,15 @@ final class PreSharedKeyExtension {
}
SecretKey binderKey = deriveBinderKey(shc, psk, session);
byte[] computedBinder =
computeBinder(shc, binderKey, session, pskBinderHash);
if (!MessageDigest.isEqual(binder, computedBinder)) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Incorrect PSK binder value");
try {
byte[] computedBinder =
computeBinder(shc, binderKey, session, pskBinderHash);
if (!MessageDigest.isEqual(binder, computedBinder)) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Incorrect PSK binder value");
}
} finally {
KeyUtil.destroySecretKeys(binderKey);
}
}
@ -712,21 +719,25 @@ final class PreSharedKeyExtension {
SecretKey binderKey =
deriveBinderKey(chc, psk, chc.resumingSession);
ClientHelloMessage clientHello = (ClientHelloMessage)message;
CHPreSharedKeySpec pskPrototype = createPskPrototype(
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
try {
ClientHelloMessage clientHello = (ClientHelloMessage)message;
CHPreSharedKeySpec pskPrototype = createPskPrototype(
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
chc.resumingSession, chc, clientHello, pskPrototype);
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
chc.resumingSession, chc, clientHello, pskPrototype);
List<byte[]> binders = new ArrayList<>();
binders.add(binder);
List<byte[]> binders = new ArrayList<>();
binders.add(binder);
CHPreSharedKeySpec pskMessage =
new CHPreSharedKeySpec(identities, binders);
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
return pskMessage.getEncoded();
CHPreSharedKeySpec pskMessage =
new CHPreSharedKeySpec(identities, binders);
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
return pskMessage.getEncoded();
} finally {
KeyUtil.destroySecretKeys(binderKey);
}
}
private CHPreSharedKeySpec createPskPrototype(
@ -780,12 +791,13 @@ final class PreSharedKeyExtension {
SSLSessionImpl session, byte[] digest) throws IOException {
try {
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
byte[] label = ("tls13 finished").getBytes();
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
label, new byte[0], hashAlg.hashLength);
SecretKey finishedKey = hkdf.expand(
binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
SecretKey finishedKey = hkdf.deriveKey("TlsBinderKey",
HKDFParameterSpec.expandOnly(binderKey, hkdfInfo,
hashAlg.hashLength));
String hmacAlg =
"Hmac" + hashAlg.name.replace("-", "");
@ -795,6 +807,8 @@ final class PreSharedKeyExtension {
return hmac.doFinal(digest);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
} finally {
KeyUtil.destroySecretKeys(finishedKey);
}
} catch (GeneralSecurityException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
@ -805,16 +819,16 @@ final class PreSharedKeyExtension {
SecretKey psk, SSLSessionImpl session) throws IOException {
try {
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
byte[] zeros = new byte[hashAlg.hashLength];
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
byte[] label = ("tls13 res binder").getBytes();
MessageDigest md = MessageDigest.getInstance(hashAlg.name);
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
label, md.digest(new byte[0]), hashAlg.hashLength);
return hkdf.expand(earlySecret,
hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
HKDFParameterSpec spec = HKDFParameterSpec.ofExtract()
.addSalt(zeros).addIKM(psk)
.thenExpand(hkdfInfo, hashAlg.hashLength);
return hkdf.deriveKey("TlsBinderKey", spec);
} catch (GeneralSecurityException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -208,8 +208,7 @@ final class RSAClientKeyExchange {
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
// update the states
chc.handshakeSession.setMasterSecret(masterSecret);
@ -296,8 +295,7 @@ final class RSAClientKeyExchange {
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
// update the states
shc.handshakeSession.setMasterSecret(masterSecret);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -292,8 +292,7 @@ final class RSAKeyExchange {
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
public SecretKey deriveKey(String typeNotUsed) throws IOException {
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
@ -305,7 +304,7 @@ final class RSAKeyExchange {
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
return kd.deriveKey("MasterSecret");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -28,29 +28,33 @@ package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KDF;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.CipherSuite.HashAlg;
final class SSLBasicKeyDerivation implements SSLKeyDerivation {
private final String hashAlg;
private final String hkdfAlg;
private final SecretKey secret;
private final byte[] hkdfInfo;
private final int keyLen;
SSLBasicKeyDerivation(SecretKey secret, String hashAlg,
byte[] label, byte[] context, int length) {
this.hashAlg = hashAlg.replace("-", "");
SSLBasicKeyDerivation(SecretKey secret, HashAlg hashAlg, byte[] label,
byte[] context) {
this.hkdfAlg = hashAlg.hkdfAlgorithm;
this.secret = secret;
this.hkdfInfo = createHkdfInfo(label, context, length);
this.hkdfInfo = createHkdfInfo(label, context, hashAlg.hashLength);
this.keyLen = hashAlg.hashLength;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec keySpec) throws IOException {
public SecretKey deriveKey(String type) throws IOException {
try {
HKDF hkdf = new HKDF(hashAlg);
return hkdf.expand(secret, hkdfInfo,
((SecretSizeSpec)keySpec).length, algorithm);
KDF hkdf = KDF.getInstance(hkdfAlg);
return hkdf.deriveKey(type,
HKDFParameterSpec.expandOnly(secret, hkdfInfo, keyLen));
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
}
@ -69,12 +73,4 @@ final class SSLBasicKeyDerivation implements SSLKeyDerivation {
}
return info;
}
static class SecretSizeSpec implements AlgorithmParameterSpec {
final int length;
SecretSizeSpec(int length) {
this.length = length;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,6 +30,9 @@ import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
interface SSLKeyDerivation {
SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException;
SecretKey deriveKey(String purpose) throws IOException;
default byte[] deriveData(String purpose) throws IOException {
throw new UnsupportedOperationException("No support for deriveData!");
};
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -84,9 +84,7 @@ enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator {
@Override
@SuppressWarnings("deprecation")
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
public SecretKey deriveKey(String typeNotUsed) throws IOException {
CipherSuite cipherSuite = context.negotiatedCipherSuite;
ProtocolVersion protocolVersion = context.negotiatedProtocol;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,7 +29,9 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KDF;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.CipherSuite.HashAlg;
@ -87,9 +89,8 @@ final class SSLSecretDerivation implements SSLKeyDerivation {
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
SecretSchedule ks = SecretSchedule.valueOf(algorithm);
public SecretKey deriveKey(String type) throws IOException {
SecretSchedule ks = SecretSchedule.valueOf(type);
try {
byte[] expandContext;
if (ks == SecretSchedule.TlsSaltSecret) {
@ -102,16 +103,16 @@ final class SSLSecretDerivation implements SSLKeyDerivation {
// get supported in the future.
throw new SSLHandshakeException(
"Unexpected unsupported hash algorithm: " +
algorithm);
hashAlg);
}
} else {
expandContext = transcriptHash;
}
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
byte[] hkdfInfo = createHkdfInfo(ks.label,
expandContext, hashAlg.hashLength);
HKDF hkdf = new HKDF(hashAlg.name);
return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm);
return hkdf.deriveKey(type, HKDFParameterSpec.expandOnly(
secret, hkdfInfo, hashAlg.hashLength));
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,7 +37,7 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import sun.security.util.Cache;
import sun.security.util.KeyUtil;
/**
* {@systemProperty jdk.tls.server.enableSessionTicketExtension} determines if the
@ -197,11 +197,7 @@ final class SSLSessionContextImpl implements SSLSessionContext {
SessionTicketExtension.StatelessKey k = entry.getValue();
if (k.isInvalid(this)) {
it.remove();
try {
k.key.destroy();
} catch (Exception e) {
// Suppress
}
KeyUtil.destroySecretKeys(k.key);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,8 +30,10 @@ import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KDF;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLHandshakeException;
@ -69,7 +71,6 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
case TLS13:
return SSLTrafficKeyDerivation.TLS13;
}
return null;
}
@ -143,16 +144,29 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
KeySchedule ks = KeySchedule.valueOf(algorithm);
public SecretKey deriveKey(String type) throws IOException {
KeySchedule ks = KeySchedule.valueOf(type);
try {
HKDF hkdf = new HKDF(cs.hashAlg.name);
byte[] hkdfInfo =
createHkdfInfo(ks.label, ks.getKeyLength(cs));
return hkdf.expand(secret, hkdfInfo,
ks.getKeyLength(cs),
ks.getAlgorithm(cs, algorithm));
KDF hkdf = KDF.getInstance(cs.hashAlg.hkdfAlgorithm);
byte[] hkdfInfo = createHkdfInfo(ks.label, ks.getKeyLength(cs));
HKDFParameterSpec spec = HKDFParameterSpec.expandOnly(secret,
hkdfInfo, ks.getKeyLength(cs));
return hkdf.deriveKey(ks.getAlgorithm(cs, type), spec);
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException(
"Could not generate secret", gse);
}
}
@Override
public byte[] deriveData(String type) throws IOException {
KeySchedule ks = KeySchedule.valueOf(type);
try {
KDF hkdf = KDF.getInstance(cs.hashAlg.hkdfAlgorithm);
byte[] hkdfInfo = createHkdfInfo(ks.label, ks.getKeyLength(cs));
HKDFParameterSpec spec = HKDFParameterSpec.expandOnly(secret,
hkdfInfo, ks.getKeyLength(cs));
return hkdf.deriveData(spec);
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException(
"Could not generate secret", gse);
@ -171,13 +185,12 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
// unlikely
throw new RuntimeException("Unexpected exception", ioe);
}
return info;
}
}
private enum KeySchedule {
// Note that we use enum name as the key/ name.
// Note that we use enum name as the key name.
TlsKey ("key", false),
TlsIv ("iv", true),
TlsUpdateNplus1 ("traffic upd", false);
@ -285,8 +298,9 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
}
}
SecretKey getTrafficKey(String algorithm) {
switch (algorithm) {
@Override
public SecretKey deriveKey(String type) throws IOException {
switch (type) {
case "clientMacKey":
return keyMaterialSpec.getClientMacKey();
case "serverMacKey":
@ -295,24 +309,25 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
return keyMaterialSpec.getClientCipherKey();
case "serverWriteKey":
return keyMaterialSpec.getServerCipherKey();
case "clientWriteIv":
IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv();
return (cliIvSpec == null) ? null :
new SecretKeySpec(cliIvSpec.getIV(), "TlsIv");
case "serverWriteIv":
IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv();
return (srvIvSpec == null) ? null :
new SecretKeySpec(srvIvSpec.getIV(), "TlsIv");
default:
throw new SSLHandshakeException(
"Cannot deriveKey for " + type);
}
return null;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
return getTrafficKey(algorithm);
public byte[] deriveData(String type) throws IOException {
switch (type) {
case "clientWriteIv":
IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv();
return (cliIvSpec == null) ? null : cliIvSpec.getIV();
case "serverWriteIv":
IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv();
return (srvIvSpec == null) ? null : srvIvSpec.getIV();
default:
throw new SSLHandshakeException(
"Cannot deriveData for " + type);
}
}
}
}

View File

@ -32,7 +32,9 @@ import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.*;
import javax.crypto.KDF;
import javax.crypto.SecretKey;
import javax.crypto.spec.HKDFParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
@ -585,7 +587,7 @@ final class ServerHello {
SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc);
SecretKey handshakeSecret = handshakeKD.deriveKey(
"TlsHandshakeSecret", null);
"TlsHandshakeSecret");
SSLTrafficKeyDerivation kdg =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
@ -601,15 +603,12 @@ final class ServerHello {
// update the handshake traffic read keys.
SecretKey readSecret = kd.deriveKey(
"TlsClientHandshakeTrafficSecret", null);
"TlsClientHandshakeTrafficSecret");
SSLKeyDerivation readKD =
kdg.createKeyDerivation(shc, readSecret);
SecretKey readKey = readKD.deriveKey(
"TlsKey", null);
SecretKey readIvSecret = readKD.deriveKey(
"TlsIv", null);
SecretKey readKey = readKD.deriveKey("TlsKey");
IvParameterSpec readIv =
new IvParameterSpec(readIvSecret.getEncoded());
new IvParameterSpec(readKD.deriveData("TlsIv"));
SSLReadCipher readCipher;
try {
readCipher =
@ -635,15 +634,12 @@ final class ServerHello {
// update the handshake traffic write secret.
SecretKey writeSecret = kd.deriveKey(
"TlsServerHandshakeTrafficSecret", null);
"TlsServerHandshakeTrafficSecret");
SSLKeyDerivation writeKD =
kdg.createKeyDerivation(shc, writeSecret);
SecretKey writeKey = writeKD.deriveKey(
"TlsKey", null);
SecretKey writeIvSecret = writeKD.deriveKey(
"TlsIv", null);
SecretKey writeKey = writeKD.deriveKey("TlsKey");
IvParameterSpec writeIv =
new IvParameterSpec(writeIvSecret.getEncoded());
new IvParameterSpec(writeKD.deriveData("TlsIv"));
SSLWriteCipher writeCipher;
try {
writeCipher =
@ -1195,12 +1191,13 @@ final class ServerHello {
try {
CipherSuite.HashAlg hashAlg = hc.negotiatedCipherSuite.hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
byte[] zeros = new byte[hashAlg.hashLength];
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
SecretKey earlySecret = hkdf.deriveKey("TlsEarlySecret",
HKDFParameterSpec.ofExtract().addIKM(psk)
.addSalt(new byte[hashAlg.hashLength]).extractOnly());
hc.handshakeKeyDerivation =
new SSLSecretDerivation(hc, earlySecret);
} catch (GeneralSecurityException gse) {
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
}
}
@ -1284,7 +1281,7 @@ final class ServerHello {
SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc);
SecretKey handshakeSecret = handshakeKD.deriveKey(
"TlsHandshakeSecret", null);
"TlsHandshakeSecret");
SSLTrafficKeyDerivation kdg =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kdg == null) {
@ -1299,16 +1296,13 @@ final class ServerHello {
// update the handshake traffic read keys.
SecretKey readSecret = secretKD.deriveKey(
"TlsServerHandshakeTrafficSecret", null);
"TlsServerHandshakeTrafficSecret");
SSLKeyDerivation readKD =
kdg.createKeyDerivation(chc, readSecret);
SecretKey readKey = readKD.deriveKey(
"TlsKey", null);
SecretKey readIvSecret = readKD.deriveKey(
"TlsIv", null);
SecretKey readKey = readKD.deriveKey("TlsKey");
IvParameterSpec readIv =
new IvParameterSpec(readIvSecret.getEncoded());
new IvParameterSpec(readKD.deriveData("TlsIv"));
SSLReadCipher readCipher;
try {
readCipher =
@ -1334,15 +1328,12 @@ final class ServerHello {
// update the handshake traffic write keys.
SecretKey writeSecret = secretKD.deriveKey(
"TlsClientHandshakeTrafficSecret", null);
"TlsClientHandshakeTrafficSecret");
SSLKeyDerivation writeKD =
kdg.createKeyDerivation(chc, writeSecret);
SecretKey writeKey = writeKD.deriveKey(
"TlsKey", null);
SecretKey writeIvSecret = writeKD.deriveKey(
"TlsIv", null);
SecretKey writeKey = writeKD.deriveKey("TlsKey");
IvParameterSpec writeIv =
new IvParameterSpec(writeIvSecret.getEncoded());
new IvParameterSpec(writeKD.deriveData("TlsIv"));
SSLWriteCipher writeCipher;
try {
writeCipher =

View File

@ -36,6 +36,9 @@ import javax.crypto.interfaces.DHKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.DestroyFailedException;
import jdk.internal.access.SharedSecrets;
import sun.security.jca.JCAUtil;
@ -457,5 +460,23 @@ public final class KeyUtil {
return alg.equalsIgnoreCase("TlsPremasterSecret")
|| alg.equalsIgnoreCase("Generic");
}
// destroy secret keys in a best-effort way
public static void destroySecretKeys(SecretKey... keys) {
for (SecretKey k : keys) {
if (k != null) {
if (k instanceof SecretKeySpec sk) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(sk);
} else {
try {
k.destroy();
} catch (DestroyFailedException e) {
// swallow
}
}
}
}
}
}

View File

@ -1,3 +0,0 @@
modules = \
java.base/sun.security.ssl
bootclasspath.dirs=.

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8145255
* @run main/othervm java.base/sun.security.ssl.TestHkdf
* @summary HKDF for Sun JSSE
*/

View File

@ -1,260 +0,0 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Actual test code for the package private HKDF implementation
*/
package sun.security.ssl;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedList;
import java.util.Objects;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class TestHkdf {
public static class TestData {
public TestData(String name, String algStr, String ikmStr,
String saltStr, String infoStr, int oLen, String expPrkStr,
String expOkmStr) {
testName = Objects.requireNonNull(name);
algName = Objects.requireNonNull(algStr);
IKM = hex2bin(Objects.requireNonNull(ikmStr));
if ((outLen = oLen) <= 0) {
throw new IllegalArgumentException(
"Output length must be greater than 0");
}
expectedPRK = hex2bin(Objects.requireNonNull(expPrkStr));
expectedOKM = hex2bin(Objects.requireNonNull(expOkmStr));
// Non-mandatory fields - may be null
salt = (saltStr != null) ? hex2bin(saltStr) : null;
info = (infoStr != null) ? hex2bin(infoStr) : null;
}
public final String testName;
public final String algName;
public final byte[] IKM;
public final byte[] salt;
public final byte[] info;
public final int outLen;
public final byte[] expectedPRK;
public final byte[] expectedOKM;
}
public static final List<TestData> testList = new LinkedList<TestData>() {{
add(new TestData("RFC 5689 Test Case 1", "SHA-256",
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
"000102030405060708090a0b0c",
"f0f1f2f3f4f5f6f7f8f9",
42,
"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5",
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf" +
"34007208d5b887185865"));
add(new TestData("RFC 5689 Test Case 2", "SHA-256",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
"404142434445464748494a4b4c4d4e4f",
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
82,
"06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244",
"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c" +
"59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71" +
"cc30c58179ec3e87c14c01d5c1f3434f1d87"));
add(new TestData("RFC 5689 Test Case 3", "SHA-256",
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
null, null, 42,
"19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04",
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d" +
"9d201395faa4b61a96c8"));
add(new TestData("RFC 5689 Test Case 4", "SHA-1",
"0b0b0b0b0b0b0b0b0b0b0b",
"000102030405060708090a0b0c",
"f0f1f2f3f4f5f6f7f8f9",
42,
"9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243",
"085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2" +
"c22e422478d305f3f896"));
add(new TestData("RFC 5689 Test Case 5", "SHA-1",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
"404142434445464748494a4b4c4d4e4f",
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
82,
"8adae09a2a307059478d309b26c4115a224cfaf6",
"0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe" +
"8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e" +
"927336d0441f4c4300e2cff0d0900b52d3b4"));
add(new TestData("RFC 5689 Test Case 6", "SHA-1",
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
null, null, 42,
"da8c8a73c7fa77288ec6f5e7c297786aa0d32d01",
"0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0" +
"ea00033de03984d34918"));
add(new TestData("RFC 5689 Test Case 7", "SHA-1",
"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
null, null, 42,
"2adccada18779e7c2077ad2eb19d3f3e731385dd",
"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5" +
"673a081d70cce7acfc48"));
}};
public static void main(String args[]) throws Exception {
int testsPassed = 0;
int testNo = 0;
for (TestData test : testList) {
System.out.println("*** Test " + ++testNo + ": " +
test.testName);
if (runVector(test)) {
testsPassed++;
}
}
System.out.println("Total tests: " + testList.size() +
", Passed: " + testsPassed + ", Failed: " +
(testList.size() - testsPassed));
if (testsPassed != testList.size()) {
throw new RuntimeException("One or more tests failed. " +
"Check output for details");
}
}
private static boolean runVector(TestData testData)
throws NoSuchAlgorithmException, InvalidKeyException {
String kdfName, prfName;
HKDF kdfHkdf;
boolean result = true;
SecretKey actualPRK;
SecretKey actualOKM;
byte[] deriveData;
// Get an instance of the HKDF derivation engine
kdfHkdf = new HKDF(testData.algName);
// Set up the input keying material and the salt as a secret
SecretKey ikmKey = new SecretKeySpec(testData.IKM, "HKDF-IKM");
SecretKey saltKey = (testData.salt != null) ?
new SecretKeySpec(testData.salt, "HKDF-Salt") : null;
// *** HKDF-Extract-only testing
System.out.println("* HKDF-Extract-Only:");
actualPRK = kdfHkdf.extract(saltKey, ikmKey, "HKDF-PRK");
result &= compareKeyAndData(actualPRK, testData.expectedPRK);
// *** HKDF Expand-Only testing
// For these tests, we'll use the actualPRK as the input key
System.out.println("* HKDF-Expand-Only:");
actualOKM = kdfHkdf.expand(actualPRK, testData.info, testData.outLen,
"HKDF-OKM");
result &= compareKeyAndData(actualOKM, testData.expectedOKM);
// *** HKDF Extract-then-Expand testing
// System.out.println("* HKDF-Extract-then-Expand:");
// actualOKM = kdfHkdf.extractExpand(ikmKey, saltKey, testData.info,
// testData.outLen, "HKDF-OKM2");
// result &= compareKeyAndData(actualOKM, testData.expectedOKM);
return result;
}
/**
* Compare actual key output from HKDF against an expected output value.
*
* @param outKey the KDF output in key form
* @param expectedOut the expected value
*
* @return true if the underlying data for outKey, outData and
* expectedOut are the same.
*/
private static boolean compareKeyAndData(SecretKey outKey,
byte[] expectedOut) {
boolean result = false;
if (Arrays.equals(outKey.getEncoded(), expectedOut)) {
System.out.println("\t* Key output: Pass");
result = true;
} else {
System.out.println("\t* Key output: FAIL");
System.out.println("Expected:\n" +
dumpHexBytes(expectedOut, 16, "\n", " "));
System.out.println("Actual:\n" +
dumpHexBytes(outKey.getEncoded(), 16, "\n", " "));
System.out.println();
}
return result;
}
/**
* Dump the hex bytes of a buffer into string form.
*
* @param data The array of bytes to dump to stdout.
* @param itemsPerLine The number of bytes to display per line
* if the {@code lineDelim} character is blank then all bytes
* will be printed on a single line.
* @param lineDelim The delimiter between lines
* @param itemDelim The delimiter between bytes
*
* @return The hexdump of the byte array
*/
private static String dumpHexBytes(byte[] data, int itemsPerLine,
String lineDelim, String itemDelim) {
StringBuilder sb = new StringBuilder();
if (data != null) {
for (int i = 0; i < data.length; i++) {
if (i % itemsPerLine == 0 && i != 0) {
sb.append(lineDelim);
}
sb.append(String.format("%02X", data[i])).append(itemDelim);
}
}
return sb.toString();
}
private static byte[] hex2bin(String hex) {
int i;
int len = hex.length();
byte[] data = new byte [len / 2];
for (i = 0; i < len; i += 2) {
data[i / 2] = (byte)((Character.digit(hex.charAt(i), 16) << 4) +
Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
}