From 4a75fd462c002a209201d8bfc8d6c9eb286a7444 Mon Sep 17 00:00:00 2001 From: Martin Balao Date: Tue, 6 Jun 2023 19:39:34 +0000 Subject: [PATCH] 8301553: Support Password-Based Cryptography in SunPKCS11 Co-authored-by: Francisco Ferrari Bihurriet Co-authored-by: Martin Balao Reviewed-by: valeriep --- .../crypto/provider/HmacPKCS12PBECore.java | 106 +--- .../com/sun/crypto/provider/PBES2Core.java | 171 +----- .../sun/crypto/provider/PBKDF2KeyImpl.java | 56 +- .../com/sun/crypto/provider/PBMAC1Core.java | 28 +- .../classes/sun/security/util/PBEUtil.java | 355 ++++++++++++ .../sun/security/pkcs11/P11Cipher.java | 6 +- .../classes/sun/security/pkcs11/P11Key.java | 57 +- .../classes/sun/security/pkcs11/P11Mac.java | 61 ++- .../sun/security/pkcs11/P11PBECipher.java | 228 ++++++++ .../security/pkcs11/P11SecretKeyFactory.java | 456 ++++++++++++--- .../classes/sun/security/pkcs11/P11Util.java | 45 +- .../sun/security/pkcs11/SunPKCS11.java | 148 ++++- .../wrapper/CK_ECDH1_DERIVE_PARAMS.java | 6 +- .../security/pkcs11/wrapper/CK_MECHANISM.java | 19 +- .../pkcs11/wrapper/CK_PBE_PARAMS.java | 24 +- .../pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java | 32 +- .../wrapper/CK_PKCS5_PBKD2_PARAMS2.java | 156 ++++++ .../wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java | 4 +- .../security/pkcs11/wrapper/Functions.java | 72 ++- .../pkcs11/wrapper/PKCS11Constants.java | 20 +- .../share/native/libj2pkcs11/p11_convert.c | 114 +++- .../share/native/libj2pkcs11/p11_util.c | 57 +- .../share/native/libj2pkcs11/pkcs11wrapper.h | 47 +- .../sun/security/pkcs11/Cipher/PBECipher.java | 259 +++++++++ .../pkcs11/KeyStore/ImportKeyToP12.java | 136 +++++ .../sun/security/pkcs11/Mac/MacSameTest.java | 11 +- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 204 +++++++ .../sun/security/pkcs11/Mac/ReinitMac.java | 12 +- test/jdk/sun/security/pkcs11/PKCS11Test.java | 58 +- .../pkcs11/SecretKeyFactory/TestPBKD.java | 517 ++++++++++++++++++ 30 files changed, 3013 insertions(+), 452 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/util/PBEUtil.java create mode 100644 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java create mode 100644 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java create mode 100644 test/jdk/sun/security/pkcs11/Cipher/PBECipher.java create mode 100644 test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java create mode 100644 test/jdk/sun/security/pkcs11/Mac/PBAMac.java create mode 100644 test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java diff --git a/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java b/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java index 1fd6230d83b..0d999e4f512 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,13 +25,14 @@ package com.sun.crypto.provider; -import java.util.Arrays; - -import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.PBEKeySpec; import java.security.*; import java.security.spec.*; +import java.util.Arrays; + +import jdk.internal.access.SharedSecrets; +import sun.security.util.PBEUtil; /** * This is an implementation of the HMAC algorithms as defined @@ -108,81 +109,30 @@ abstract class HmacPKCS12PBECore extends HmacCore { */ protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - char[] passwdChars; - byte[] salt = null; - int iCount = 0; - if (key instanceof javax.crypto.interfaces.PBEKey) { - javax.crypto.interfaces.PBEKey pbeKey = - (javax.crypto.interfaces.PBEKey) key; - passwdChars = pbeKey.getPassword(); - salt = pbeKey.getSalt(); // maybe null if unspecified - iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified - } else if (key instanceof SecretKey) { - byte[] passwdBytes; - if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) || - (passwdBytes = key.getEncoded()) == null) { - throw new InvalidKeyException("Missing password"); - } - passwdChars = new char[passwdBytes.length]; - for (int i=0; i { - Arrays.fill(k, (byte) 0x00); - Arrays.fill(p, '\0'); - }); + if (key == null) { + Arrays.fill(passwd, '\0'); + } } + // Use the cleaner to zero the key when no longer referenced + final byte[] k = this.key; + final char[] p = this.passwd; + cleaner = CleanerFactory.cleaner().register(this, + () -> { + Arrays.fill(k, (byte) 0x00); + Arrays.fill(p, '\0'); + }); } private static byte[] deriveKey(final Mac prf, final byte[] password, @@ -211,11 +205,7 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey { } public byte[] getEncoded() { - // The key is zeroized by finalize() - // The reachability fence ensures finalize() isn't called early - byte[] result = key.clone(); - Reference.reachabilityFence(this); - return result; + return key.clone(); } public String getAlgorithm() { @@ -226,16 +216,12 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey { return iterCount; } - public void clearPassword() { - Arrays.fill(passwd, (char)0); + public void clear() { + cleaner.clean(); } public char[] getPassword() { - // The password is zeroized by finalize() - // The reachability fence ensures finalize() isn't called early - char[] result = passwd.clone(); - Reference.reachabilityFence(this); - return result; + return passwd.clone(); } public byte[] getSalt() { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java index 6c2f5df6716..56059a89264 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBMAC1Core.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,6 +25,8 @@ package com.sun.crypto.provider; +import jdk.internal.access.SharedSecrets; + import java.util.Arrays; import javax.crypto.SecretKey; @@ -179,23 +181,29 @@ abstract class PBMAC1Core extends HmacCore { } PBKDF2KeyImpl s = null; - PBKDF2Core kdf = getKDFImpl(kdfAlgo); - byte[] derivedKey; + byte[] derivedKey = null; + SecretKeySpec cipherKey = null; try { + PBKDF2Core kdf = getKDFImpl(kdfAlgo); s = (PBKDF2KeyImpl)kdf.engineGenerateSecret(pbeSpec); derivedKey = s.getEncoded(); + cipherKey = new SecretKeySpec(derivedKey, kdfAlgo); + super.engineInit(cipherKey, null); } catch (InvalidKeySpecException ikse) { throw new InvalidKeyException("Cannot construct PBE key", ikse); } finally { - pbeSpec.clearPassword(); - if (s != null) { - s.clearPassword(); + if (cipherKey != null) { + SharedSecrets.getJavaxCryptoSpecAccess() + .clearSecretKeySpec(cipherKey); } + if (derivedKey != null) { + Arrays.fill(derivedKey, (byte) 0); + } + if (s != null) { + s.clear(); + } + pbeSpec.clearPassword(); } - SecretKey cipherKey = new SecretKeySpec(derivedKey, kdfAlgo); - Arrays.fill(derivedKey, (byte)0); - - super.engineInit(cipherKey, null); } public static final class HmacSHA1 extends PBMAC1Core { diff --git a/src/java.base/share/classes/sun/security/util/PBEUtil.java b/src/java.base/share/classes/sun/security/util/PBEUtil.java new file mode 100644 index 00000000000..12e71418bf4 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/PBEUtil.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * 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.util; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +public final class PBEUtil { + + /* + * PBES2Params is an auxiliary class that represents the state needed for + * PBES2 operations (iterations count, salt and IV) and its (re) + * initialization logic. Users of this class are CipherSpi implementors that + * support PBES2 cryptography (RFC #8018), such as PBES2Core (SunJCE) and + * P11PBECipher (SunPKCS11). + * + * CipherSpi implementors must call ::getPBEKeySpec in every engine + * initialization (CipherSpi::engineInit override) to reset the state and + * get new values in a PBEKeySpec instance. These new values are taken + * from parameters, defaults or generated randomly. + * + * After engine initialization, values in effect can be extracted with + * ::getAlgorithmParameters (as AlgorithmParameters) or ::getIvSpec (as + * IvParameterSpec). + */ + public static final class PBES2Params { + private static final int DEFAULT_SALT_LENGTH = 20; + private static final int DEFAULT_ITERATIONS = 4096; + + private int iCount; + private byte[] salt; + private IvParameterSpec ivSpec; + + /* + * Initialize a PBES2Params instance. May generate random salt and + * IV if not passed and the operation is encryption. If initialization + * fails, values are reset. Used by PBES2Params and P11PBECipher + * (SunPKCS11). + */ + public void initialize(int blkSize, int opmode, int iCount, byte[] salt, + AlgorithmParameterSpec ivSpec, SecureRandom random) + throws InvalidAlgorithmParameterException { + try { + boolean doEncrypt = opmode == Cipher.ENCRYPT_MODE || + opmode == Cipher.WRAP_MODE; + if (ivSpec instanceof IvParameterSpec iv) { + this.ivSpec = iv; + } else if (ivSpec == null && doEncrypt) { + byte[] ivBytes = new byte[blkSize]; + random.nextBytes(ivBytes); + this.ivSpec = new IvParameterSpec(ivBytes); + } else { + throw new InvalidAlgorithmParameterException("Wrong " + + "parameter type: IvParameterSpec " + + (doEncrypt ? "or null " : "") + "expected"); + } + this.iCount = iCount == 0 ? DEFAULT_ITERATIONS : iCount; + if (salt == null) { + if (doEncrypt) { + salt = new byte[DEFAULT_SALT_LENGTH]; + random.nextBytes(salt); + } else { + throw new InvalidAlgorithmParameterException("Salt " + + "needed for decryption"); + } + } + this.salt = salt; + } catch (InvalidAlgorithmParameterException e) { + this.ivSpec = null; + this.iCount = 0; + this.salt = null; + throw e; + } + } + + /* + * Obtain an IvParameterSpec for Cipher services. This method returns + * null when the state is not initialized. Used by PBES2Core (SunJCE) + * and P11PBECipher (SunPKCS11). + */ + public IvParameterSpec getIvSpec() { + return ivSpec; + } + + /* + * Obtain AlgorithmParameters for Cipher services. This method will + * initialize PBES2Params if needed, generating new values randomly or + * assigning from defaults. If PBES2Params is initialized, existing + * values will be returned. Used by PBES2Core (SunJCE) and + * P11PBECipher (SunPKCS11). + */ + public AlgorithmParameters getAlgorithmParameters(int blkSize, + String pbeAlgo, Provider algParamsProv, SecureRandom random) { + AlgorithmParameters params; + try { + if (iCount == 0 && salt == null && ivSpec == null) { + initialize(blkSize, Cipher.ENCRYPT_MODE, 0, null, null, + random); + } + params = AlgorithmParameters.getInstance(pbeAlgo, + algParamsProv); + params.init(new PBEParameterSpec(salt, iCount, ivSpec)); + } catch (NoSuchAlgorithmException nsae) { + // should never happen + throw new RuntimeException("AlgorithmParameters for " + + pbeAlgo + " not configured"); + } catch (InvalidParameterSpecException ipse) { + // should never happen + throw new RuntimeException("PBEParameterSpec not supported"); + } catch (InvalidAlgorithmParameterException iape) { + // should never happen + throw new RuntimeException("Error initializing PBES2Params"); + } + return params; + } + + /* + * Initialize PBES2Params and obtain a PBEKeySpec for Cipher services. + * Data from the key, parameters, defaults or random may be used for + * initialization. Used by PBES2Core (SunJCE) and P11PBECipher + * (SunPKCS11). + */ + public PBEKeySpec getPBEKeySpec(int blkSize, int keyLength, int opmode, + Key key, AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (key == null) { + throw new InvalidKeyException("Null key"); + } + byte[] passwdBytes; + char[] passwdChars = null; + if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) || + (passwdBytes = key.getEncoded()) == null) { + throw new InvalidKeyException("Missing password"); + } + try { + int iCountInit; + byte[] saltInit; + AlgorithmParameterSpec ivSpecInit; + // Extract from the supplied PBE params, if present + if (params instanceof PBEParameterSpec pbeParams) { + // salt should be non-null per PBEParameterSpec + iCountInit = check(pbeParams.getIterationCount()); + saltInit = check(pbeParams.getSalt()); + ivSpecInit = pbeParams.getParameterSpec(); + } else if (params == null) { + // Try extracting from the key if present. If unspecified, + // PBEKey returns 0 and null respectively. + if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) { + iCountInit = check(pbeKey.getIterationCount()); + saltInit = check(pbeKey.getSalt()); + } else { + iCountInit = 0; + saltInit = null; + } + ivSpecInit = null; + } else { + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: PBE expected"); + } + initialize(blkSize, opmode, iCountInit, saltInit, ivSpecInit, + random); + passwdChars = new char[passwdBytes.length]; + for (int i = 0; i < passwdChars.length; i++) { + passwdChars[i] = (char) (passwdBytes[i] & 0x7f); + } + return new PBEKeySpec(passwdChars, salt, iCount, keyLength); + } finally { + // password char[] was cloned in PBEKeySpec constructor, + // so we can zero it out here + if (passwdChars != null) Arrays.fill(passwdChars, '\0'); + if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00); + } + } + + /* + * Obtain an AlgorithmParameterSpec from an AlgorithmParameters + * instance, for Cipher services. Used by PBES2Core (SunJCE) and + * P11PBECipher (SunPKCS11). + */ + public static AlgorithmParameterSpec getParameterSpec( + AlgorithmParameters params) + throws InvalidAlgorithmParameterException { + AlgorithmParameterSpec pbeSpec = null; + if (params != null) { + try { + pbeSpec = params.getParameterSpec(PBEParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: PBE expected"); + } + } + return pbeSpec; + } + + private static byte[] check(byte[] salt) + throws InvalidAlgorithmParameterException { + if (salt != null && salt.length < 8) { + throw new InvalidAlgorithmParameterException( + "Salt must be at least 8 bytes long"); + } + return salt; + } + + private static int check(int iCount) + throws InvalidAlgorithmParameterException { + if (iCount < 0) { + throw new InvalidAlgorithmParameterException( + "Iteration count must be a positive number"); + } + return iCount; + } + } + + /* + * Obtain a PBEKeySpec for Mac services, after key and parameters + * validation. Used by HmacPKCS12PBECore (SunJCE) and P11Mac (SunPKCS11). + */ + public static PBEKeySpec getPBAKeySpec(Key key, + AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + char[] passwdChars; + byte[] salt = null; + int iCount = 0; + if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) { + passwdChars = pbeKey.getPassword(); + salt = pbeKey.getSalt(); // maybe null if unspecified + iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified + } else if (key instanceof SecretKey) { + byte[] passwdBytes; + if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) || + (passwdBytes = key.getEncoded()) == null) { + throw new InvalidKeyException("Missing password"); + } + passwdChars = new char[passwdBytes.length]; + for (int i = 0; i < passwdChars.length; i++) { + passwdChars[i] = (char) (passwdBytes[i] & 0x7f); + } + Arrays.fill(passwdBytes, (byte)0x00); + } else { + throw new InvalidKeyException("SecretKey of PBE type required"); + } + + try { + if (params == null) { + // should not auto-generate default values since current + // javax.crypto.Mac api does not have any method for caller to + // retrieve the generated defaults. + if ((salt == null) || (iCount == 0)) { + throw new InvalidAlgorithmParameterException( + "PBEParameterSpec required for salt " + + "and iteration count"); + } + } else if (!(params instanceof PBEParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "PBEParameterSpec type required"); + } else { + PBEParameterSpec pbeParams = (PBEParameterSpec) params; + // make sure the parameter values are consistent + if (salt != null) { + if (!Arrays.equals(salt, pbeParams.getSalt())) { + throw new InvalidAlgorithmParameterException( + "Inconsistent value of salt " + + "between key and params"); + } + } else { + salt = pbeParams.getSalt(); + } + if (iCount != 0) { + if (iCount != pbeParams.getIterationCount()) { + throw new InvalidAlgorithmParameterException( + "Different iteration count " + + "between key and params"); + } + } else { + iCount = pbeParams.getIterationCount(); + } + } + // For security purpose, we need to enforce a minimum length + // for salt; just require the minimum salt length to be 8-byte + // which is what PKCS#5 recommends and openssl does. + if (salt.length < 8) { + throw new InvalidAlgorithmParameterException( + "Salt must be at least 8 bytes long"); + } + if (iCount <= 0) { + throw new InvalidAlgorithmParameterException( + "IterationCount must be a positive number"); + } + return new PBEKeySpec(passwdChars, salt, iCount); + } finally { + Arrays.fill(passwdChars, '\0'); + } + } + + /* + * Check that the key implements the PBEKey interface. If params is an + * instance of PBEParameterSpec, validate consistency with the key's + * derivation data. Used by P11Mac and P11PBECipher (SunPKCS11). + */ + public static void checkKeyAndParams(Key key, + AlgorithmParameterSpec params, String algorithm) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) { + if (params instanceof PBEParameterSpec pbeParams) { + if (pbeParams.getIterationCount() != + pbeKey.getIterationCount() || + !Arrays.equals(pbeParams.getSalt(), pbeKey.getSalt())) { + throw new InvalidAlgorithmParameterException( + "Salt or iteration count parameters are " + + "not consistent with PBE key"); + } + } + } else { + throw new InvalidKeyException( + "Cannot use a " + algorithm + " service with a key that " + + "does not implement javax.crypto.interfaces.PBEKey"); + } + } +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java index 873c69d5b00..c65c2185a3e 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -262,10 +262,8 @@ final class P11Cipher extends CipherSpi { // no native padding support; use our own padding impl paddingObj = new PKCS5Padding(blockSize); padBuffer = new byte[blockSize]; - char[] tokenLabel = token.tokenInfo.label; // NSS requires block-sized updates in multi-part operations. - reqBlockUpdates = ((tokenLabel[0] == 'N' && tokenLabel[1] == 'S' - && tokenLabel[2] == 'S') ? true : false); + reqBlockUpdates = P11Util.isNSS(token); } } else { throw new NoSuchPaddingException("Unsupported padding " + padding); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index bd2d6e81311..6b26297b1b4 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -141,9 +141,7 @@ abstract class P11Key implements Key, Length { this.tokenObject = tokenObject; this.sensitive = sensitive; this.extractable = extractable; - char[] tokenLabel = this.token.tokenInfo.label; - isNSS = (tokenLabel[0] == 'N' && tokenLabel[1] == 'S' - && tokenLabel[2] == 'S'); + isNSS = P11Util.isNSS(this.token); boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS && extractable && !tokenObject); this.keyIDHolder = new NativeKeyHolder(this, keyID, session, @@ -354,6 +352,18 @@ abstract class P11Key implements Key, Length { return new P11SecretKey(session, keyID, algorithm, keyLength, attrs); } + static SecretKey pbeKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attrs, char[] password, byte[] salt, + int iterationCount) { + attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + return new P11PBEKey(session, keyID, algorithm, keyLength, + attrs, password, salt, iterationCount); + } + static SecretKey masterSecretKey(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attrs, int major, int minor) { @@ -487,6 +497,45 @@ abstract class P11Key implements Key, Length { } } + static final class P11PBEKey extends P11SecretKey + implements PBEKey { + private static final long serialVersionUID = 6847576994253634876L; + private char[] password; + private final byte[] salt; + private final int iterationCount; + P11PBEKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, + char[] password, byte[] salt, int iterationCount) { + super(session, keyID, algorithm, keyLength, attributes); + this.password = password.clone(); + this.salt = salt.clone(); + this.iterationCount = iterationCount; + } + + @Override + public char[] getPassword() { + if (password == null) { + throw new IllegalStateException("password has been cleared"); + } + return password.clone(); + } + + @Override + public byte[] getSalt() { + return salt.clone(); + } + + @Override + public int getIterationCount() { + return iterationCount; + } + + void clearPassword() { + Arrays.fill(password, '\0'); + password = null; + } + } + @SuppressWarnings("deprecation") private static class P11TlsMasterSecretKey extends P11SecretKey implements TlsMasterSecret { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java index cbf40bd39b4..ee90e8bab6f 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -29,8 +29,11 @@ import java.nio.ByteBuffer; import java.security.*; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; import javax.crypto.MacSpi; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; @@ -39,6 +42,7 @@ import sun.nio.ch.DirectBuffer; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*; +import sun.security.util.PBEUtil; /** * MAC implementation class. This class currently supports HMAC using @@ -65,6 +69,9 @@ final class P11Mac extends MacSpi { // algorithm name private final String algorithm; + // PBEKeyInfo if algorithm is PBE-related, otherwise null + private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi; + // mechanism object private final CK_MECHANISM ckMechanism; @@ -88,6 +95,7 @@ final class P11Mac extends MacSpi { super(); this.token = token; this.algorithm = algorithm; + this.svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(algorithm); Long params = null; macLength = switch ((int) mechanism) { case (int) CKM_MD5_HMAC -> 16; @@ -192,12 +200,53 @@ final class P11Mac extends MacSpi { // see JCE spec protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - if (params != null) { - throw new InvalidAlgorithmParameterException - ("Parameters not supported"); - } reset(true); - p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); + p11Key = null; + if (svcPbeKi != null) { + if (key instanceof P11Key) { + // If the key is a P11Key, it must come from a PBE derivation + // because this is a PBE Mac service. In addition to checking + // the key, check that params (if passed) are consistent. + PBEUtil.checkKeyAndParams(key, params, algorithm); + } else { + // If the key is not a P11Key, a derivation is needed. Data for + // derivation has to be carried either as part of the key or + // params. Use SunPKCS11 PBE key derivation to obtain a P11Key. + // Assign the derived key to p11Key because conversion is never + // needed for this case. + PBEKeySpec pbeKeySpec = PBEUtil.getPBAKeySpec(key, params); + try { + P11Key.P11PBEKey p11PBEKey = + P11SecretKeyFactory.derivePBEKey(token, + pbeKeySpec, svcPbeKi); + // This Mac service uses the token where the derived key + // lives so there won't be any need to re-derive and use + // the password. The p11Key cannot be accessed out of this + // class. + p11PBEKey.clearPassword(); + p11Key = p11PBEKey; + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } finally { + pbeKeySpec.clearPassword(); + } + } + if (params instanceof PBEParameterSpec pbeParams) { + // For PBE services, reassign params to the underlying + // service params. Notice that Mac services expect this + // value to be null. + params = pbeParams.getParameterSpec(); + } + } + if (params != null) { + throw new InvalidAlgorithmParameterException( + "Parameters not supported"); + } + // In non-PBE cases and PBE cases where we didn't derive, + // a key conversion might be needed. + if (p11Key == null) { + p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); + } try { initialize(); } catch (PKCS11Exception e) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java new file mode 100644 index 00000000000..dc4233793af --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * 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.pkcs11; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.BadPaddingException; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import sun.security.jca.JCAUtil; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.pkcs11.wrapper.PKCS11Exception; +import sun.security.util.PBEUtil; + +final class P11PBECipher extends CipherSpi { + private final Token token; + private final String pbeAlg; + private final P11Cipher cipher; + private final int blkSize; + private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi; + private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params(); + + P11PBECipher(Token token, String pbeAlg, long cipherMech) + throws PKCS11Exception, NoSuchAlgorithmException { + super(); + String cipherTrans; + if (cipherMech == CKM_AES_CBC_PAD || cipherMech == CKM_AES_CBC) { + cipherTrans = "AES/CBC/PKCS5Padding"; + } else { + throw new NoSuchAlgorithmException( + "Cipher transformation not supported."); + } + cipher = new P11Cipher(token, cipherTrans, cipherMech); + blkSize = cipher.engineGetBlockSize(); + this.pbeAlg = pbeAlg; + svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(pbeAlg); + assert svcPbeKi != null : "algorithm must be in KeyInfo map"; + this.token = token; + } + + // see JCE spec + @Override + protected void engineSetMode(String mode) + throws NoSuchAlgorithmException { + cipher.engineSetMode(mode); + } + + // see JCE spec + @Override + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + cipher.engineSetPadding(padding); + } + + // see JCE spec + @Override + protected int engineGetBlockSize() { + return cipher.engineGetBlockSize(); + } + + // see JCE spec + @Override + protected int engineGetOutputSize(int inputLen) { + return cipher.engineGetOutputSize(inputLen); + } + + // see JCE spec + @Override + protected byte[] engineGetIV() { + return cipher.engineGetIV(); + } + + // see JCE spec + @Override + protected AlgorithmParameters engineGetParameters() { + return pbes2Params.getAlgorithmParameters(blkSize, pbeAlg, + P11Util.getSunJceProvider(), JCAUtil.getSecureRandom()); + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + SecureRandom random) throws InvalidKeyException { + try { + engineInit(opmode, key, (AlgorithmParameterSpec) null, random); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException("requires PBE parameters", e); + } + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, + InvalidAlgorithmParameterException { + if (key instanceof P11Key) { + // If the key is a P11Key, it must come from a PBE derivation + // because this is a PBE Cipher service. In addition to checking the + // key, check that params (if passed) are consistent. + PBEUtil.checkKeyAndParams(key, params, pbeAlg); + // At this point, we know that the key is a P11PBEKey. + P11Key.P11PBEKey p11PBEKey = (P11Key.P11PBEKey) key; + // PBE services require a PBE key of the same algorithm and the + // underlying service (non-PBE) won't check it. + if (!pbeAlg.equals(p11PBEKey.getAlgorithm())) { + throw new InvalidKeyException("Cannot use a " + + p11PBEKey.getAlgorithm() + " key for a " + pbeAlg + + " service"); + } + if (params instanceof PBEParameterSpec pbeParams) { + params = pbeParams.getParameterSpec(); + } + pbes2Params.initialize(blkSize, opmode, + p11PBEKey.getIterationCount(), p11PBEKey.getSalt(), params, + random); + } else { + // If the key is not a P11Key, a derivation is needed. Data for + // derivation has to be carried either as part of the key or params. + // Use SunPKCS11 PBE key derivation to obtain a P11Key. + PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( + blkSize, svcPbeKi.keyLen, opmode, key, params, random); + try { + P11Key.P11PBEKey p11PBEKey = P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, svcPbeKi); + // The internal Cipher service uses the token where the + // derived key lives so there won't be any need to re-derive + // and use the password. The key cannot be accessed out of this + // class. + p11PBEKey.clearPassword(); + key = p11PBEKey; + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } finally { + pbeSpec.clearPassword(); + } + } + cipher.engineInit(opmode, key, pbes2Params.getIvSpec(), random); + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, + InvalidAlgorithmParameterException { + engineInit(opmode, key, PBEUtil.PBES2Params.getParameterSpec(params), + random); + } + + // see JCE spec + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, + int inputLen) { + return cipher.engineUpdate(input, inputOffset, inputLen); + } + + // see JCE spec + @Override + protected int engineUpdate(byte[] input, int inputOffset, + int inputLen, byte[] output, int outputOffset) + throws ShortBufferException { + return cipher.engineUpdate(input, inputOffset, inputLen, + output, outputOffset); + } + + // see JCE spec + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen); + } + + // see JCE spec + @Override + protected int engineDoFinal(byte[] input, int inputOffset, + int inputLen, byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen, output, + outputOffset); + } + + // see JCE spec + @Override + protected int engineGetKeySize(Key key) { + // It's guaranteed that when engineInit succeeds, the key length + // for the underlying cipher is equal to the PBE service key length. + // Otherwise, initialization fails. + return svcPbeKi.keyLen; + } + +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java index 6d547e892d4..91572aec6b2 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,15 +25,19 @@ package sun.security.pkcs11; +import java.nio.charset.StandardCharsets; import java.util.*; import java.security.*; import java.security.spec.*; import javax.crypto.*; +import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.*; import static sun.security.pkcs11.TemplateManager.*; + +import jdk.internal.access.SharedSecrets; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -52,44 +56,190 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { // algorithm name private final String algorithm; + // PBEKeyInfo if algorithm is PBE-related, otherwise null + private final PBEKeyInfo svcPbeKi; + P11SecretKeyFactory(Token token, String algorithm) { super(); this.token = token; this.algorithm = algorithm; + this.svcPbeKi = getPBEKeyInfo(algorithm); } - private static final Map keyTypes; + private static final Map keyInfo = new HashMap<>(); + private static final KeyInfo HMAC = new KeyInfo("HMAC", PCKK_HMAC); + private static final KeyInfo SSLMAC = new KeyInfo("SSLMAC", PCKK_SSLMAC); + + static KeyInfo getKeyInfo(String algo) { + KeyInfo ki = keyInfo.get(algo); + if (ki == null) { + String algoUpper = algo.toUpperCase(Locale.ENGLISH); + ki = keyInfo.get(algoUpper); + if (ki == null) { + if (algoUpper.startsWith("HMAC")) { + return HMAC; + } else if (algoUpper.startsWith("SSLMAC")) { + return SSLMAC; + } + } + } + return ki; + } + + static PBEKeyInfo getPBEKeyInfo(String algo) { + if (getKeyInfo(algo) instanceof PBEKeyInfo pbeKi) { + return pbeKi; + } + return null; + } + + private static void putKeyInfo(KeyInfo ki) { + keyInfo.put(ki.algo, ki); + keyInfo.put(ki.algo.toUpperCase(Locale.ENGLISH), ki); + } + + static sealed class KeyInfo permits PBEKeyInfo { + public final String algo; + public final long keyType; + + KeyInfo(String algo, long keyType) { + this.algo = algo; + this.keyType = keyType; + } + + // The P11SecretKeyFactory::convertKey method needs to know if a service + // type and a key are compatible. Trivial cases such as having the same + // algorithm names are handled directly. KeyInfo::checkUse helps with + // cases that require to retrieve the key's KeyInfo (ki), in addition to + // the service's KeyInfo (si), to make a decision. + static boolean checkUse(KeyInfo ki, KeyInfo si) { + if (si instanceof PBEKeyInfo && !si.algo.equals(ki.algo)) { + // PBE services require a PBE key of the same algorithm. + return false; + } + if (ki instanceof PBKDF2KeyInfo) { + // We cannot tell what the PBE key was derived for, + // so any service is allowed in principle. + return true; + } + // This path handles non-PBE cases where aliases are used (i.e: + // RC4 vs ARCFOUR) and mixed PBE - non-PBE cases (i.e.: a + // PBE-derived AES key used in an AES Cipher service). + return ki.keyType == si.keyType; + } + } + + static abstract sealed class PBEKeyInfo extends KeyInfo + permits AESPBEKeyInfo, PBKDF2KeyInfo, P12MacPBEKeyInfo { + public static final long INVALID_PRF = -1; + public final long kdfMech; + public final long prfMech; + public final int keyLen; + public final CK_ATTRIBUTE[] extraAttrs; + + protected PBEKeyInfo(String algo, long kdfMech, long prfMech, + long keyType, int keyLen, CK_ATTRIBUTE[] extraAttrs) { + super(algo, keyType); + this.kdfMech = kdfMech; + this.prfMech = prfMech; + this.keyLen = keyLen; + this.extraAttrs = extraAttrs; + } + } + + static final class AESPBEKeyInfo extends PBEKeyInfo { + private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] { + CK_ATTRIBUTE.ENCRYPT_TRUE}; + + AESPBEKeyInfo(String algo, long prfMech, int keyLen) { + super(algo, CKM_PKCS5_PBKD2, prfMech, CKK_AES, keyLen, attr); + } + } + + static final class PBKDF2KeyInfo extends PBEKeyInfo { + private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] { + CK_ATTRIBUTE.ENCRYPT_TRUE, CK_ATTRIBUTE.SIGN_TRUE}; + + PBKDF2KeyInfo(String algo, long prfMech) { + super(algo, CKM_PKCS5_PBKD2, prfMech, CKK_GENERIC_SECRET, -1, attr); + } + } + + static final class P12MacPBEKeyInfo extends PBEKeyInfo { + private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] { + CK_ATTRIBUTE.SIGN_TRUE}; + + P12MacPBEKeyInfo(String algo, long kdfMech, int keyLen) { + super(algo, kdfMech, PBEKeyInfo.INVALID_PRF, + CKK_GENERIC_SECRET, keyLen, attr); + } + } static { - keyTypes = new HashMap(); - addKeyType("RC4", CKK_RC4); - addKeyType("ARCFOUR", CKK_RC4); - addKeyType("DES", CKK_DES); - addKeyType("DESede", CKK_DES3); - addKeyType("AES", CKK_AES); - addKeyType("Blowfish", CKK_BLOWFISH); - addKeyType("ChaCha20", CKK_CHACHA20); - addKeyType("ChaCha20-Poly1305", CKK_CHACHA20); + putKeyInfo(new KeyInfo("RC4", CKK_RC4)); + putKeyInfo(new KeyInfo("ARCFOUR", CKK_RC4)); + putKeyInfo(new KeyInfo("DES", CKK_DES)); + putKeyInfo(new KeyInfo("DESede", CKK_DES3)); + putKeyInfo(new KeyInfo("AES", CKK_AES)); + putKeyInfo(new KeyInfo("Blowfish", CKK_BLOWFISH)); + putKeyInfo(new KeyInfo("ChaCha20", CKK_CHACHA20)); + putKeyInfo(new KeyInfo("ChaCha20-Poly1305", CKK_CHACHA20)); // we don't implement RC2 or IDEA, but we want to be able to generate // keys for those SSL/TLS ciphersuites. - addKeyType("RC2", CKK_RC2); - addKeyType("IDEA", CKK_IDEA); + putKeyInfo(new KeyInfo("RC2", CKK_RC2)); + putKeyInfo(new KeyInfo("IDEA", CKK_IDEA)); - addKeyType("TlsPremasterSecret", PCKK_TLSPREMASTER); - addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER); - addKeyType("TlsMasterSecret", PCKK_TLSMASTER); - addKeyType("Generic", CKK_GENERIC_SECRET); + putKeyInfo(new KeyInfo("TlsPremasterSecret", PCKK_TLSPREMASTER)); + putKeyInfo(new KeyInfo("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER)); + putKeyInfo(new KeyInfo("TlsMasterSecret", PCKK_TLSMASTER)); + putKeyInfo(new KeyInfo("Generic", CKK_GENERIC_SECRET)); + + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA1AndAES_128", + CKP_PKCS5_PBKD2_HMAC_SHA1, 128)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA224AndAES_128", + CKP_PKCS5_PBKD2_HMAC_SHA224, 128)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA256AndAES_128", + CKP_PKCS5_PBKD2_HMAC_SHA256, 128)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA384AndAES_128", + CKP_PKCS5_PBKD2_HMAC_SHA384, 128)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA512AndAES_128", + CKP_PKCS5_PBKD2_HMAC_SHA512, 128)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA1AndAES_256", + CKP_PKCS5_PBKD2_HMAC_SHA1, 256)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA224AndAES_256", + CKP_PKCS5_PBKD2_HMAC_SHA224, 256)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA256AndAES_256", + CKP_PKCS5_PBKD2_HMAC_SHA256, 256)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA384AndAES_256", + CKP_PKCS5_PBKD2_HMAC_SHA384, 256)); + putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA512AndAES_256", + CKP_PKCS5_PBKD2_HMAC_SHA512, 256)); + + putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA1", + CKP_PKCS5_PBKD2_HMAC_SHA1)); + putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA224", + CKP_PKCS5_PBKD2_HMAC_SHA224)); + putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA256", + CKP_PKCS5_PBKD2_HMAC_SHA256)); + putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA384", + CKP_PKCS5_PBKD2_HMAC_SHA384)); + putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA512", + CKP_PKCS5_PBKD2_HMAC_SHA512)); + + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA1", + CKM_PBA_SHA1_WITH_SHA1_HMAC, 160)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA224", + CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, 224)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA256", + CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, 256)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA384", + CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, 384)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA512", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512)); } - private static void addKeyType(String name, long id) { - Long l = Long.valueOf(id); - keyTypes.put(name, l); - keyTypes.put(name.toUpperCase(Locale.ENGLISH), l); - } - - // returns the PKCS11 key type of the specified algorithm - // no pseudo KeyTypes + // No pseudo key types static long getPKCS11KeyType(String algorithm) { long kt = getKeyType(algorithm); if (kt == -1 || kt > PCKK_ANY) { @@ -98,30 +248,18 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { return kt; } - // returns direct lookup result of keyTypes using algorithm static long getKeyType(String algorithm) { - Long l = keyTypes.get(algorithm); - if (l == null) { - algorithm = algorithm.toUpperCase(Locale.ENGLISH); - l = keyTypes.get(algorithm); - if (l == null) { - if (algorithm.startsWith("HMAC")) { - return PCKK_HMAC; - } else if (algorithm.startsWith("SSLMAC")) { - return PCKK_SSLMAC; - } - } - } - return (l != null) ? l.longValue() : -1; + KeyInfo ki = getKeyInfo(algorithm); + return ki == null ? -1 : ki.keyType; } /** * Convert an arbitrary key of algorithm into a P11Key of provider. * Used in engineTranslateKey(), P11Cipher.init(), and P11Mac.init(). */ - static P11Key convertKey(Token token, Key key, String algo) + static P11Key convertKey(Token token, Key key, String svcAlgo) throws InvalidKeyException { - return convertKey(token, key, algo, null); + return convertKey(token, key, svcAlgo, null); } /** @@ -129,30 +267,32 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { * P11Key of provider. * Used in P11KeyStore.storeSkey. */ - static P11Key convertKey(Token token, Key key, String algo, - CK_ATTRIBUTE[] extraAttrs) - throws InvalidKeyException { + static P11Key convertKey(Token token, Key key, String svcAlgo, + CK_ATTRIBUTE[] extraAttrs) throws InvalidKeyException { token.ensureValid(); - if (key == null) { - throw new InvalidKeyException("Key must not be null"); - } if (!(key instanceof SecretKey)) { throw new InvalidKeyException("Key must be a SecretKey"); } - long algoType; - if (algo == null) { - algo = key.getAlgorithm(); - algoType = getKeyType(algo); - } else { - algoType = getKeyType(algo); - long keyAlgorithmType = getKeyType(key.getAlgorithm()); - if (algoType != keyAlgorithmType) { - if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) { - // ignore key algorithm for MACs - } else { - throw new InvalidKeyException - ("Key algorithm must be " + algo); - } + final String keyAlgo = key.getAlgorithm(); + if (keyAlgo == null) { + throw new InvalidKeyException("Key must specify its algorithm"); + } + if (svcAlgo == null) { + svcAlgo = keyAlgo; + } + KeyInfo ki = null; + KeyInfo si = getKeyInfo(svcAlgo); + if (si == null) { + throw new InvalidKeyException("Unknown algorithm " + svcAlgo); + } + // Check if the key can be used for the service. + // Any key can be used for a MAC service. + if (svcAlgo != keyAlgo && + si.keyType != PCKK_HMAC && si.keyType != PCKK_SSLMAC) { + ki = getKeyInfo(keyAlgo); + if (ki == null || !KeyInfo.checkUse(ki, si)) { + throw new InvalidKeyException("Cannot use a " + keyAlgo + + " key for a " + svcAlgo + " service"); } } if (key instanceof P11Key p11Key) { @@ -184,15 +324,159 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { if (p11Key != null) { return p11Key; } - if (!"RAW".equalsIgnoreCase(key.getFormat())) { - throw new InvalidKeyException("Encoded format must be RAW"); + if (key instanceof PBEKey pbeKey) { + ki = ki == null ? getKeyInfo(keyAlgo) : ki; + if (ki instanceof PBEKeyInfo pbeKi) { + PBEKeySpec keySpec = getPbeKeySpec(pbeKey); + try { + p11Key = derivePBEKey(token, keySpec, pbeKi); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } finally { + keySpec.clearPassword(); + } + } else { + throw new InvalidKeyException("Cannot derive unknown " + + keyAlgo + " algorithm"); + } + } else { + if (si instanceof PBEKeyInfo) { + throw new InvalidKeyException("PBE service requires a PBE key"); + } + if (!"RAW".equalsIgnoreCase(key.getFormat())) { + throw new InvalidKeyException("Encoded format must be RAW"); + } + byte[] encoded = key.getEncoded(); + try { + p11Key = createKey(token, encoded, svcAlgo, si.keyType, + extraAttrs); + } finally { + Arrays.fill(encoded, (byte) 0); + } } - byte[] encoded = key.getEncoded(); - p11Key = createKey(token, encoded, algo, algoType, extraAttrs); token.secretCache.put(key, p11Key); return p11Key; } + static P11Key.P11PBEKey derivePBEKey(Token token, PBEKeySpec keySpec, + PBEKeyInfo pbeKi) throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec == null) { + throw new InvalidKeySpecException("PBEKeySpec must not be null"); + } + Session session = null; + char[] password = null; + char[] encPassword = null; + try { + session = token.getObjSession(); + CK_MECHANISM ckMech; + password = keySpec.getPassword(); + byte[] salt = keySpec.getSalt(); + int itCount = keySpec.getIterationCount(); + int keySize = keySpec.getKeyLength(); + assert password != null : + "PBEKeySpec does not allow a null password"; + if (salt == null) { + throw new InvalidKeySpecException("Salt not found"); + } + assert salt.length > 0 : "PBEKeySpec does not allow an empty salt"; + if (itCount < 1) { + throw new InvalidKeySpecException("Iteration count must be " + + "a non-zero positive integer"); + } + if (pbeKi.keyLen > 0) { + if (keySize == 0) { + keySize = pbeKi.keyLen; + } else if (keySize != pbeKi.keyLen) { + throw new InvalidKeySpecException( + "Key length is invalid for " + pbeKi.algo + " (" + + "expecting " + pbeKi.keyLen + " but was " + + keySize + ")"); + } + } + if (keySize < 1 || keySize % 8 != 0) { + throw new InvalidKeySpecException("Key length must be " + + "multiple of 8 and greater than zero"); + } + + if (pbeKi.kdfMech == CKM_PKCS5_PBKD2) { + encPassword = P11Util.encodePassword(password, + StandardCharsets.UTF_8, 0); + CK_VERSION p11Ver = token.p11.getVersion(); + if (P11Util.isNSS(token) || p11Ver.major < 2 || + p11Ver.major == 2 && p11Ver.minor < 40) { + // NSS keeps using the old structure beyond PKCS #11 v2.40. + ckMech = new CK_MECHANISM(pbeKi.kdfMech, + new CK_PKCS5_PBKD2_PARAMS(encPassword, salt, + itCount, pbeKi.prfMech)); + } else { + ckMech = new CK_MECHANISM(pbeKi.kdfMech, + new CK_PKCS5_PBKD2_PARAMS2(encPassword, salt, + itCount, pbeKi.prfMech)); + } + } else { + /* + * PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2). + * + * According to PKCS #11, "password" in CK_PBE_PARAMS is of + * CK_UTF8CHAR_PTR type. While this suggests a UTF-8 encoding, + * RFC 7292 Appendix B.1 indicates that the password has to be + * encoded as a BMPString with a 2-bytes NULL terminator. + */ + encPassword = P11Util.encodePassword(password, + StandardCharsets.UTF_16BE, 2); + ckMech = new CK_MECHANISM(pbeKi.kdfMech, + new CK_PBE_PARAMS(encPassword, salt, itCount)); + } + + CK_ATTRIBUTE[] attrs = + new CK_ATTRIBUTE[3 + pbeKi.extraAttrs.length]; + attrs[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY); + attrs[1] = new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3); + attrs[2] = new CK_ATTRIBUTE(CKA_KEY_TYPE, pbeKi.keyType); + System.arraycopy(pbeKi.extraAttrs, 0, attrs, 3, + pbeKi.extraAttrs.length); + CK_ATTRIBUTE[] attr = token.getAttributes( + O_GENERATE, CKO_SECRET_KEY, pbeKi.keyType, attrs); + long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); + return (P11Key.P11PBEKey) P11Key.pbeKey(session, keyID, pbeKi.algo, + keySize, attr, password, salt, itCount); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException("Could not create key", e); + } finally { + if (encPassword != null) { + Arrays.fill(encPassword, '\0'); + } + if (password != null) { + Arrays.fill(password, '\0'); + } + token.releaseSession(session); + } + } + + private static PBEKeySpec getPbeKeySpec(PBEKey pbeKey) { + int keyLength = 0; + if ("RAW".equalsIgnoreCase(pbeKey.getFormat())) { + byte[] encoded = pbeKey.getEncoded(); + if (encoded != null) { + keyLength = encoded.length << 3; + Arrays.fill(encoded, (byte) 0); + } + } + int ic = pbeKey.getIterationCount(); + byte[] salt = pbeKey.getSalt(); + char[] pwd = pbeKey.getPassword(); + try { + return keyLength == 0 ? + new PBEKeySpec(pwd, salt, ic) : + new PBEKeySpec(pwd, salt, ic, keyLength); + } finally { + if (pwd != null) { + Arrays.fill(pwd, '\0'); + } + } + } + static void fixDESParity(byte[] key, int offset) { for (int i = 0; i < 8; i++) { int b = key[offset] & 0xfe; @@ -286,36 +570,51 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { if (keySpec == null) { throw new InvalidKeySpecException("KeySpec must not be null"); } - if (keySpec instanceof SecretKeySpec) { + if (keySpec instanceof SecretKeySpec secretKeySpec) { try { - Key key = convertKey(token, (SecretKey)keySpec, algorithm); + Key key = convertKey(token, secretKeySpec, algorithm); return (SecretKey)key; } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } + } else if (keySpec instanceof PBEKeySpec pbeKeySpec && + svcPbeKi != null) { + return derivePBEKey(token, pbeKeySpec, svcPbeKi); } else if (algorithm.equalsIgnoreCase("DES")) { - if (keySpec instanceof DESKeySpec) { - byte[] keyBytes = ((DESKeySpec)keySpec).getKey(); - keySpec = new SecretKeySpec(keyBytes, "DES"); - return engineGenerateSecret(keySpec); + if (keySpec instanceof DESKeySpec desKeySpec) { + return generateDESSecret(desKeySpec.getKey(), "DES"); } } else if (algorithm.equalsIgnoreCase("DESede")) { - if (keySpec instanceof DESedeKeySpec) { - byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey(); - keySpec = new SecretKeySpec(keyBytes, "DESede"); - return engineGenerateSecret(keySpec); + if (keySpec instanceof DESedeKeySpec desEdeKeySpec) { + return generateDESSecret(desEdeKeySpec.getKey(), "DESede"); } } throw new InvalidKeySpecException ("Unsupported spec: " + keySpec.getClass().getName()); } + private SecretKey generateDESSecret(byte[] keyBytes, String desAlgo) + throws InvalidKeySpecException { + SecretKeySpec secretKeySpec = null; + try { + secretKeySpec = new SecretKeySpec(keyBytes, desAlgo); + return engineGenerateSecret(secretKeySpec); + } finally { + if (secretKeySpec != null) { + SharedSecrets.getJavaxCryptoSpecAccess() + .clearSecretKeySpec(secretKeySpec); + } + if (keyBytes != null) { + Arrays.fill(keyBytes, (byte) 0); + } + } + } + private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException { try { key = engineTranslateKey(key); if (!"RAW".equalsIgnoreCase(key.getFormat())) { - throw new InvalidKeySpecException - ("Could not obtain key bytes"); + throw new InvalidKeySpecException("Could not obtain key bytes"); } byte[] k = key.getEncoded(); return k; @@ -334,6 +633,9 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { } if (keySpec.isAssignableFrom(SecretKeySpec.class)) { return new SecretKeySpec(getKeyBytes(key), algorithm); + } else if (keySpec.isAssignableFrom(PBEKeySpec.class) && + key instanceof PBEKey pbeKey && svcPbeKi != null) { + return getPbeKeySpec(pbeKey); } else if (algorithm.equalsIgnoreCase("DES")) { try { if (keySpec.isAssignableFrom(DESKeySpec.class)) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java index 5a3229653de..8a2b745fe38 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -27,6 +27,9 @@ package sun.security.pkcs11; import java.lang.ref.Cleaner; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; import java.security.*; import sun.security.pkcs11.wrapper.PKCS11Exception; @@ -51,6 +54,46 @@ public final class P11Util { // empty } + static boolean isNSS(Token token) { + char[] tokenLabel = token.tokenInfo.label; + if (tokenLabel != null && tokenLabel.length >= 3) { + return (tokenLabel[0] == 'N' && tokenLabel[1] == 'S' + && tokenLabel[2] == 'S'); + } + return false; + } + + static char[] encodePassword(char[] password, Charset cs, + int nullTermBytes) { + /* + * When a Java char (2 bytes) is converted to CK_UTF8CHAR (1 byte) for + * a PKCS #11 (native) call, the high-order byte is discarded (see + * jCharArrayToCKUTF8CharArray in p11_util.c). In order to have an + * encoded string passed to C_GenerateKey, we need to account for + * truncation and expand beforehand: high and low parts of each char + * are split into 2 chars. As an example, this is the transformation + * for a NULL terminated password "a" that has to be encoded in + * UTF-16 BE: + * char[] password => [ 0x0061, 0x0000 ] + * / \ / \ + * ByteBuffer passwordBytes => [ 0x00, 0x61, 0x00, 0x00 ] + * | | | | + * char[] encPassword => [0x0000, 0x0061, 0x0000, 0x0000] + * | | | | + * PKCS #11 call (bytes) => [ 0x00, 0x61, 0x00, 0x00 ] + */ + ByteBuffer passwordBytes = cs.encode(CharBuffer.wrap(password)); + char[] encPassword = + new char[passwordBytes.remaining() + nullTermBytes]; + int i = 0; + while (passwordBytes.hasRemaining()) { + encPassword[i] = (char) (passwordBytes.get() & 0xFF); + // Erase password bytes as we read during encoding. + passwordBytes.put(i++, (byte) 0); + } + return encPassword; + } + static Provider getSunProvider() { Provider p = sun; if (p == null) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index b5b64054c38..5cd6828d293 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -421,14 +421,23 @@ public final class SunPKCS11 extends AuthProvider { final String className; final List aliases; final int[] mechanisms; + final int[] requiredMechs; private Descriptor(String type, String algorithm, String className, - List aliases, int[] mechanisms) { + List aliases, int[] mechanisms) { + this(type, algorithm, className, aliases, mechanisms, null); + } + // mechanisms is a list of possible mechanisms that implement the + // algorithm, at least one of them must be available. requiredMechs + // is a list of auxiliary mechanisms, all of them must be available + private Descriptor(String type, String algorithm, String className, + List aliases, int[] mechanisms, int[] requiredMechs) { this.type = type; this.algorithm = algorithm; this.className = className; this.aliases = aliases; this.mechanisms = mechanisms; + this.requiredMechs = requiredMechs; } private P11Service service(Token token, int mechanism) { return new P11Service @@ -470,12 +479,24 @@ public final class SunPKCS11 extends AuthProvider { register(new Descriptor(type, algorithm, className, aliases, m)); } + private static void d(String type, String algorithm, String className, + int[] m, int[] requiredMechs) { + register(new Descriptor(type, algorithm, className, null, m, + requiredMechs)); + } + private static void dA(String type, String algorithm, String className, int[] m) { register(new Descriptor(type, algorithm, className, getAliases(algorithm), m)); } + private static void dA(String type, String algorithm, String className, + int[] m, int[] requiredMechs) { + register(new Descriptor(type, algorithm, className, + getAliases(algorithm), m, requiredMechs)); + } + private static void register(Descriptor d) { for (int i = 0; i < d.mechanisms.length; i++) { int m = d.mechanisms[i]; @@ -526,6 +547,7 @@ public final class SunPKCS11 extends AuthProvider { String P11KeyWrapCipher = "sun.security.pkcs11.P11KeyWrapCipher"; String P11RSACipher = "sun.security.pkcs11.P11RSACipher"; String P11AEADCipher = "sun.security.pkcs11.P11AEADCipher"; + String P11PBECipher = "sun.security.pkcs11.P11PBECipher"; String P11Signature = "sun.security.pkcs11.P11Signature"; String P11PSSSignature = "sun.security.pkcs11.P11PSSSignature"; @@ -588,6 +610,24 @@ public final class SunPKCS11 extends AuthProvider { d(MAC, "SslMacSHA1", P11Mac, m(CKM_SSL3_SHA1_MAC)); + /* + * PBA HMacs + * + * KeyDerivationMech must be supported + * for these services to be available. + * + */ + d(MAC, "HmacPBESHA1", P11Mac, m(CKM_SHA_1_HMAC), + m(CKM_PBA_SHA1_WITH_SHA1_HMAC)); + d(MAC, "HmacPBESHA224", P11Mac, m(CKM_SHA224_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA256", P11Mac, m(CKM_SHA256_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA384", P11Mac, m(CKM_SHA384_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA512", P11Mac, m(CKM_SHA512_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + d(KPG, "RSA", P11KeyPairGenerator, getAliases("PKCS1"), m(CKM_RSA_PKCS_KEY_PAIR_GEN)); @@ -686,6 +726,60 @@ public final class SunPKCS11 extends AuthProvider { d(SKF, "ChaCha20", P11SecretKeyFactory, m(CKM_CHACHA20_POLY1305)); + /* + * PBE Secret Key Factories + * + * KeyDerivationPrf must be supported for these services + * to be available. + * + */ + d(SKF, "PBEWithHmacSHA1AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBEWithHmacSHA224AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBEWithHmacSHA256AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBEWithHmacSHA384AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBEWithHmacSHA512AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + d(SKF, "PBEWithHmacSHA1AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBEWithHmacSHA224AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBEWithHmacSHA256AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBEWithHmacSHA384AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBEWithHmacSHA512AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + /* + * PBA Secret Key Factories + */ + d(SKF, "HmacPBESHA1", P11SecretKeyFactory, + m(CKM_PBA_SHA1_WITH_SHA1_HMAC)); + d(SKF, "HmacPBESHA224", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA256", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA384", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA512", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + /* + * PBKDF2 Secret Key Factories + */ + dA(SKF, "PBKDF2WithHmacSHA1", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBKDF2WithHmacSHA224", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBKDF2WithHmacSHA256", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBKDF2WithHmacSHA384", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBKDF2WithHmacSHA512", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + // XXX attributes for Ciphers (supported modes, padding) dA(CIP, "ARCFOUR", P11Cipher, m(CKM_RC4)); @@ -779,6 +873,44 @@ public final class SunPKCS11 extends AuthProvider { d(CIP, "RSA/ECB/NoPadding", P11RSACipher, m(CKM_RSA_X_509)); + /* + * PBE Ciphers + * + * KeyDerivationMech and KeyDerivationPrf must be supported + * for these services to be available. + * + */ + d(CIP, "PBEWithHmacSHA1AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA_1_HMAC)); + d(CIP, "PBEWithHmacSHA224AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA224_HMAC)); + d(CIP, "PBEWithHmacSHA256AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA256_HMAC)); + d(CIP, "PBEWithHmacSHA384AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA384_HMAC)); + d(CIP, "PBEWithHmacSHA512AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA512_HMAC)); + d(CIP, "PBEWithHmacSHA1AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA_1_HMAC)); + d(CIP, "PBEWithHmacSHA224AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA224_HMAC)); + d(CIP, "PBEWithHmacSHA256AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA256_HMAC)); + d(CIP, "PBEWithHmacSHA384AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA384_HMAC)); + d(CIP, "PBEWithHmacSHA512AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA512_HMAC)); + d(SIG, "RawDSA", P11Signature, List.of("NONEwithDSA"), m(CKM_DSA)); @@ -1169,9 +1301,21 @@ public final class SunPKCS11 extends AuthProvider { if (ds == null) { continue; } + descLoop: for (Descriptor d : ds) { Integer oldMech = supportedAlgs.get(d); if (oldMech == null) { + if (d.requiredMechs != null) { + // Check that other mechanisms required for the + // service are supported before listing it as + // available for the first time. + for (int requiredMech : d.requiredMechs) { + if (token.getMechanismInfo( + requiredMech & 0xFFFFFFFFL) == null) { + continue descLoop; + } + } + } supportedAlgs.put(d, integerMech); continue; } @@ -1272,6 +1416,8 @@ public final class SunPKCS11 extends AuthProvider { } else if (algorithm.contains("/KW/") || algorithm.contains("/KWP/")) { return new P11KeyWrapCipher(token, algorithm, mechanism); + } else if (algorithm.startsWith("PBE")) { + return new P11PBECipher(token, algorithm, mechanism); } else { return new P11Cipher(token, algorithm, mechanism); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java index 88ff8a71fc3..233d84e2617 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -100,9 +100,9 @@ public class CK_ECDH1_DERIVE_PARAMS { } /** - * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * Returns the string representation of CK_ECDH1_DERIVE_PARAMS. * - * @return the string representation of CK_PKCS5_PBKD2_PARAMS + * @return the string representation of CK_ECDH1_DERIVE_PARAMS */ public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java index 0c9ebb289c1..fd2c59eb4e0 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -160,6 +160,18 @@ public class CK_MECHANISM { init(mechanism, params); } + public CK_MECHANISM(long mechanism, CK_PBE_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_PKCS5_PBKD2_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_PKCS5_PBKD2_PARAMS2 params) { + init(mechanism, params); + } + // For PSS. the parameter may be set multiple times, use the // CK_MECHANISM(long) constructor and setParameter(CK_RSA_PKCS_PSS_PARAMS) // methods instead of creating yet another constructor @@ -193,11 +205,12 @@ public class CK_MECHANISM { sb.append(Constants.INDENT); sb.append("mechanism: "); - sb.append(mechanism); + sb.append(Functions.getMechanismName(mechanism)); sb.append(Constants.NEWLINE); sb.append(Constants.INDENT); - sb.append("pParameter: "); + sb.append("pParameter:"); + sb.append(Constants.NEWLINE); sb.append(pParameter.toString()); sb.append(Constants.NEWLINE); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java index e8b048869c4..a25fa1c39e5 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -50,15 +50,15 @@ package sun.security.pkcs11.wrapper; /** - * class CK_PBE_PARAMS provides all of the necessary information required byte + * class CK_PBE_PARAMS provides all the necessary information required by * the CKM_PBE mechanisms and the CKM_PBA_SHA1_WITH_SHA1_HMAC mechanism.

* PKCS#11 structure: *

  * typedef struct CK_PBE_PARAMS {
- *   CK_CHAR_PTR pInitVector;
- *   CK_CHAR_PTR pPassword;
+ *   CK_BYTE_PTR pInitVector;
+ *   CK_UTF8CHAR_PTR pPassword;
  *   CK_ULONG ulPasswordLen;
- *   CK_CHAR_PTR pSalt;
+ *   CK_BYTE_PTR pSalt;
  *   CK_ULONG ulSaltLen;
  *   CK_ULONG ulIteration;
  * } CK_PBE_PARAMS;
@@ -72,15 +72,15 @@ public class CK_PBE_PARAMS {
     /**
      * PKCS#11:
      * 
-     *   CK_CHAR_PTR pInitVector;
+     *   CK_BYTE_PTR pInitVector;
      * 
*/ - public char[] pInitVector; + public byte[] pInitVector; /** * PKCS#11: *
-     *   CK_CHAR_PTR pPassword;
+     *   CK_UTF8CHAR_PTR pPassword;
      *   CK_ULONG ulPasswordLen;
      * 
*/ @@ -89,11 +89,11 @@ public class CK_PBE_PARAMS { /** * PKCS#11: *
-     *   CK_CHAR_PTR pSalt
+     *   CK_BYTE_PTR pSalt
      *   CK_ULONG ulSaltLen;
      * 
*/ - public char[] pSalt; + public byte[] pSalt; /** * PKCS#11: @@ -103,6 +103,12 @@ public class CK_PBE_PARAMS { */ public long ulIteration; + public CK_PBE_PARAMS(char[] pPassword, byte[] pSalt, long ulIteration) { + this.pPassword = pPassword; + this.pSalt = pSalt; + this.ulIteration = ulIteration; + } + /** * Returns the string representation of CK_PBE_PARAMS. * diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java index fb90bfced27..0bfbba51553 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java @@ -47,7 +47,7 @@ package sun.security.pkcs11.wrapper; - +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * class CK_PKCS5_PBKD2_PARAMS provides the parameters to the CKM_PKCS5_PBKD2 @@ -55,13 +55,15 @@ package sun.security.pkcs11.wrapper; * PKCS#11 structure: *
  * typedef struct CK_PKCS5_PBKD2_PARAMS {
- *   CK_PKCS5_PBKD2_SALT_SOURCE_TYPE saltSource;
+ *   CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
  *   CK_VOID_PTR pSaltSourceData;
  *   CK_ULONG ulSaltSourceDataLen;
  *   CK_ULONG iterations;
  *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
  *   CK_VOID_PTR pPrfData;
  *   CK_ULONG ulPrfDataLen;
+ *   CK_UTF8CHAR_PTR pPassword;
+ *   CK_ULONG_PTR ulPasswordLen;
  * } CK_PKCS5_PBKD2_PARAMS;
  * 
* @@ -112,6 +114,24 @@ public class CK_PKCS5_PBKD2_PARAMS { */ public byte[] pPrfData; + /** + * PKCS#11: + *
+     *   CK_UTF8CHAR_PTR pPassword
+     *   CK_ULONG_PTR ulPasswordLen;
+     * 
+ */ + public char[] pPassword; + + public CK_PKCS5_PBKD2_PARAMS(char[] pPassword, byte[] pSalt, + long iterations, long prf) { + this.pPassword = pPassword; + this.pSaltSourceData = pSalt; + this.iterations = iterations; + this.prf = prf; + this.saltSource = CKZ_SALT_SPECIFIED; + } + /** * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. * @@ -122,7 +142,7 @@ public class CK_PKCS5_PBKD2_PARAMS { sb.append(Constants.INDENT); sb.append("saltSource: "); - sb.append(saltSource); + sb.append(Functions.getParamSourcesName(saltSource)); sb.append(Constants.NEWLINE); sb.append(Constants.INDENT); @@ -132,7 +152,7 @@ public class CK_PKCS5_PBKD2_PARAMS { sb.append(Constants.INDENT); sb.append("ulSaltSourceDataLen: "); - sb.append(pSaltSourceData.length); + sb.append(Functions.getLength(pSaltSourceData)); sb.append(Constants.NEWLINE); sb.append(Constants.INDENT); @@ -142,7 +162,7 @@ public class CK_PKCS5_PBKD2_PARAMS { sb.append(Constants.INDENT); sb.append("prf: "); - sb.append(prf); + sb.append(Functions.getPrfName(prf)); sb.append(Constants.NEWLINE); sb.append(Constants.INDENT); @@ -152,7 +172,7 @@ public class CK_PKCS5_PBKD2_PARAMS { sb.append(Constants.INDENT); sb.append("ulPrfDataLen: "); - sb.append(pPrfData.length); + sb.append(Functions.getLength(pPrfData)); //buffer.append(Constants.NEWLINE); return sb.toString(); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java new file mode 100644 index 00000000000..56c7633427b --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * 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.pkcs11.wrapper; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * class CK_PKCS5_PBKD2_PARAMS2 provides the parameters to the CKM_PKCS5_PBKD2 + * mechanism.

+ * PKCS#11 structure: + *

+ * typedef struct CK_PKCS5_PBKD2_PARAMS2 {
+ *   CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ *   CK_VOID_PTR pSaltSourceData;
+ *   CK_ULONG ulSaltSourceDataLen;
+ *   CK_ULONG iterations;
+ *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ *   CK_VOID_PTR pPrfData;
+ *   CK_ULONG ulPrfDataLen;
+ *   CK_UTF8CHAR_PTR pPassword;
+ *   CK_ULONG ulPasswordLen;
+ * } CK_PKCS5_PBKD2_PARAMS2;
+ * 
+ * + */ +public class CK_PKCS5_PBKD2_PARAMS2 { + + /** + * PKCS#11: + *
+     *   CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+     * 
+ */ + public long saltSource; + + /** + * PKCS#11: + *
+     *   CK_VOID_PTR pSaltSourceData;
+     *   CK_ULONG ulSaltSourceDataLen;
+     * 
+ */ + public byte[] pSaltSourceData; + + /** + * PKCS#11: + *
+     *   CK_ULONG iterations;
+     * 
+ */ + public long iterations; + + /** + * PKCS#11: + *
+     *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+     * 
+ */ + public long prf; + + /** + * PKCS#11: + *
+     *   CK_VOID_PTR pPrfData;
+     *   CK_ULONG ulPrfDataLen;
+     * 
+ */ + public byte[] pPrfData; + + /** + * PKCS#11: + *
+     *   CK_UTF8CHAR_PTR pPassword
+     *   CK_ULONG ulPasswordLen;
+     * 
+ */ + public char[] pPassword; + + public CK_PKCS5_PBKD2_PARAMS2(char[] pPassword, byte[] pSalt, + long iterations, long prf) { + this.pPassword = pPassword; + this.pSaltSourceData = pSalt; + this.iterations = iterations; + this.prf = prf; + this.saltSource = CKZ_SALT_SPECIFIED; + } + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS2. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS2 + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(Constants.INDENT); + sb.append("saltSource: "); + sb.append(Functions.getParamSourcesName(saltSource)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("pSaltSourceData: "); + sb.append(Functions.toHexString(pSaltSourceData)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("ulSaltSourceDataLen: "); + sb.append(Functions.getLength(pSaltSourceData)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("iterations: "); + sb.append(iterations); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("prf: "); + sb.append(Functions.getPrfName(prf)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("pPrfData: "); + sb.append(Functions.toHexString(pPrfData)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("ulPrfDataLen: "); + sb.append(Functions.getLength(pPrfData)); + + return sb.toString(); + } + +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java index 1f9c4d39f57..5e3c1b9d29f 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java @@ -94,9 +94,9 @@ public class CK_X9_42_DH1_DERIVE_PARAMS { public byte[] pPublicData; /** - * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * Returns the string representation of CK_X9_42_DH1_DERIVE_PARAMS. * - * @return the string representation of CK_PKCS5_PBKD2_PARAMS + * @return the string representation of CK_X9_42_DH1_DERIVE_PARAMS */ public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/Functions.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/Functions.java index 7d8f32a6cce..31abd869a76 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/Functions.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/Functions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -104,6 +104,20 @@ public class Functions { private static final Map mgfIds = new HashMap(); + // Pseudo-random functions (CKP_*) + private static final Map prfNames = + new HashMap(); + + private static final Map prfIds = + new HashMap(); + + // Salt/Encoding parameter sources (CKZ_*) + private static final Map paramSourcesNames = + new HashMap(); + + private static final Map paramSourcesIds = + new HashMap(); + /** * For converting numbers to their hex presentation. */ @@ -208,6 +222,16 @@ public class Functions { return helpBigInteger.toString(2); } + /** + * get a byte array length as int + * + * @param value the byte array to get its length + * @return the byte array length as int or 0 if null + */ + public static int getLength(byte[] value) { + return value == null ? 0 : value.length; + } + private static class Flags { private final long[] flagIds; private final String[] flagNames; @@ -483,6 +507,22 @@ public class Functions { return getId(mgfIds, name); } + public static String getPrfName(long id) { + return getName(prfNames, id); + } + + public static long getPrfId(String name) { + return getId(prfIds, name); + } + + public static String getParamSourcesName(long id) { + return getName(paramSourcesNames, id); + } + + public static long getParamSourcesId(String name) { + return getId(paramSourcesIds, name); + } + /** * Check the given arrays for equalitiy. This method considers both arrays as * equal, if both are null or both have the same length and @@ -638,6 +678,14 @@ public class Functions { addMapping(mgfNames, mgfIds, id, name); } + private static void addPrf(long id, String name) { + addMapping(prfNames, prfIds, id, name); + } + + private static void addParamSources(long id, String name) { + addMapping(paramSourcesNames, paramSourcesIds, id, name); + } + // The ordering here follows the PKCS11Constants class static { addMech(CKM_RSA_PKCS_KEY_PAIR_GEN, "CKM_RSA_PKCS_KEY_PAIR_GEN"); @@ -1098,6 +1146,14 @@ public class Functions { addMech(CKM_VENDOR_DEFINED, "CKM_VENDOR_DEFINED"); addMech(CKM_NSS_TLS_PRF_GENERAL, "CKM_NSS_TLS_PRF_GENERAL"); + addMech(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, + "CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN"); + addMech(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, + "CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN"); + addMech(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, + "CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN"); + addMech(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, + "CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN"); addMech(PCKM_SECURERANDOM, "SecureRandom"); addMech(PCKM_KEYSTORE, "KeyStore"); @@ -1352,6 +1408,20 @@ public class Functions { addMGF(CKG_MGF1_SHA3_256, "CKG_MGF1_SHA3_256"); addMGF(CKG_MGF1_SHA3_384, "CKG_MGF1_SHA3_384"); addMGF(CKG_MGF1_SHA3_512, "CKG_MGF1_SHA3_512"); + + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA1, "CKP_PKCS5_PBKD2_HMAC_SHA1"); + addPrf(CKP_PKCS5_PBKD2_HMAC_GOSTR3411, + "CKP_PKCS5_PBKD2_HMAC_GOSTR3411"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA224, "CKP_PKCS5_PBKD2_HMAC_SHA224"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA256, "CKP_PKCS5_PBKD2_HMAC_SHA256"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA384, "CKP_PKCS5_PBKD2_HMAC_SHA384"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512, "CKP_PKCS5_PBKD2_HMAC_SHA512"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512_224, + "CKP_PKCS5_PBKD2_HMAC_SHA512_224"); + addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512_256, + "CKP_PKCS5_PBKD2_HMAC_SHA512_256"); + + addParamSources(CKZ_SALT_SPECIFIED, "CKZ_SALT_SPECIFIED"); } } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java index 0d65ee26805..43fda2011bb 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -999,6 +999,15 @@ public interface PKCS11Constants { // NSS private public static final long CKM_NSS_TLS_PRF_GENERAL = 0x80000373L; + // Additional PKCS #12 PBE key derivation algorithms defined in NSS v3.29 + public static final long CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN + /* (CKM_NSS + 29) */ = 0xCE53436DL; + public static final long CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN + /* (CKM_NSS + 30) */ = 0xCE53436EL; + public static final long CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN + /* (CKM_NSS + 31) */ = 0xCE53436FL; + public static final long CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN + /* (CKM_NSS + 32) */ = 0xCE534370L; // internal ids for our pseudo mechanisms SecureRandom and KeyStore public static final long PCKM_SECURERANDOM = 0x7FFFFF20L; @@ -1103,7 +1112,9 @@ public interface PKCS11Constants { public static final long CKD_BLAKE2B_256_KDF = 0x00000018L; public static final long CKD_BLAKE2B_384_KDF = 0x00000019L; public static final long CKD_BLAKE2B_512_KDF = 0x0000001aL; + */ + // PBKDF2 support, used in P11Util public static final long CKP_PKCS5_PBKD2_HMAC_SHA1 = 0x00000001L; public static final long CKP_PKCS5_PBKD2_HMAC_GOSTR3411 = 0x00000002L; public static final long CKP_PKCS5_PBKD2_HMAC_SHA224 = 0x00000003L; @@ -1115,6 +1126,7 @@ public interface PKCS11Constants { public static final long CKZ_SALT_SPECIFIED = 0x00000001L; + /* public static final long CK_OTP_VALUE = 0x00000000L; public static final long CK_OTP_PIN = 0x00000001L; public static final long CK_OTP_CHALLENGE = 0x00000002L; @@ -1153,9 +1165,9 @@ public interface PKCS11Constants { // private NSS attribute (for DSA and DH private keys) public static final long CKA_NETSCAPE_DB = 0xD5A0DB00L; - // base number of NSS private attributes - public static final long CKA_NETSCAPE_BASE /*0x80000000L + 0x4E534350L*/ - = 0xCE534350L; + // base number of NSS private attributes. CKA_NETSCAPE_BASE is now known as + // CKM_NSS = CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS = 0x80000000 | 0x4E534350 + public static final long CKA_NETSCAPE_BASE = 0xCE534350L; // object type for NSS trust public static final long CKO_NETSCAPE_TRUST = 0xCE534353L; diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c index 5765662e611..53ee635a24c 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c @@ -1512,9 +1512,11 @@ CK_VOID_PTR jMechParamToCKMechParamPtrSlow(JNIEnv *env, jobject jParam, case CKM_RSA_PKCS_OAEP: ckpParamPtr = jRsaPkcsOaepParamToCKRsaPkcsOaepParamPtr(env, jParam, ckpLength); break; - case CKM_PBE_SHA1_DES3_EDE_CBC: - case CKM_PBE_SHA1_DES2_EDE_CBC: case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: ckpParamPtr = jPbeParamToCKPbeParamPtr(env, jParam, ckpLength); break; case CKM_PKCS5_PBKD2: @@ -1658,13 +1660,13 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) // retrieve java values jPbeParamsClass = (*env)->FindClass(env, CLASS_PBE_PARAMS); if (jPbeParamsClass == NULL) { return NULL; } - fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pInitVector", "[C"); + fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pInitVector", "[B"); if (fieldID == NULL) { return NULL; } jInitVector = (*env)->GetObjectField(env, jParam, fieldID); fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pPassword", "[C"); if (fieldID == NULL) { return NULL; } jPassword = (*env)->GetObjectField(env, jParam, fieldID); - fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pSalt", "[C"); + fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pSalt", "[B"); if (fieldID == NULL) { return NULL; } jSalt = (*env)->GetObjectField(env, jParam, fieldID); fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "ulIteration", "J"); @@ -1680,15 +1682,15 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) // populate using java values ckParamPtr->ulIteration = jLongToCKULong(jIteration); - jCharArrayToCKCharArray(env, jInitVector, &(ckParamPtr->pInitVector), &ckTemp); + jByteArrayToCKByteArray(env, jInitVector, &(ckParamPtr->pInitVector), &ckTemp); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - jCharArrayToCKCharArray(env, jPassword, &(ckParamPtr->pPassword), &(ckParamPtr->ulPasswordLen)); + jCharArrayToCKUTF8CharArray(env, jPassword, &(ckParamPtr->pPassword), &(ckParamPtr->ulPasswordLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - jCharArrayToCKCharArray(env, jSalt, &(ckParamPtr->pSalt), &(ckParamPtr->ulSaltLen)); + jByteArrayToCKByteArray(env, jSalt, &(ckParamPtr->pSalt), &(ckParamPtr->ulSaltLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } @@ -1699,6 +1701,9 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) return ckParamPtr; cleanup: free(ckParamPtr->pInitVector); + if (ckParamPtr->pPassword != NULL) { + memset(ckParamPtr->pPassword, 0, ckParamPtr->ulPasswordLen); + } free(ckParamPtr->pPassword); free(ckParamPtr->pSalt); free(ckParamPtr); @@ -1767,31 +1772,60 @@ void copyBackPBEInitializationVector(JNIEnv *env, CK_MECHANISM *ckMechanism, job } } +#define PBKD2_PARAM_SET(member, value) \ + do { \ + if(ckParamPtr->version == PARAMS) { \ + ckParamPtr->params.v1.member = value; \ + } else { \ + ckParamPtr->params.v2.member = value; \ + } \ + } while(0) + +#define PBKD2_PARAM_ADDR(member) \ + ( \ + (ckParamPtr->version == PARAMS) ? \ + (void*) &ckParamPtr->params.v1.member : \ + (void*) &ckParamPtr->params.v2.member \ + ) + /* - * converts the Java CK_PKCS5_PBKD2_PARAMS object to a CK_PKCS5_PBKD2_PARAMS + * converts a Java CK_PKCS5_PBKD2_PARAMS object to a CK_PKCS5_PBKD2_PARAMS + * pointer, or a Java CK_PKCS5_PBKD2_PARAMS2 object to a CK_PKCS5_PBKD2_PARAMS2 * pointer * - * @param env - used to call JNI funktions to get the Java classes and objects - * @param jParam - the Java CK_PKCS5_PBKD2_PARAMS object to convert + * @param env - used to call JNI functions to get the Java classes and objects + * @param jParam - the Java object to convert * @param pLength - length of the allocated memory of the returned pointer - * @return pointer to the new CK_PKCS5_PBKD2_PARAMS structure + * @return pointer to the new structure */ -CK_PKCS5_PBKD2_PARAMS_PTR +CK_VOID_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) { - CK_PKCS5_PBKD2_PARAMS_PTR ckParamPtr; + VersionedPbkd2ParamsPtr ckParamPtr; + ParamVersion paramVersion; + CK_ULONG_PTR pUlPasswordLen; jclass jPkcs5Pbkd2ParamsClass; jfieldID fieldID; jlong jSaltSource, jIteration, jPrf; - jobject jSaltSourceData, jPrfData; + jobject jSaltSourceData, jPrfData, jPassword; if (pLength != NULL) { *pLength = 0L; } // retrieve java values - jPkcs5Pbkd2ParamsClass = (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS); - if (jPkcs5Pbkd2ParamsClass == NULL) { return NULL; } + if ((jPkcs5Pbkd2ParamsClass = + (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS)) != NULL + && (*env)->IsInstanceOf(env, jParam, jPkcs5Pbkd2ParamsClass)) { + paramVersion = PARAMS; + } else if ((jPkcs5Pbkd2ParamsClass = + (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS2)) != NULL + && (*env)->IsInstanceOf(env, jParam, jPkcs5Pbkd2ParamsClass)) { + paramVersion = PARAMS2; + } else { + p11ThrowPKCS11RuntimeException(env, "Unknown PBKD2 mechanism parameters class."); + return NULL; + } fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "saltSource", "J"); if (fieldID == NULL) { return NULL; } jSaltSource = (*env)->GetLongField(env, jParam, fieldID); @@ -1807,36 +1841,60 @@ jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pL fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "pPrfData", "[B"); if (fieldID == NULL) { return NULL; } jPrfData = (*env)->GetObjectField(env, jParam, fieldID); + fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "pPassword", "[C"); + if (fieldID == NULL) { return NULL; } + jPassword = (*env)->GetObjectField(env, jParam, fieldID); - // allocate memory for CK_PKCS5_PBKD2_PARAMS pointer - ckParamPtr = calloc(1, sizeof(CK_PKCS5_PBKD2_PARAMS)); + // allocate memory for VersionedPbkd2Params and store the structure version + ckParamPtr = calloc(1, sizeof(VersionedPbkd2Params)); if (ckParamPtr == NULL) { p11ThrowOutOfMemoryError(env, 0); return NULL; } + ckParamPtr->version = paramVersion; // populate using java values - ckParamPtr->saltSource = jLongToCKULong(jSaltSource); - jByteArrayToCKByteArray(env, jSaltSourceData, (CK_BYTE_PTR *) - &(ckParamPtr->pSaltSourceData), &(ckParamPtr->ulSaltSourceDataLen)); + PBKD2_PARAM_SET(saltSource, jLongToCKULong(jSaltSource)); + jByteArrayToCKByteArray(env, jSaltSourceData, + (CK_BYTE_PTR *) PBKD2_PARAM_ADDR(pSaltSourceData), + PBKD2_PARAM_ADDR(ulSaltSourceDataLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - ckParamPtr->iterations = jLongToCKULong(jIteration); - ckParamPtr->prf = jLongToCKULong(jPrf); - jByteArrayToCKByteArray(env, jPrfData, (CK_BYTE_PTR *) - &(ckParamPtr->pPrfData), &(ckParamPtr->ulPrfDataLen)); + PBKD2_PARAM_SET(iterations, jLongToCKULong(jIteration)); + PBKD2_PARAM_SET(prf, jLongToCKULong(jPrf)); + jByteArrayToCKByteArray(env, jPrfData, + (CK_BYTE_PTR *) PBKD2_PARAM_ADDR(pPrfData), + PBKD2_PARAM_ADDR(ulPrfDataLen)); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + if (ckParamPtr->version == PARAMS) { + pUlPasswordLen = calloc(1, sizeof(CK_ULONG)); + if (pUlPasswordLen == NULL) { + p11ThrowOutOfMemoryError(env, 0); + goto cleanup; + } + ckParamPtr->params.v1.ulPasswordLen = pUlPasswordLen; + } else { + pUlPasswordLen = &ckParamPtr->params.v2.ulPasswordLen; + } + jCharArrayToCKUTF8CharArray(env, jPassword, + (CK_CHAR_PTR *) PBKD2_PARAM_ADDR(pPassword), + pUlPasswordLen); if ((*env)->ExceptionCheck(env)) { goto cleanup; } if (pLength != NULL) { - *pLength = sizeof(CK_PKCS5_PBKD2_PARAMS); + *pLength = (ckParamPtr->version == PARAMS ? + sizeof(ckParamPtr->params.v1) : + sizeof(ckParamPtr->params.v2)); } + // VersionedPbkd2ParamsPtr is equivalent to CK_PKCS5_PBKD2_PARAMS[2]_PTR return ckParamPtr; cleanup: - free(ckParamPtr->pSaltSourceData); - free(ckParamPtr->pPrfData); + FREE_VERSIONED_PBKD2_MEMBERS(ckParamPtr); free(ckParamPtr); return NULL; diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c index ead7de0f37e..7421c1ff374 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c @@ -408,11 +408,32 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) { case CKM_CAMELLIA_CTR: // params do not contain pointers break; + case CKM_PKCS5_PBKD2: + // get the versioned structure from behind memory + TRACE0(((VersionedPbkd2ParamsPtr)tmp)->version == PARAMS ? + "[ CK_PKCS5_PBKD2_PARAMS ]\n" : + "[ CK_PKCS5_PBKD2_PARAMS2 ]\n"); + FREE_VERSIONED_PBKD2_MEMBERS((VersionedPbkd2ParamsPtr)tmp); + break; + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + TRACE0("[ CK_PBE_PARAMS ]\n"); + free(((CK_PBE_PARAMS_PTR)tmp)->pInitVector); + if (((CK_PBE_PARAMS_PTR)tmp)->pPassword != NULL) { + memset(((CK_PBE_PARAMS_PTR)tmp)->pPassword, 0, + ((CK_PBE_PARAMS_PTR)tmp)->ulPasswordLen); + } + free(((CK_PBE_PARAMS_PTR)tmp)->pPassword); + free(((CK_PBE_PARAMS_PTR)tmp)->pSalt); + break; default: // currently unsupported mechs by SunPKCS11 provider // CKM_RSA_PKCS_OAEP, CKM_ECMQV_DERIVE, // CKM_X9_42_*, CKM_KEA_DERIVE, CKM_RC2_*, CKM_RC5_*, - // CKM_SKIPJACK_*, CKM_KEY_WRAP_SET_OAEP, CKM_PKCS5_PBKD2, + // CKM_SKIPJACK_*, CKM_KEY_WRAP_SET_OAEP, // PBE mechs, WTLS mechs, CMS mechs, // CKM_EXTRACT_KEY_FROM_KEY, CKM_OTP, CKM_KIP, // CKM_DSA_PARAMETER_GEN?, CKM_GOSTR3410_* @@ -515,12 +536,11 @@ void jBooleanArrayToCKBBoolArray(JNIEnv *env, const jbooleanArray jArray, CK_BBO jboolean* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jboolean*) calloc(*ckpLength, sizeof(jboolean)); if (jpTemp == NULL) { p11ThrowOutOfMemoryError(env, 0); @@ -557,12 +577,11 @@ void jByteArrayToCKByteArray(JNIEnv *env, const jbyteArray jArray, CK_BYTE_PTR * jbyte* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jbyte*) calloc(*ckpLength, sizeof(jbyte)); if (jpTemp == NULL) { p11ThrowOutOfMemoryError(env, 0); @@ -604,12 +623,11 @@ void jLongArrayToCKULongArray(JNIEnv *env, const jlongArray jArray, CK_ULONG_PTR jlong* jTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jTemp = (jlong*) calloc(*ckpLength, sizeof(jlong)); if (jTemp == NULL) { p11ThrowOutOfMemoryError(env, 0); @@ -646,12 +664,11 @@ void jCharArrayToCKCharArray(JNIEnv *env, const jcharArray jArray, CK_CHAR_PTR * jchar* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jchar*) calloc(*ckpLength, sizeof(jchar)); if (jpTemp == NULL) { p11ThrowOutOfMemoryError(env, 0); @@ -688,12 +705,11 @@ void jCharArrayToCKUTF8CharArray(JNIEnv *env, const jcharArray jArray, CK_UTF8CH jchar* jTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jTemp = (jchar*) calloc(*ckpLength, sizeof(jchar)); if (jTemp == NULL) { p11ThrowOutOfMemoryError(env, 0); @@ -701,19 +717,20 @@ void jCharArrayToCKUTF8CharArray(JNIEnv *env, const jcharArray jArray, CK_UTF8CH } (*env)->GetCharArrayRegion(env, jArray, 0, *ckpLength, jTemp); if ((*env)->ExceptionCheck(env)) { - free(jTemp); - return; + goto cleanup; } *ckpArray = (CK_UTF8CHAR_PTR) calloc(*ckpLength, sizeof(CK_UTF8CHAR)); if (*ckpArray == NULL) { - free(jTemp); p11ThrowOutOfMemoryError(env, 0); - return; + goto cleanup; } for (i=0; i<(*ckpLength); i++) { (*ckpArray)[i] = jCharToCKUTF8Char(jTemp[i]); } +cleanup: + // Clean possible temporary copies of passwords. + memset(jTemp, 0, *ckpLength * sizeof(jchar)); free(jTemp); } diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h index 767c2798f6c..b0a514ed8c7 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h @@ -67,15 +67,22 @@ /* extra PKCS#11 constants not in the standard include files */ +/* CKA_NETSCAPE_BASE is now known as CKM_NSS (CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS) */ #define CKA_NETSCAPE_BASE (0x80000000 + 0x4E534350) #define CKA_NETSCAPE_TRUST_BASE (CKA_NETSCAPE_BASE + 0x2000) #define CKA_NETSCAPE_TRUST_SERVER_AUTH (CKA_NETSCAPE_TRUST_BASE + 8) #define CKA_NETSCAPE_TRUST_CLIENT_AUTH (CKA_NETSCAPE_TRUST_BASE + 9) -#define CKA_NETSCAPE_TRUST_CODE_SIGNING (CKA_NETSCAPE_TRUST_BASE + 10) +#define CKA_NETSCAPE_TRUST_CODE_SIGNING (CKA_NETSCAPE_TRUST_BASE + 10) #define CKA_NETSCAPE_TRUST_EMAIL_PROTECTION (CKA_NETSCAPE_TRUST_BASE + 11) #define CKA_NETSCAPE_DB 0xD5A0DB00 #define CKM_NSS_TLS_PRF_GENERAL 0x80000373 +/* additional PKCS #12 PBE key derivation algorithms defined in NSS v3.29 */ +#define CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 29) +#define CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 30) +#define CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 31) +#define CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 32) + /* Define the PKCS#11 functions to include and exclude. Reduces the size @@ -271,6 +278,7 @@ void printDebug(const char *format, ...); #define CLASS_PBE_PARAMS "sun/security/pkcs11/wrapper/CK_PBE_PARAMS" #define PBE_INIT_VECTOR_SIZE 8 #define CLASS_PKCS5_PBKD2_PARAMS "sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS" +#define CLASS_PKCS5_PBKD2_PARAMS2 "sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2" #define CLASS_EXTRACT_PARAMS "sun/security/pkcs11/wrapper/CK_EXTRACT_PARAMS" #define CLASS_ECDH1_DERIVE_PARAMS "sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS" @@ -383,7 +391,7 @@ CK_VOID_PTR jMechParamToCKMechParamPtr(JNIEnv *env, jobject jParam, CK_MECHANISM CK_RSA_PKCS_OAEP_PARAMS_PTR jRsaPkcsOaepParamToCKRsaPkcsOaepParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_PBE_PARAMS_PTR jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); -CK_PKCS5_PBKD2_PARAMS_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); +CK_VOID_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR jSsl3MasterKeyDeriveParamToCKSsl3MasterKeyDeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_SSL3_KEY_MAT_PARAMS_PTR jSsl3KeyMatParamToCKSsl3KeyMatParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_KEY_DERIVATION_STRING_DATA jKeyDerivationStringDataToCKKeyDerivationStringData(JNIEnv *env, jobject jParam); @@ -393,6 +401,41 @@ CK_ECDH2_DERIVE_PARAMS_PTR jEcdh2DeriveParamToCKEcdh2DeriveParamPtr(JNIEnv *env, CK_X9_42_DH1_DERIVE_PARAMS_PTR jX942Dh1DeriveParamToCKX942Dh1DeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_X9_42_DH2_DERIVE_PARAMS_PTR jX942Dh2DeriveParamToCKX942Dh2DeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); +/* handling of CK_PKCS5_PBKD2_PARAMS and CK_PKCS5_PBKD2_PARAMS2 */ +typedef enum {PARAMS=0, PARAMS2} ParamVersion; + +typedef struct { + union { + CK_PKCS5_PBKD2_PARAMS v1; + CK_PKCS5_PBKD2_PARAMS2 v2; + } params; + ParamVersion version; +} VersionedPbkd2Params, *VersionedPbkd2ParamsPtr; + +#define FREE_VERSIONED_PBKD2_MEMBERS(verParamsPtr) \ + do { \ + if ((verParamsPtr)->version == PARAMS) { \ + free((verParamsPtr)->params.v1.pSaltSourceData); \ + free((verParamsPtr)->params.v1.pPrfData); \ + if ((verParamsPtr)->params.v1.pPassword != NULL && \ + (verParamsPtr)->params.v1.ulPasswordLen \ + != NULL) { \ + memset((verParamsPtr)->params.v1.pPassword, 0, \ + *((verParamsPtr)->params.v1.ulPasswordLen)); \ + } \ + free((verParamsPtr)->params.v1.pPassword); \ + free((verParamsPtr)->params.v1.ulPasswordLen); \ + } else { \ + free((verParamsPtr)->params.v2.pSaltSourceData); \ + free((verParamsPtr)->params.v2.pPrfData); \ + if ((verParamsPtr)->params.v2.pPassword != NULL) { \ + memset((verParamsPtr)->params.v2.pPassword, 0, \ + (verParamsPtr)->params.v2.ulPasswordLen); \ + } \ + free((verParamsPtr)->params.v2.pPassword); \ + } \ + } while(0) + /* functions to copy the returned values inside CK-mechanism back to Java object */ void copyBackPBEInitializationVector(JNIEnv *env, CK_MECHANISM *ckMechanism, jobject jMechanism); diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java new file mode 100644 index 00000000000..04e9adf9663 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * + * 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. + */ + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +/* + * @test + * @bug 8301553 + * @summary test password based encryption on SunPKCS11's Cipher service + * @library /test/lib .. + * @run main/othervm/timeout=30 PBECipher + */ + +public final class PBECipher extends PKCS11Test { + private static final char[] password = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes( + StandardCharsets.UTF_8); + private static final int iterations = 1000; + private static final int AES_BLOCK_SIZE = 16; + private static final PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, + iterations, new IvParameterSpec(new byte[AES_BLOCK_SIZE])); + private static final String plainText = "This is a known plain text!"; + private static final String sep = "======================================" + + "==================================="; + + private enum Configuration { + // Pass salt and iterations to a Cipher through a PBEParameterSpec. + PBEParameterSpec, + + // Derive a key using SunPKCS11's SecretKeyFactory (wrapping password, + // salt and iterations in a PBEKeySpec), and pass it to a Cipher. + SecretKeyFactoryDerivedKey, + + // Pass salt and iterations to a Cipher through an AlgorithmParameters. + AlgorithmParameters, + + // Pass password, salt and iterations and iterations to + // a Cipher through an anonymous class implementing the + // javax.crypto.interfaces.PBEKey interface. + AnonymousPBEKey, + } + + private static Provider sunJCE = Security.getProvider("SunJCE"); + + private record AssertionData(String pbeCipherAlgo, String cipherAlgo, + BigInteger expectedCiphertext) {} + + private static AssertionData cipherAssertionData(String pbeCipherAlgo, + String cipherAlgo, String staticExpectedCiphertextString) { + BigInteger staticExpectedCiphertext = + new BigInteger(staticExpectedCiphertextString, 16); + BigInteger expectedCiphertext = null; + if (sunJCE != null) { + try { + expectedCiphertext = computeCipherText(sunJCE, pbeCipherAlgo, + pbeCipherAlgo, Configuration.PBEParameterSpec); + checkAssertionValues(expectedCiphertext, + staticExpectedCiphertext); + } catch (GeneralSecurityException e) { + // Move to staticExpectedCiphertext as it's unlikely + // that any of the algorithms are available. + sunJCE = null; + } + } + if (expectedCiphertext == null) { + expectedCiphertext = staticExpectedCiphertext; + } + return new AssertionData(pbeCipherAlgo, cipherAlgo, expectedCiphertext); + } + + private static void checkAssertionValues(BigInteger expectedValue, + BigInteger staticExpectedValue) { + if (!expectedValue.equals(staticExpectedValue)) { + printHex("SunJCE value", expectedValue); + printHex("Static value", staticExpectedValue); + throw new Error("Static and SunJCE values do not match."); + } + } + + // Generated with SunJCE. + private static final AssertionData[] assertionData = new AssertionData[]{ + cipherAssertionData("PBEWithHmacSHA1AndAES_128", + "AES/CBC/PKCS5Padding", "ba1c9614d550912925d99e0bc8969032" + + "7ac6258b72117dcf750c19ee6ca73dd4"), + cipherAssertionData("PBEWithHmacSHA224AndAES_128", + "AES/CBC/PKCS5Padding", "41960c43ca99cf2184511aaf2f0508a9" + + "7da3762ee6c2b7e2027c8076811f2e52"), + cipherAssertionData("PBEWithHmacSHA256AndAES_128", + "AES/CBC/PKCS5Padding", "6bb6a3dc3834e81e5ca6b5e70073ff46" + + "903b188940a269ed26db2ffe622b8e16"), + cipherAssertionData("PBEWithHmacSHA384AndAES_128", + "AES/CBC/PKCS5Padding", "22aabf7a6a059415dc4ca7d985f3de06" + + "8f8300ca48d8de585d802670f4c1d9bd"), + cipherAssertionData("PBEWithHmacSHA512AndAES_128", + "AES/CBC/PKCS5Padding", "b523e7c462a0b7fd74e492b3a6550464" + + "ceebe81f08649ae163673afc242ad8a2"), + cipherAssertionData("PBEWithHmacSHA1AndAES_256", + "AES/CBC/PKCS5Padding", "1e7c25e166afae069cec68ef9affca61" + + "aea02ab1c3dc7471cb767ed7d6e37af0"), + cipherAssertionData("PBEWithHmacSHA224AndAES_256", + "AES/CBC/PKCS5Padding", "6701f1cc75b6494ec4bd27158aa2c15d" + + "7d10bc2f1fbb7d92d8277c7edfd1dd57"), + cipherAssertionData("PBEWithHmacSHA256AndAES_256", + "AES/CBC/PKCS5Padding", "f82eb2fc016505baeb23ecdf85163933" + + "5e8d6d48b48631185641febb75898a1d"), + cipherAssertionData("PBEWithHmacSHA384AndAES_256", + "AES/CBC/PKCS5Padding", "ee9528022e58cdd9be80cd88443e03b3" + + "de13376cf97c53d946d5c5dfc88097be"), + cipherAssertionData("PBEWithHmacSHA512AndAES_256", + "AES/CBC/PKCS5Padding", "18f472912ffaa31824e20a5486324e14" + + "0225e20cb158762e8647b1216fe0ab7e"), + }; + + private static final class NoRandom extends SecureRandom { + @Override + public void nextBytes(byte[] bytes) {} + } + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + for (Configuration conf : Configuration.values()) { + for (AssertionData data : assertionData) { + testWith(sunPKCS11, data, true, conf); + if (conf != Configuration.PBEParameterSpec && + conf != Configuration.AlgorithmParameters) { + testWith(sunPKCS11, data, false, conf); + } + } + } + System.out.println("TEST PASS - OK"); + } + + private static void testWith(Provider sunPKCS11, AssertionData data, + boolean testPBEService, Configuration conf) throws Exception { + String svcAlgo = testPBEService ? data.pbeCipherAlgo : data.cipherAlgo; + System.out.println(sep + System.lineSeparator() + svcAlgo + + " (with " + conf.name() + ")"); + + BigInteger cipherText = computeCipherText(sunPKCS11, svcAlgo, + data.pbeCipherAlgo, conf); + printHex("Cipher Text", cipherText); + + if (!cipherText.equals(data.expectedCiphertext)) { + printHex("Expected Cipher Text", data.expectedCiphertext); + throw new Exception("Expected Cipher Text did not match"); + } + } + + private static BigInteger computeCipherText(Provider p, String svcAlgo, + String keyAlgo, Configuration conf) + throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance(svcAlgo, p); + switch (conf) { + case PBEParameterSpec, AlgorithmParameters -> { + SecretKey key = getPasswordOnlyPBEKey(); + switch (conf) { + case PBEParameterSpec -> { + cipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec); + } + case AlgorithmParameters -> { + AlgorithmParameters algoParams = + AlgorithmParameters.getInstance("PBES2"); + algoParams.init(pbeSpec); + cipher.init(Cipher.ENCRYPT_MODE, key, algoParams); + } + } + } + case SecretKeyFactoryDerivedKey -> { + SecretKey key = getDerivedSecretKey(p, keyAlgo); + cipher.init(Cipher.ENCRYPT_MODE, key, + pbeSpec.getParameterSpec()); + } + case AnonymousPBEKey -> { + SecretKey key = getAnonymousPBEKey(keyAlgo, + svcAlgo.equals(keyAlgo)); + cipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); + } + } + return new BigInteger(1, cipher.doFinal( + plainText.getBytes(StandardCharsets.UTF_8))); + } + + private static SecretKey getPasswordOnlyPBEKey() + throws GeneralSecurityException { + return SecretKeyFactory.getInstance("PBE") + .generateSecret(new PBEKeySpec(password)); + } + + private static SecretKey getDerivedSecretKey(Provider sunPKCS11, + String algorithm) throws GeneralSecurityException { + return SecretKeyFactory.getInstance(algorithm, sunPKCS11) + .generateSecret(new PBEKeySpec(password, salt, iterations)); + } + + private static SecretKey getAnonymousPBEKey(String algorithm, + boolean isPbeCipherSvc) { + return new PBEKey() { + public byte[] getSalt() { return salt.clone(); } + public int getIterationCount() { return iterations; } + public String getAlgorithm() { return algorithm; } + public String getFormat() { return "RAW"; } + public char[] getPassword() { return password.clone(); } + public byte[] getEncoded() { + byte[] encodedKey = null; + if (isPbeCipherSvc) { + encodedKey = new byte[password.length]; + for (int i = 0; i < password.length; i++) { + encodedKey[i] = (byte) (password[i] & 0x7f); + } + } + return encodedKey; + } + }; + } + + private static void printHex(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + public static void main(String[] args) throws Exception { + main(new PBECipher()); + } +} diff --git a/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java b/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java new file mode 100644 index 00000000000..395fe416858 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.Provider; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/* + * @test + * @bug 8301553 + * @summary test SunPKCS11's password based privacy and integrity + * applied to PKCS #12 keystores + * @library /test/lib .. + * @modules java.base/sun.security.util + * @run main/othervm/timeout=30 ImportKeyToP12 + */ + +public final class ImportKeyToP12 extends PKCS11Test { + private static final String alias = "alias"; + private static final char[] password = "123456".toCharArray(); + private static final Key key = new SecretKeySpec(new byte[] { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }, "AES"); + private static final String[] pbeCipherAlgs = new String[] { + "PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA224AndAES_128", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA384AndAES_128", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA1AndAES_256", + "PBEWithHmacSHA224AndAES_256", "PBEWithHmacSHA256AndAES_256", + "PBEWithHmacSHA384AndAES_256", "PBEWithHmacSHA512AndAES_256" + }; + private static final String[] pbeMacAlgs = new String[] { + "HmacPBESHA1", "HmacPBESHA224", "HmacPBESHA256", + "HmacPBESHA384", "HmacPBESHA512" + }; + private static final KeyStore p12; + private static final String sep = "======================================" + + "==================================="; + + static { + KeyStore tP12 = null; + try { + tP12 = KeyStore.getInstance("PKCS12"); + } catch (KeyStoreException e) {} + p12 = tP12; + } + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + // Test all privacy PBE algorithms with an integrity algorithm fixed + for (String pbeCipherAlg : pbeCipherAlgs) { + // Make sure that SunPKCS11 implements the Cipher algorithm + Cipher.getInstance(pbeCipherAlg, sunPKCS11); + testWith(sunPKCS11, pbeCipherAlg, pbeMacAlgs[0]); + } + // Test all integrity PBE algorithms with a privacy algorithm fixed + for (String pbeMacAlg : pbeMacAlgs) { + // Make sure that SunPKCS11 implements the Mac algorithm + Mac.getInstance(pbeMacAlg, sunPKCS11); + testWith(sunPKCS11, pbeCipherAlgs[0], pbeMacAlg); + } + System.out.println("TEST PASS - OK"); + } + + /* + * Consistency test: 1) store a secret key in a PKCS #12 keystore using + * PBE algorithms from SunPKCS11 and, 2) read the secret key from the + * PKCS #12 keystore using PBE algorithms from other security providers + * such as SunJCE. + */ + private void testWith(Provider sunPKCS11, String pbeCipherAlg, + String pbeMacAlg) throws Exception { + System.out.println(sep + System.lineSeparator() + + "Cipher PBE: " + pbeCipherAlg + System.lineSeparator() + + "Mac PBE: " + pbeMacAlg); + + System.setProperty("keystore.pkcs12.macAlgorithm", pbeMacAlg); + System.setProperty("keystore.pkcs12.keyProtectionAlgorithm", + pbeCipherAlg); + + // Create an empty PKCS #12 keystore + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + p12.load(null, password); + + // Use PBE privacy and integrity algorithms from SunPKCS11 to store + // the secret key + Security.insertProviderAt(sunPKCS11, 1); + p12.setKeyEntry(alias, key, password, null); + p12.store(baos, password); + + // Use PBE privacy and integrity algorithms from other security + // providers, such as SunJCE, to read the secret key + Security.removeProvider(sunPKCS11.getName()); + p12.load(new ByteArrayInputStream(baos.toByteArray()), password); + Key k = p12.getKey(alias, password); + + if (!MessageDigest.isEqual(key.getEncoded(), k.getEncoded())) { + throw new Exception("Keys differ. Consistency check failed."); + } + System.out.println("Secret key import successful" + + System.lineSeparator() + sep); + } + + public static void main(String[] args) throws Exception { + main(new ImportKeyToP12()); + } +} diff --git a/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java b/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java index f009fbb95e5..297c5b27a38 100644 --- a/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java +++ b/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -37,12 +37,10 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; -import java.security.SecureRandom; import java.util.List; import javax.crypto.Mac; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; public class MacSameTest extends PKCS11Test { @@ -69,14 +67,10 @@ public class MacSameTest extends PKCS11Test { public void main(Provider p) { List algorithms = getSupportedAlgorithms("Mac", "Hmac", p); boolean success = true; - SecureRandom srdm = new SecureRandom(); for (String alg : algorithms) { // first try w/ java secret key object - byte[] keyVal = new byte[KEY_SIZE]; - srdm.nextBytes(keyVal); - SecretKey skey = new SecretKeySpec(keyVal, alg); - + SecretKey skey = generateKey(alg, KEY_SIZE); try { doTest(alg, skey, p); } catch (Exception e) { @@ -86,6 +80,7 @@ public class MacSameTest extends PKCS11Test { } try { + // No KeyGenerator support for PBE KeyGenerator kg = KeyGenerator.getInstance(alg, p); kg.init(KEY_SIZE); skey = kg.generateKey(); diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java new file mode 100644 index 00000000000..d0b3312c52b --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. + * + * 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. + */ + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.Provider; +import java.security.Security; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +/* + * @test + * @bug 8301553 + * @summary test password based authentication on SunPKCS11's Mac service + * @library /test/lib .. + * @run main/othervm/timeout=30 PBAMac + */ + +public final class PBAMac extends PKCS11Test { + private static final char[] password = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes( + StandardCharsets.UTF_8); + private static final int iterations = 1000; + private static final String plainText = "This is a known plain text!"; + private static final String sep = "======================================" + + "==================================="; + + private enum Configuration { + // Pass salt and iterations to a Mac through a PBEParameterSpec. + PBEParameterSpec, + + // Derive a key using SunPKCS11's SecretKeyFactory (wrapping password, + // salt and iterations in a PBEKeySpec), and pass it to a Mac. + SecretKeyFactoryDerivedKey, + + // Pass password, salt and iterations and iterations to + // a Mac through an anonymous class implementing the + // javax.crypto.interfaces.PBEKey interface. + AnonymousPBEKey, + } + + private static Provider sunJCE = Security.getProvider("SunJCE"); + + private record AssertionData(String pbeHmacAlgo, String hmacAlgo, + BigInteger expectedMac) {} + + private static AssertionData macAssertionData(String pbeHmacAlgo, + String hmacAlgo, String staticExpectedMacString) { + BigInteger staticExpectedMac = new BigInteger(staticExpectedMacString, + 16); + BigInteger expectedMac = null; + if (sunJCE != null) { + try { + expectedMac = computeMac(sunJCE, pbeHmacAlgo, + pbeHmacAlgo, Configuration.PBEParameterSpec); + checkAssertionValues(expectedMac, staticExpectedMac); + } catch (GeneralSecurityException e) { + // Move to staticExpectedMac as it's unlikely + // that any of the algorithms are available. + sunJCE = null; + } + } + if (expectedMac == null) { + expectedMac = staticExpectedMac; + } + return new AssertionData(pbeHmacAlgo, hmacAlgo, expectedMac); + } + + private static void checkAssertionValues(BigInteger expectedValue, + BigInteger staticExpectedValue) { + if (!expectedValue.equals(staticExpectedValue)) { + printHex("SunJCE value", expectedValue); + printHex("Static value", staticExpectedValue); + throw new Error("Static and SunJCE values do not match."); + } + } + + // Generated with SunJCE. + private static final AssertionData[] assertionData = new AssertionData[]{ + macAssertionData("HmacPBESHA1", "HmacSHA1", + "707606929395e4297adc63d520ac7d22f3f5fa66"), + macAssertionData("HmacPBESHA224", "HmacSHA224", + "4ffb5ad4974a7a9fca5a36ebe3e34dd443c07fb68c392f8b611657e6"), + macAssertionData("HmacPBESHA256", "HmacSHA256", + "9e8c102c212d2fd1334dc497acb4e002b04e84713b7eda5a63807af2" + + "989d3e50"), + macAssertionData("HmacPBESHA384", "HmacSHA384", + "77f31a785d4f2220251143a4ba80f5610d9d0aeaebb4a278b8a7535c" + + "8cea8e8211809ba450458e351c5b66d691839c23"), + macAssertionData("HmacPBESHA512", "HmacSHA512", + "a53f942a844b234a69c1f92cba20ef272c4394a3cf4024dc16d9dbac" + + "1969870b1c2b28b897149a1a3b9ad80a7ca8c547dfabf3ed5f144c6b" + + "593900b62e120c45"), + }; + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + for (Configuration conf : Configuration.values()) { + for (AssertionData data : assertionData) { + testWith(sunPKCS11, data, true, conf); + if (conf != Configuration.PBEParameterSpec) { + testWith(sunPKCS11, data, false, conf); + } + } + } + System.out.println("TEST PASS - OK"); + } + + private static void testWith(Provider sunPKCS11, AssertionData data, + boolean testPBEService, Configuration conf) throws Exception { + String svcAlgo = testPBEService ? data.pbeHmacAlgo : data.hmacAlgo; + System.out.println(sep + System.lineSeparator() + svcAlgo + + " (with " + conf.name() + ")"); + + BigInteger mac = computeMac(sunPKCS11, svcAlgo, data.pbeHmacAlgo, conf); + printHex("HMAC", mac); + + if (!mac.equals(data.expectedMac)) { + printHex("Expected HMAC", data.expectedMac); + throw new Exception("Expected HMAC did not match"); + } + } + + private static BigInteger computeMac(Provider p, String svcAlgo, + String keyAlgo, Configuration conf) + throws GeneralSecurityException { + Mac mac = Mac.getInstance(svcAlgo, p); + switch (conf) { + case PBEParameterSpec -> { + SecretKey key = getPasswordOnlyPBEKey(); + mac.init(key, new PBEParameterSpec(salt, iterations)); + } + case SecretKeyFactoryDerivedKey -> { + SecretKey key = getDerivedSecretKey(p, keyAlgo); + mac.init(key); + } + case AnonymousPBEKey -> { + SecretKey key = getAnonymousPBEKey(keyAlgo); + mac.init(key); + } + } + return new BigInteger(1, mac.doFinal( + plainText.getBytes(StandardCharsets.UTF_8))); + } + + private static SecretKey getPasswordOnlyPBEKey() + throws GeneralSecurityException { + return SecretKeyFactory.getInstance("PBE") + .generateSecret(new PBEKeySpec(password)); + } + + private static SecretKey getDerivedSecretKey(Provider sunPKCS11, + String algorithm) throws GeneralSecurityException { + return SecretKeyFactory.getInstance(algorithm, sunPKCS11) + .generateSecret(new PBEKeySpec(password, salt, iterations)); + } + + private static SecretKey getAnonymousPBEKey(String algorithm) { + return new PBEKey() { + public byte[] getSalt() { return salt.clone(); } + public int getIterationCount() { return iterations; } + public String getAlgorithm() { return algorithm; } + public String getFormat() { return "RAW"; } + public char[] getPassword() { return password.clone(); } + public byte[] getEncoded() { return null; } + }; + } + + private static void printHex(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + public static void main(String[] args) throws Exception { + main(new PBAMac()); + } +} diff --git a/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java b/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java index 66fd7d88ce4..57ea24cdf8f 100644 --- a/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -37,7 +37,7 @@ import java.security.Provider; import java.util.Random; import java.util.List; import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; +import javax.crypto.SecretKey; public class ReinitMac extends PKCS11Test { @@ -51,13 +51,12 @@ public class ReinitMac extends PKCS11Test { Random random = new Random(); byte[] data = new byte[10 * 1024]; random.nextBytes(data); - byte[] keyVal = new byte[16]; - random.nextBytes(keyVal); boolean success = true; for (String alg : algorithms) { + SecretKey skey = generateKey(alg, 16); try { - doTest(alg, p, keyVal, data); + doTest(alg, p, skey, data); } catch (Exception e) { System.out.println("Unexpected exception: " + e); e.printStackTrace(); @@ -72,10 +71,9 @@ public class ReinitMac extends PKCS11Test { } } - private void doTest(String alg, Provider p, byte[] keyVal, byte[] data) + private void doTest(String alg, Provider p, SecretKey key, byte[] data) throws Exception { System.out.println("Testing " + alg); - SecretKeySpec key = new SecretKeySpec(keyVal, alg); Mac mac = Mac.getInstance(alg, p); mac.init(key); mac.init(key); diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index a9a8a8178ee..92c452a69b0 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -40,6 +40,7 @@ import java.security.NoSuchProviderException; import java.security.Policy; import java.security.Provider; import java.security.ProviderException; +import java.security.SecureRandom; import java.security.Security; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; @@ -55,6 +56,9 @@ import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + import jdk.test.lib.artifacts.Artifact; import jdk.test.lib.artifacts.ArtifactResolver; import jdk.test.lib.artifacts.ArtifactResolverException; @@ -828,6 +832,58 @@ public abstract class PKCS11Test { return algorithms; } + private static final SecureRandom srdm = new SecureRandom(); + + static SecretKey generateKey(String alg, int keySize) { + if (alg.contains("PBE")) { + return generateKeyPBE(alg, keySize); + } else { + return generateKeyNonPBE(alg, keySize); + } + } + + private static SecretKey generateKeyNonPBE(String alg, int keySize) { + byte[] keyVal = new byte[keySize]; + srdm.nextBytes(keyVal); + return new SecretKeySpec(keyVal, alg); + } + + private static SecretKey generateKeyPBE(String alg, int keySize) { + char[] pass = new char[keySize]; + for (int i = 0; i < pass.length; i++) { + pass[i] = (char) ('0' + srdm.nextInt(74)); + } + byte[] salt = new byte[srdm.nextInt(8, 16)]; + srdm.nextBytes(salt); + int iterations = srdm.nextInt(1, 1000); + return new javax.crypto.interfaces.PBEKey() { + @Override + public String getAlgorithm() { + return "PBE"; + } + @Override + public String getFormat() { + return null; + } + @Override + public byte[] getEncoded() { + throw new RuntimeException("Should not be called"); + } + @Override + public char[] getPassword() { + return pass; + } + @Override + public byte[] getSalt() { + return salt; + } + @Override + public int getIterationCount() { + return iterations; + } + }; + } + static byte[] generateData(int length) { byte data[] = new byte[length]; for (int i=0; i PKCS12PBECipherCore = Class.forName( + "com.sun.crypto.provider.PKCS12PBECipherCore"); + + Field macKeyField = PKCS12PBECipherCore.getDeclaredField("MAC_KEY"); + macKeyField.setAccessible(true); + int MAC_KEY = (int) macKeyField.get(null); + + Method deriveMethod = PKCS12PBECipherCore.getDeclaredMethod( + "derive", char[].class, byte[].class, int.class, + int.class, int.class, String.class, int.class); + deriveMethod.setAccessible(true); + expectedKey = i((byte[]) deriveMethod.invoke(null, + keySpec.getPassword(), keySpec.getSalt(), + keySpec.getIterationCount(), keySpec.getKeyLength() / 8, + MAC_KEY, hashAlgo, blockLen)); + checkAssertionValues(expectedKey, staticExpectedKey); + } catch (ReflectiveOperationException ignored) { + expectedKey = staticExpectedKey; + } + return new AssertionData(algo, keySpec, expectedKey); + } + + private static AssertionData pbkd2AssertionData(String algo, + char[] password, int keyLen, String kdfAlgo, + String staticExpectedKeyString) { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); + BigInteger staticExpectedKey = new BigInteger(staticExpectedKeyString, + 16); + BigInteger expectedKey = null; + if (sunJCE != null) { + try { + expectedKey = i(SecretKeyFactory.getInstance(kdfAlgo, sunJCE) + .generateSecret(keySpec).getEncoded()); + checkAssertionValues(expectedKey, staticExpectedKey); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + // Move to staticExpectedKey as it's unlikely + // that any of the algorithms are available. + sunJCE = null; + } + } + if (expectedKey == null) { + expectedKey = staticExpectedKey; + } + return new AssertionData(algo, keySpec, expectedKey); + } + + private static void checkAssertionValues(BigInteger expectedValue, + BigInteger staticExpectedValue) { + if (!expectedValue.equals(staticExpectedValue)) { + printHex("SunJCE value", expectedValue); + printHex("Static value", staticExpectedValue); + throw new Error("Static and SunJCE values do not match."); + } + } + + private static final char[] pwd = "123456\uA4F7".toCharArray(); + private static final char[] emptyPwd = new char[0]; + private static final byte[] salt = "abcdefgh".getBytes( + StandardCharsets.UTF_8); + private static final int iterations = 1000; + + // Generated with SunJCE. Keep a reference to some + // entries for tests executing invalid conditions. + private static final AssertionData hmacPBESHA1Data = + p12PBKDAssertionData("HmacPBESHA1", pwd, 160, "SHA-1", 64, + "13156c6bee8e13ef568231e0174651afa5a358b0"); + private static final AssertionData hmacPBESHA224Data = + p12PBKDAssertionData("HmacPBESHA224", pwd, 224, "SHA-224", 64, + "d93acf4b3bea8a89d098e290928840c0b693a30cad0117f70ace50c2"); + private static final AssertionData pbeWithHmacSHA512AndAES256Data = + pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898" + + "7e3077595f90b60ab96d4f29203927b0"); + private static final AssertionData pbkdf2WithHmacSHA256Data = + pbkd2AssertionData("PBKDF2WithHmacSHA256", pwd, 384, + "PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8" + + "44984e3e9b619488a42b93dd6453f630ae3e2ad7ed809fa9e98a7921" + + "87d62e84"); + private static final AssertionData[] assertionData = new AssertionData[]{ + hmacPBESHA1Data, + hmacPBESHA224Data, + p12PBKDAssertionData("HmacPBESHA256", pwd, 256, "SHA-256", 64, + "1bb3ed1ffb784ed32f59b4d7515971699af99cf67a2e574000964c8e" + + "1eba1c45"), + p12PBKDAssertionData("HmacPBESHA384", pwd, 384, "SHA-384", 128, + "d4ce121d3cec88a8c8b0c6225f7f996b72d76017c2d91bc51fd47985" + + "86d1012d1ad03a39fdcd0fdc438d164ab50259fc"), + p12PBKDAssertionData("HmacPBESHA512", pwd, 512, "SHA-512", 128, + "5f80b350986e5156669193eaa42a107e7d6636d82fb550f67af5b2c2" + + "f546d977b70e52bbbcb6bb8976f9d3f0eaf9bfef5306c50ee5ccda3e" + + "e4c4c7c8421fe4d"), + pbkd2AssertionData("PBEWithHmacSHA1AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d"), + pbkd2AssertionData("PBEWithHmacSHA224AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e"), + pbkd2AssertionData("PBEWithHmacSHA256AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8"), + pbkd2AssertionData("PBEWithHmacSHA384AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351"), + pbkd2AssertionData("PBEWithHmacSHA512AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898"), + pbkd2AssertionData("PBEWithHmacSHA1AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d40" + + "b5552da5e645dad4b5911ce0f2f06b"), + pbkd2AssertionData("PBEWithHmacSHA224AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e" + + "7daa2119fee8c64aef7c1f4c1871724e"), + pbkd2AssertionData("PBEWithHmacSHA256AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8" + + "44984e3e9b619488a42b93dd6453f630"), + pbkd2AssertionData("PBEWithHmacSHA384AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351" + + "d7d0ad3a18912086f80d974f2acc2efb"), + pbeWithHmacSHA512AndAES256Data, + pbkd2AssertionData("PBKDF2WithHmacSHA1", pwd, 240, + "PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d40" + + "b5552da5e645dad4b5911ce0f2"), + pbkd2AssertionData("PBKDF2WithHmacSHA224", pwd, 336, + "PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e" + + "7daa2119fee8c64aef7c1f4c1871724e0ea628577e0ab54fa7c6"), + pbkdf2WithHmacSHA256Data, + pbkd2AssertionData("PBKDF2WithHmacSHA384", pwd, 576, + "PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351" + + "d7d0ad3a18912086f80d974f2acc2efba52650d4bf872455820f24c8" + + "46742161da84a1b4c3f197f4347308e8841a8971cf686aef29107396"), + pbkd2AssertionData("PBKDF2WithHmacSHA512", pwd, 768, + "PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898" + + "7e3077595f90b60ab96d4f29203927b00aa1a11e4d19d4f275a7f453" + + "14be500dacc3c1de9f704827b396463ccaa8957344d41bd64d9d09ff" + + "474e776469d326b1ee6ee5a5d854b86d3d7a25084afd6d6f"), + p12PBKDAssertionData("HmacPBESHA512", emptyPwd, 512, "SHA-512", + 128, "90b6e088490c6c5e6b6e81209bd769d27df3868cae79591577a" + + "c35b46e4c6ebcc4b90f4943e3cb165f9d1789d938235f4b35ba74df9" + + "e509fbbb7aa329a432445"), + pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", emptyPwd, 256, + "PBKDF2WithHmacSHA512", "3a5c5fd11e4d381b32e11baa93d7b128" + + "09e016e48e0542c5d3453fc240a0fa76"), + }; + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + + // Test valid cases. + for (Configuration conf : Configuration.values()) { + for (AssertionData data : assertionData) { + testValidWith(sunPKCS11, data, conf); + } + } + + // Test invalid cases. + testInvalidTranslateKey(sunPKCS11); + testInvalidGenerateSecret(sunPKCS11); + testInvalidGetKeySpec(sunPKCS11); + + System.out.println("TEST PASS - OK"); + } + + private static void testValidWith(Provider sunPKCS11, AssertionData data, + Configuration conf) throws Exception { + System.out.println(sep + System.lineSeparator() + data.algo + + " (with " + conf.name() + ")"); + + SecretKeyFactory skf = SecretKeyFactory.getInstance(data.algo, + sunPKCS11); + SecretKey derivedKey = switch (conf) { + case PBEKeySpec -> skf.generateSecret(data.keySpec); + case AnonymousPBEKey -> skf.translateKey(getAnonymousPBEKey( + data.algo, data.keySpec)); + }; + BigInteger derivedKeyValue = i(derivedKey.getEncoded()); + printHex("Derived Key", derivedKeyValue); + + if (!derivedKeyValue.equals(data.expectedKey)) { + printHex("Expected Derived Key", data.expectedKey); + throw new Exception("Expected Derived Key did not match"); + } + + if (skf.translateKey(derivedKey) != derivedKey) { + throw new Exception("SecretKeyFactory::translateKey must return " + + "the same key when a P11PBEKey from the same token is " + + "passed"); + } + + testGetKeySpec(data, skf, derivedKey); + if (sunJCE != null && data.algo.startsWith("PBKDF2")) { + testTranslateP11PBEKeyToSunJCE(data.algo, (PBEKey) derivedKey); + } + } + + private static SecretKey getAnonymousPBEKey(String algorithm, + PBEKeySpec keySpec) { + return new PBEKey() { + public byte[] getSalt() { return keySpec.getSalt(); } + public int getIterationCount() { + return keySpec.getIterationCount(); + } + public String getAlgorithm() { return algorithm; } + public String getFormat() { return "RAW"; } + public char[] getPassword() { return keySpec.getPassword(); } + public byte[] getEncoded() { + return new byte[keySpec.getKeyLength() / 8]; + } + }; + } + + private static void printHex(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + private static void testGetKeySpec(AssertionData data, + SecretKeyFactory skf, SecretKey derivedKey) throws Exception { + System.out.println(sep + System.lineSeparator() + + "SecretKeyFactory::getKeySpec() (for " + data.algo + ")"); + KeySpec skfKeySpec = skf.getKeySpec(derivedKey, PBEKeySpec.class); + if (skfKeySpec instanceof PBEKeySpec skfPBEKeySpec) { + char[] specPassword = skfPBEKeySpec.getPassword(); + byte[] specSalt = skfPBEKeySpec.getSalt(); + int specIterations = skfPBEKeySpec.getIterationCount(); + int specKeyLength = skfPBEKeySpec.getKeyLength(); + System.out.println(" spec key length (bits): " + specKeyLength); + System.out.println(" spec password: " + + String.valueOf(specPassword)); + System.out.println(" spec iteration count: " + specIterations); + printHex(" spec salt", i(specSalt)); + + if (!Arrays.equals(specPassword, data.keySpec.getPassword())) { + throw new Exception("Password differs"); + } + if (!Arrays.equals(specSalt, data.keySpec.getSalt())) { + throw new Exception("Salt differs"); + } + if (specIterations != data.keySpec.getIterationCount()) { + throw new Exception("Iteration count differs"); + } + if (specKeyLength != data.keySpec.getKeyLength()) { + throw new Exception("Key length differs"); + } + } else { + throw new Exception("Invalid key spec type: " + skfKeySpec); + } + + // Test extracting key bytes with a SecretKeySpec. + SecretKeySpec secretKeySpec = (SecretKeySpec) + skf.getKeySpec(derivedKey, SecretKeySpec.class); + if (!Arrays.equals(secretKeySpec.getEncoded(), + derivedKey.getEncoded())) { + throw new Exception("Unable to extract key bytes with a " + + "SecretKeySpec"); + } + } + + private static void testTranslateP11PBEKeyToSunJCE(String algorithm, + PBEKey p11PbeK) throws Exception { + System.out.println(sep + System.lineSeparator() + + "Translate P11PBEKey to SunJCE (for " + algorithm + ")"); + SecretKey jceK = SecretKeyFactory.getInstance(algorithm, sunJCE) + .translateKey(p11PbeK); + BigInteger jceEncoded = i(jceK.getEncoded()); + printHex(" translated to SunJCE", jceEncoded); + if (jceK instanceof PBEKey jcePbeK) { + if (!Arrays.equals(jcePbeK.getPassword(), p11PbeK.getPassword())) { + throw new Exception("Password differs"); + } + if (!Arrays.equals(jcePbeK.getSalt(), p11PbeK.getSalt())) { + throw new Exception("Salt differs"); + } + if (jcePbeK.getIterationCount() != p11PbeK.getIterationCount()) { + throw new Exception("Iteration count differs"); + } + if (!jceEncoded.equals(i(p11PbeK.getEncoded()))) { + throw new Exception("Encoded key differs"); + } + } else { + throw new Exception("Unexpected key type for SunJCE key: " + + jceK.getClass().getName()); + } + } + + @FunctionalInterface + private interface Action { + void run() throws Exception; + } + + private static void assertThrows(Class expectedExc, String expectedMsg, + Action action) throws Exception { + String shtExpected = "Should have thrown '" + + expectedExc.getSimpleName() + ": " + expectedMsg + "'"; + try { + action.run(); + } catch (Exception e) { + if (expectedExc.isAssignableFrom(e.getClass()) && + e.getMessage().equals(expectedMsg)) { + return; + } + e.printStackTrace(); + throw new Exception(shtExpected + ", but threw '" + + e.getClass().getSimpleName() + ": " + e.getMessage() + "'"); + } + throw new Exception(shtExpected + ", but it didn't throw"); + } + + private static void testInvalidTranslateKey(Provider sunPKCS11) + throws Exception { + System.out.println(sep + System.lineSeparator() + + "Invalid SecretKeyFactory::translateKey tests"); + + SecretKeyFactory skf1 = SecretKeyFactory.getInstance( + hmacPBESHA1Data.algo, sunPKCS11); + SecretKeyFactory skf2 = SecretKeyFactory.getInstance("AES", sunPKCS11); + SecretKeyFactory skf3 = SecretKeyFactory.getInstance( + pbkdf2WithHmacSHA256Data.algo, sunPKCS11); + PBEKey p11PbeKey = (PBEKey) skf1.translateKey(getAnonymousPBEKey( + skf1.getAlgorithm(), hmacPBESHA1Data.keySpec)); + Class e = InvalidKeyException.class; + + System.out.println(" * Non-PBEKey key to PBE SecretKeyFactory"); + assertThrows(e, "PBE service requires a PBE key", + () -> skf1.translateKey(new SecretKeySpec( + new byte[10], hmacPBESHA1Data.algo))); + + System.out.println(" * PBEKey key to PBE SecretKeyFactory of a " + + "different algorithm"); + assertThrows(e, "Cannot use a " + hmacPBESHA1Data.algo + " key for a " + + hmacPBESHA224Data.algo + " service", + () -> SecretKeyFactory.getInstance(hmacPBESHA224Data.algo, + sunPKCS11).translateKey(p11PbeKey)); + + System.out.println(" * Non-AES PBEKey key to AES SecretKeyFactory"); + assertThrows(e, "Cannot use a " + hmacPBESHA1Data.algo + " key for a " + + skf2.getAlgorithm() + " service", + () -> skf2.translateKey(p11PbeKey)); + + System.out.println(" * Inconsistent key length between key and " + + "algorithm"); + PBEKeySpec kSpec1 = new PBEKeySpec(pwd, salt, 1, 16); + assertThrows(e, InvalidKeySpecException.class.getName() + ": Key " + + "length is invalid for " + skf1.getAlgorithm() + " (expecting" + + " " + hmacPBESHA1Data.keySpec.getKeyLength() + " but was " + + kSpec1.getKeyLength() + ")", + () -> skf1.translateKey(getAnonymousPBEKey( + skf1.getAlgorithm(), kSpec1))); + + System.out.println(" * Invalid key length in bits"); + PBEKeySpec kSpec2 = new PBEKeySpec(pwd, salt, 1); + assertThrows(e, InvalidKeySpecException.class.getName() + ": Key " + + "length must be multiple of 8 and greater than zero", + () -> skf3.translateKey(getAnonymousPBEKey( + skf3.getAlgorithm(), kSpec2))); + + System.out.println(); + } + + private static void testInvalidGenerateSecret(Provider sunPKCS11) + throws Exception { + System.out.println(sep + System.lineSeparator() + + "Invalid SecretKeyFactory::generateSecret tests"); + + SecretKeyFactory skf1 = SecretKeyFactory.getInstance( + hmacPBESHA1Data.algo, sunPKCS11); + SecretKeyFactory skf2 = SecretKeyFactory.getInstance( + pbeWithHmacSHA512AndAES256Data.algo, sunPKCS11); + SecretKeyFactory skf3 = SecretKeyFactory.getInstance( + "PBKDF2WithHmacSHA512", sunPKCS11); + SecretKeyFactory skf4 = SecretKeyFactory.getInstance("AES", sunPKCS11); + Class e = InvalidKeySpecException.class; + + System.out.println(" * Missing salt and iteration count"); + assertThrows(e, "Salt not found", + () -> skf1.generateSecret(new PBEKeySpec(pwd))); + + System.out.println(" * Inconsistent key length between spec and " + + "algorithm"); + PBEKeySpec kSpec = new PBEKeySpec(pwd, salt, 1, 16); + assertThrows(e, "Key length is invalid for " + skf1.getAlgorithm() + + " (expecting " + hmacPBESHA1Data.keySpec.getKeyLength() + + " but was " + kSpec.getKeyLength() + ")", + () -> skf1.generateSecret(kSpec)); + assertThrows(e, "Key length is invalid for " + skf2.getAlgorithm() + + " (expecting " + pbeWithHmacSHA512AndAES256Data.keySpec + .getKeyLength() + " but was " + kSpec.getKeyLength() + ")", + () -> skf2.generateSecret(kSpec)); + + System.out.println(" * Invalid key length in bits"); + String msg = "Key length must be multiple of 8 and greater than zero"; + assertThrows(e, msg, + () -> skf3.generateSecret(new PBEKeySpec(pwd, salt, 1))); + assertThrows(e, msg, + () -> skf3.generateSecret(new PBEKeySpec(pwd, salt, 1, 3))); + + System.out.println(" * PBEKeySpec to non-PBE SecretKeyFactory"); + assertThrows(e, "Unsupported spec: javax.crypto.spec.PBEKeySpec", + () -> skf4.generateSecret(kSpec)); + + System.out.println(); + } + + private static void testInvalidGetKeySpec(Provider sunPKCS11) + throws Exception { + System.out.println(sep + System.lineSeparator() + + "Invalid SecretKeyFactory::getKeySpec tests"); + + SecretKeyFactory skf1 = SecretKeyFactory.getInstance( + hmacPBESHA1Data.algo, sunPKCS11); + SecretKeyFactory skf2 = SecretKeyFactory.getInstance( + "AES", sunPKCS11); + PBEKey p11PbeKey = (PBEKey) skf1.translateKey(getAnonymousPBEKey( + skf1.getAlgorithm(), hmacPBESHA1Data.keySpec)); + Class e = InvalidKeySpecException.class; + + System.out.println(" * null KeySpec class"); + assertThrows(e, "key and keySpec must not be null", + () -> skf1.getKeySpec(p11PbeKey, null)); + + System.out.println(" * Invalid key type for PBEKeySpec"); + assertThrows(e, "Unsupported spec: " + PBEKeySpec.class.getName(), + () -> skf1.getKeySpec(new SecretKeySpec(new byte[16], + skf1.getAlgorithm()), PBEKeySpec.class)); + + System.out.println(" * Invalid PBE key and PBEKeySpec for " + + skf2.getAlgorithm() + " SecretKeyFactory"); + assertThrows(e, "Unsupported spec: " + PBEKeySpec.class.getName(), + () -> skf2.getKeySpec(p11PbeKey, PBEKeySpec.class)); + + System.out.println(); + } + + public static void main(String[] args) throws Exception { + main(new TestPBKD()); + } +}