diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java b/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java index e13eec429057c..2d9ad93d2fa29 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHKeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -379,11 +379,10 @@ protected SecretKey engineGenerateSecret(String algorithm) throw new NoSuchAlgorithmException("null algorithm"); } - if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && - !AllowKDF.VALUE) { - - throw new NoSuchAlgorithmException("Unsupported secret key " - + "algorithm: " + algorithm); + if (!KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm) && + !AllowKDF.VALUE) { + throw new NoSuchAlgorithmException( + "Unsupported secret key algorithm: " + algorithm); } byte[] secret = engineGenerateSecret(); @@ -419,13 +418,17 @@ protected SecretKey engineGenerateSecret(String algorithm) throw new InvalidKeyException("Key material is too short"); } return skey; - } else if (algorithm.equals("TlsPremasterSecret")) { - // remove leading zero bytes per RFC 5246 Section 8.1.2 - return new SecretKeySpec( + } else if (KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) { + if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { + // remove leading zero bytes per RFC 5246 Section 8.1.2 + return new SecretKeySpec( KeyUtil.trimZeroes(secret), "TlsPremasterSecret"); + } else { + return new SecretKeySpec(secret, algorithm); + } } else { - throw new NoSuchAlgorithmException("Unsupported secret key " - + "algorithm: "+ algorithm); + throw new NoSuchAlgorithmException( + "Unsupported secret key algorithm: " + algorithm); } } } diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreement.java b/src/java.base/share/classes/javax/crypto/KeyAgreement.java index e3a68e88d03d4..4b3a3fd1cf7b8 100644 --- a/src/java.base/share/classes/javax/crypto/KeyAgreement.java +++ b/src/java.base/share/classes/javax/crypto/KeyAgreement.java @@ -666,18 +666,30 @@ public final int generateSecret(byte[] sharedSecret, int offset) * {@code generateSecret} to change the private information used in * subsequent operations. * - * @param algorithm the requested secret-key algorithm - * - * @return the shared secret key + * @param algorithm the requested secret key algorithm. This is different + * from the {@code KeyAgreement} algorithm provided to the + * {@code getInstance} method. See the SecretKey Algorithms section in the + * + * Java Security Standard Algorithm Names Specification + * for information about standard secret key algorithm names. + * Specify "Generic" if the output will be used as the input keying + * material of a key derivation function (KDF). + * + * @return the shared secret key. The length of the key material + * may be adjusted to be compatible with the specified algorithm, + * regardless of whether the key is extractable. If {@code algorithm} + * is specified as "Generic" and it is supported by the implementation, + * the full shared secret is returned. * * @exception IllegalStateException if this key agreement has not been * initialized or if {@code doPhase} has not been called to supply the * keys for all parties in the agreement - * @exception NoSuchAlgorithmException if the specified secret-key - * algorithm is not available - * @exception InvalidKeyException if the shared secret-key material cannot + * @exception NoSuchAlgorithmException if the specified secret key + * algorithm is not supported + * @exception InvalidKeyException if the shared secret key material cannot * be used to generate a secret key of the specified algorithm (e.g., * the key material is too short) + * @spec security/standard-names.html Java Security Standard Algorithm Names */ public final SecretKey generateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java b/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java index a4c242e46cca5..84d68a39725cf 100644 --- a/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java +++ b/src/java.base/share/classes/javax/crypto/KeyAgreementSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -205,18 +205,30 @@ protected abstract int engineGenerateSecret(byte[] sharedSecret, * {@code generateSecret} to change the private information used in * subsequent operations. * - * @param algorithm the requested secret key algorithm - * - * @return the shared secret key + * @param algorithm the requested secret key algorithm. This is different + * from the {@code KeyAgreement} algorithm provided to the + * {@code getInstance} method. See the SecretKey Algorithms section in the + * + * Java Security Standard Algorithm Names Specification + * for information about standard secret key algorithm names. + * Specify "Generic" if the output will be used as the input keying + * material of a key derivation function (KDF). + * + * @return the shared secret key. The length of the key material + * may be adjusted to be compatible with the specified algorithm, + * regardless of whether the key is extractable. If {@code algorithm} + * is specified as "Generic" and it is supported by the implementation, + * the full shared secret is returned. * * @exception IllegalStateException if this key agreement has not been * initialized or if {@code doPhase} has not been called to supply the * keys for all parties in the agreement - * @exception NoSuchAlgorithmException if the requested secret key - * algorithm is not available + * @exception NoSuchAlgorithmException if the specified secret key + * algorithm is not supported * @exception InvalidKeyException if the shared secret key material cannot * be used to generate a secret key of the requested algorithm type (e.g., * the key material is too short) + * @spec security/standard-names.html Java Security Standard Algorithm Names */ protected abstract SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, diff --git a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java index be3bdfdd63990..8f6bdb8d80ae1 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java +++ b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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,6 +29,7 @@ import sun.security.util.ArrayUtil; import sun.security.util.CurveDB; import sun.security.util.ECUtil; +import sun.security.util.KeyUtil; import sun.security.util.NamedCurve; import sun.security.util.math.IntegerFieldModuloP; import sun.security.util.math.IntegerMontgomeryFieldModuloP; @@ -254,11 +255,11 @@ protected SecretKey engineGenerateSecret(String algorithm) if (algorithm == null) { throw new NoSuchAlgorithmException("Algorithm must not be null"); } - if (!(algorithm.equals("TlsPremasterSecret"))) { - throw new NoSuchAlgorithmException - ("Only supported for algorithm TlsPremasterSecret"); + if (!KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) { + throw new NoSuchAlgorithmException( + "Unsupported secret key algorithm: " + algorithm); } - return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret"); + return new SecretKeySpec(engineGenerateSecret(), algorithm); } private static diff --git a/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java b/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java index e2a625a55d9b2..01dce4c53e905 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java +++ b/src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, 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 @@ -25,6 +25,8 @@ package sun.security.ec; +import sun.security.util.KeyUtil; + import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -207,9 +209,9 @@ protected SecretKey engineGenerateSecret(String algorithm) throw new NoSuchAlgorithmException("Algorithm must not be null"); } - if (!(algorithm.equals("TlsPremasterSecret"))) { + if (!KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) { throw new NoSuchAlgorithmException( - "Only supported for algorithm TlsPremasterSecret"); + "Unsupported secret key algorithm: " + algorithm); } return new SecretKeySpec(engineGenerateSecret(), algorithm); } 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 6eb5acfade3fd..224167653f789 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -424,5 +424,10 @@ public static String hashAlgFromHSS(PublicKey publicKey) throw new NoSuchAlgorithmException("Cannot decode public key", e); } } + + public static boolean isSupportedKeyAgreementOutputAlgorithm(String alg) { + return alg.equalsIgnoreCase("TlsPremasterSecret") + || alg.equalsIgnoreCase("Generic"); + } } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java index 53728219d8bb2..ec5e03cc15a67 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -33,6 +33,8 @@ import static sun.security.pkcs11.TemplateManager.*; import sun.security.pkcs11.wrapper.*; +import sun.security.util.KeyUtil; + import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** @@ -168,9 +170,9 @@ protected SecretKey engineGenerateSecret(String algorithm) if (algorithm == null) { throw new NoSuchAlgorithmException("Algorithm must not be null"); } - if (!algorithm.equals("TlsPremasterSecret")) { - throw new NoSuchAlgorithmException - ("Only supported for algorithm TlsPremasterSecret"); + if (!KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) { + throw new NoSuchAlgorithmException( + "Unsupported secret key algorithm: " + algorithm); } return nativeGenerateSecret(algorithm); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java index 6c010a4a5130e..183135ce7e126 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -268,20 +268,19 @@ protected SecretKey engineGenerateSecret(String algorithm) throw new NoSuchAlgorithmException("Algorithm must not be null"); } - if (algorithm.equals("TlsPremasterSecret")) { + if (KeyUtil.isSupportedKeyAgreementOutputAlgorithm(algorithm)) { // For now, only perform native derivation for TlsPremasterSecret - // as that is required for FIPS compliance. + // and Generic algorithms. TlsPremasterSecret is required for + // FIPS compliance and Generic is required for input to KDF. // For other algorithms, there are unresolved issues regarding // how this should work in JCE plus a Solaris truncation bug. // (bug not yet filed). return nativeGenerateSecret(algorithm); } - if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && - !AllowKDF.VALUE) { - - throw new NoSuchAlgorithmException("Unsupported secret key " - + "algorithm: " + algorithm); + if (!AllowKDF.VALUE) { + throw new NoSuchAlgorithmException( + "Unsupported secret key algorithm: " + algorithm); } byte[] secret = engineGenerateSecret(); @@ -295,8 +294,6 @@ protected SecretKey engineGenerateSecret(String algorithm) keyLen = 24; } else if (algorithm.equalsIgnoreCase("Blowfish")) { keyLen = Math.min(56, secret.length); - } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { - keyLen = secret.length; } else { throw new NoSuchAlgorithmException ("Unknown algorithm " + algorithm); @@ -340,7 +337,8 @@ private SecretKey nativeGenerateSecret(String algorithm) int keyLen = (int)lenAttributes[0].getLong(); SecretKey key = P11Key.secretKey (session, keyID, algorithm, keyLen << 3, attributes); - if ("RAW".equals(key.getFormat())) { + if ("RAW".equals(key.getFormat()) + && algorithm.equalsIgnoreCase("TlsPremasterSecret")) { // Workaround for Solaris bug 6318543. // Strip leading zeroes ourselves if possible (key not sensitive). // This should be removed once the Solaris fix is available diff --git a/test/jdk/java/security/KeyAgreement/Generic.java b/test/jdk/java/security/KeyAgreement/Generic.java new file mode 100644 index 0000000000000..dcf212dbffe7c --- /dev/null +++ b/test/jdk/java/security/KeyAgreement/Generic.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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 + * 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 8189441 + * @library /test/lib /test/jdk/sun/security/pkcs11 + * @summary make sure Generic is accepted by all KeyAgreement implementations + * @run main Generic builtin + * @run main/othervm Generic nss + * @run main/othervm -DCUSTOM_P11_CONFIG_NAME=p11-nss-sensitive.txt Generic nss + */ +import jdk.test.lib.Asserts; + +import javax.crypto.KeyAgreement; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Security; +import java.util.List; + +public class Generic { + + public static void main(String[] args) throws Exception { + if (args[0].equals("nss")) { + test(PKCS11Test.getSunPKCS11(PKCS11Test.getNssConfig())); + } else { + for (var p : Security.getProviders()) { + test(p); + } + } + } + + static void test(Provider p) throws Exception { + for (var s : p.getServices()) { + if (s.getType().equalsIgnoreCase("KeyAgreement")) { + try { + System.out.println(s.getProvider().getName() + "." + s.getAlgorithm()); + var g = KeyPairGenerator.getInstance(ka2kpg(s.getAlgorithm()), p); + var kp1 = g.generateKeyPair(); + var kp2 = g.generateKeyPair(); + var ka = KeyAgreement.getInstance(s.getAlgorithm(), s.getProvider()); + for (var alg : List.of("TlsPremasterSecret", "Generic")) { + ka.init(kp1.getPrivate()); + ka.doPhase(kp2.getPublic(), true); + Asserts.assertEquals( + ka.generateSecret(alg).getAlgorithm(), alg); + } + } catch (Exception e) { + throw e; + } + } + } + } + + // Find key algorithm from KeyAgreement algorithm + private static String ka2kpg(String ka) { + return ka.equals("ECDH") ? "EC" : ka; + } +} diff --git a/test/jdk/sun/security/pkcs11/nss/p11-nss-sensitive.txt b/test/jdk/sun/security/pkcs11/nss/p11-nss-sensitive.txt index 4579dd593982d..df891c7097a0f 100644 --- a/test/jdk/sun/security/pkcs11/nss/p11-nss-sensitive.txt +++ b/test/jdk/sun/security/pkcs11/nss/p11-nss-sensitive.txt @@ -49,10 +49,12 @@ attributes(*,CKO_PRIVATE_KEY,CKK_DH) = { # Make all private keys sensitive attributes(*,CKO_PRIVATE_KEY,*) = { CKA_SENSITIVE = true + CKA_EXTRACTABLE = false } # Make all secret keys sensitive attributes(*,CKO_SECRET_KEY,*) = { CKA_SENSITIVE = true + CKA_EXTRACTABLE = false }