diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java index 3fa6b14762ac2..d86d161370f05 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKEM.java @@ -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.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 KeyPair derive(int kem_id) { 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 @@ private byte[] DH(PrivateKey skE, PublicKey pkR) 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 @@ private PublicKey getPublicKey(PrivateKey sk) // 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(); - } - 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); + 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; } - 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 @@ private static byte[] I2OSP(int n, int w) { } } - 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); + } } } diff --git a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java index 8a49a9550f4b8..6da6f7456913c 100644 --- a/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java +++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java @@ -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 @@ public byte[] produce(ConnectionContext context, 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 @@ public void consume(ConnectionContext context, 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 @@ public void consume(ConnectionContext context, } } - 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( diff --git a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java index 019f1ad34d925..4a2063ded4238 100644 --- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java +++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java @@ -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 HashAlg { 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 diff --git a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java index b8f8310284088..fb5d6feef556b 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java @@ -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 @@ public byte[] produce(ConnectionContext context, "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 @@ public void consume(ConnectionContext context, // 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 = diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java index 0dcf5ec27b789..e1c1b1377ad25 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java @@ -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 @@ public byte[] produce(ConnectionContext context, "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 @@ public void consume(ConnectionContext context, // 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 @@ public byte[] produce(ConnectionContext context, "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 @@ public void consume(ConnectionContext context, // 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 = diff --git a/src/java.base/share/classes/sun/security/ssl/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java index 9afa83a0afcc0..dcb499b6d0c01 100644 --- a/src/java.base/share/classes/sun/security/ssl/Finished.java +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java @@ -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.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 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 @@ public byte[] createVerifyData(HandshakeContext context, 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 @@ public byte[] createVerifyData(HandshakeContext context, } catch (NoSuchAlgorithmException |InvalidKeyException ex) { throw new ProviderException( "Failed to generate verify_data", ex); + } finally { + KeyUtil.destroySecretKeys(finishedSecret); } } } @@ -717,17 +715,14 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, 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 @@ private byte[] onProduceFinished(ClientHandshakeContext chc, // 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 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, 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 @@ private byte[] onProduceFinished(ServerHandshakeContext shc, } catch (GeneralSecurityException gse) { throw shc.conContext.fatal(Alert.INTERNAL_ERROR, "Failure to derive application secrets", gse); + } finally { + KeyUtil.destroySecretKeys(saltSecret); } /* @@ -960,34 +953,31 @@ private void onConsumeFinished(ClientHandshakeContext chc, 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 @@ private void onConsumeFinished(ClientHandshakeContext chc, } catch (GeneralSecurityException gse) { throw chc.conContext.fatal(Alert.INTERNAL_ERROR, "Failure to derive application secrets", gse); + } finally { + KeyUtil.destroySecretKeys(saltSecret); } // @@ -1084,16 +1076,13 @@ private void onConsumeFinished(ServerHandshakeContext shc, 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 @@ private void onConsumeFinished(ServerHandshakeContext shc, 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) { diff --git a/src/java.base/share/classes/sun/security/ssl/HKDF.java b/src/java.base/share/classes/sun/security/ssl/HKDF.java deleted file mode 100644 index 5e5cd1443055d..0000000000000 --- a/src/java.base/share/classes/sun/security/ssl/HKDF.java +++ /dev/null @@ -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); - } -} - diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index b76da75c763cc..623f83f547a8b 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -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 @@ private SecretKey t12DeriveKey(String algorithm, } 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); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java index 49eb0420f47d1..c4549070f02c0 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java @@ -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 @@ public void consume(ConnectionContext context, 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 @@ public byte[] produce(ConnectionContext context, 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 { diff --git a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java index 8be021b41114c..4c879e0dc4d76 100644 --- a/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java +++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java @@ -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.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 @@ public String toString() { 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); } diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 9e843a0e4a564..149d781183872 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -29,8 +29,10 @@ 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.SSLHandshake.HandshakeMessage; import sun.security.ssl.SessionTicketExtension.SessionTicketSpec; import sun.security.util.HexDumpEncoder; +import sun.security.util.KeyUtil; import static sun.security.ssl.SSLExtension.*; import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; @@ -566,11 +569,15 @@ private static void checkBinder(ServerHandshakeContext shc, } 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); } } @@ -719,21 +726,25 @@ public byte[] produce(ConnectionContext context, 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 binders = new ArrayList<>(); - binders.add(binder); + List 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( @@ -787,12 +798,13 @@ private static byte[] computeBinder(HandshakeContext context, 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("-", ""); @@ -802,6 +814,8 @@ private static byte[] computeBinder(HandshakeContext context, 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); @@ -812,16 +826,16 @@ private static SecretKey deriveBinderKey(HandshakeContext context, 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); } diff --git a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java index 5189822da3087..701ba35174e8f 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java @@ -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 @@ public byte[] produce(ConnectionContext context, "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 @@ public void consume(ConnectionContext context, "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); diff --git a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java index 3ad4a00955624..311ac97e744db 100644 --- a/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java @@ -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 @@ class RSAKAKeyDerivation implements SSLKeyDerivation { } @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 @@ public SecretKey deriveKey(String algorithm, } SSLKeyDerivation kd = mskd.createKeyDerivation( context, preMasterSecret); - return kd.deriveKey("MasterSecret", params); + return kd.deriveKey("MasterSecret"); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java index 20fc711259379..58ed20792ad18 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java @@ -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 @@ 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 @@ private static byte[] createHkdfInfo( } return info; } - - static class SecretSizeSpec implements AlgorithmParameterSpec { - final int length; - - SecretSizeSpec(int length) { - this.length = length; - } - } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java index d132af3b0b060..ab415bdae4b17 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java @@ -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 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!"); + }; } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java index b3e60553d552c..db5887c5e8e48 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java @@ -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 @@ class LegacyMasterKeyDerivation implements SSLKeyDerivation { @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; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java index 0c0dc46d33dc5..dc3b2bcb4bc83 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java @@ -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.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 @@ SSLSecretDerivation forContext(HandshakeContext context) { } @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 @@ public SecretKey deriveKey(String algorithm, // 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); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java index 0ff80f0c76c31..4baa3304fee1f 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java @@ -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.SSLSessionContext; import sun.security.util.Cache; - +import sun.security.util.KeyUtil; /** * {@systemProperty jdk.tls.server.enableSessionTicketExtension} determines if the @@ -197,11 +197,7 @@ private void cleanupStatelessKeys() { SessionTicketExtension.StatelessKey k = entry.getValue(); if (k.isInvalid(this)) { it.remove(); - try { - k.key.destroy(); - } catch (Exception e) { - // Suppress - } + KeyUtil.destroySecretKeys(k.key); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java index b17a03f0cb4c0..1db07c77160d3 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java @@ -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.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 @@ static SSLTrafficKeyDerivation valueOf(ProtocolVersion protocolVersion) { case TLS13: return SSLTrafficKeyDerivation.TLS13; } - return null; } @@ -143,16 +144,29 @@ static final class T13TrafficKeyDerivation implements SSLKeyDerivation { } @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 { + 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 { - 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.deriveData(spec); } catch (GeneralSecurityException gse) { throw new SSLHandshakeException( "Could not generate secret", gse); @@ -171,13 +185,12 @@ private static byte[] createHkdfInfo( // 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 @@ static final class LegacyTrafficKeyDerivation implements SSLKeyDerivation { } } - 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 @@ SecretKey getTrafficKey(String algorithm) { return keyMaterialSpec.getClientCipherKey(); case "serverWriteKey": return keyMaterialSpec.getServerCipherKey(); + default: + throw new SSLHandshakeException( + "Cannot deriveKey for " + type); + } + } + + @Override + public byte[] deriveData(String type) throws IOException { + switch (type) { case "clientWriteIv": IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv(); - return (cliIvSpec == null) ? null : - new SecretKeySpec(cliIvSpec.getIV(), "TlsIv"); + return (cliIvSpec == null) ? null : cliIvSpec.getIV(); case "serverWriteIv": IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv(); - return (srvIvSpec == null) ? null : - new SecretKeySpec(srvIvSpec.getIV(), "TlsIv"); + return (srvIvSpec == null) ? null : srvIvSpec.getIV(); + default: + throw new SSLHandshakeException( + "Cannot deriveData for " + type); } - - return null; - } - - @Override - public SecretKey deriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - return getTrafficKey(algorithm); } } } - diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index f19964dd5c7b8..0a23a1737d73a 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -35,7 +35,9 @@ 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; @@ -620,7 +622,7 @@ public byte[] produce(ConnectionContext context, SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); SecretKey handshakeSecret = handshakeKD.deriveKey( - "TlsHandshakeSecret", null); + "TlsHandshakeSecret"); SSLTrafficKeyDerivation kdg = SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); @@ -636,15 +638,12 @@ public byte[] produce(ConnectionContext context, // 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 = @@ -670,15 +669,12 @@ public byte[] produce(ConnectionContext context, // 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 = @@ -1222,12 +1218,13 @@ private static void setUpPskKD(HandshakeContext hc, 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); } } @@ -1311,7 +1308,7 @@ public void consume(ConnectionContext context, SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc); SecretKey handshakeSecret = handshakeKD.deriveKey( - "TlsHandshakeSecret", null); + "TlsHandshakeSecret"); SSLTrafficKeyDerivation kdg = SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); if (kdg == null) { @@ -1326,16 +1323,13 @@ public void consume(ConnectionContext context, // 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 = @@ -1361,15 +1355,12 @@ public void consume(ConnectionContext context, // 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 = diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 2c9416c09831f..a7e655b492d93 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -36,6 +36,9 @@ 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; @@ -445,5 +448,23 @@ public static boolean isSupportedKeyAgreementOutputAlgorithm(String alg) { 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 + } + } + } + } + } } diff --git a/test/jdk/sun/security/ssl/internal/TEST.properties b/test/jdk/sun/security/ssl/internal/TEST.properties deleted file mode 100644 index c14498c7b24d5..0000000000000 --- a/test/jdk/sun/security/ssl/internal/TEST.properties +++ /dev/null @@ -1,3 +0,0 @@ -modules = \ - java.base/sun.security.ssl -bootclasspath.dirs=. diff --git a/test/jdk/sun/security/ssl/internal/TestRun.java b/test/jdk/sun/security/ssl/internal/TestRun.java deleted file mode 100644 index 27c4e2ba67c5e..0000000000000 --- a/test/jdk/sun/security/ssl/internal/TestRun.java +++ /dev/null @@ -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 - */ - diff --git a/test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java b/test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java deleted file mode 100644 index f3b489206a015..0000000000000 --- a/test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java +++ /dev/null @@ -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 testList = new LinkedList() {{ - 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; - } -}