From bef6ba0d558abf652742be4798c32d20e3e10492 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 4 Oct 2022 13:32:46 -0300 Subject: [PATCH 01/24] Add missing trace in p11_util.c --- src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c | 1 + 1 file changed, 1 insertion(+) 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 aa76945283d74..565818403c567 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c @@ -422,6 +422,7 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) { 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); free(((CK_PBE_PARAMS_PTR)tmp)->pPassword); free(((CK_PBE_PARAMS_PTR)tmp)->pSalt); From 7efaec5013e66a788e06233825badc1d4b8a0510 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 4 Oct 2022 13:39:28 -0300 Subject: [PATCH 02/24] Rename internal helper method Cipher algorithms have an 'AesData' suffix as part of their name, so Mac ones should also have a 'MacData' suffix, to difference them from SecretKeyFactory-only algorithms (PBKDF2*). --- .../classes/sun/security/pkcs11/P11Util.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 72b64f72c0a8f..02effdf6a9c57 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 @@ -73,8 +73,8 @@ public static void addPbkdf2AesData(String algo, long kdfMech, "AES", keyLen, Operation.ENCRYPTION)); } - public static void addPkcs12KDData(String algo, long kdfMech, - int keyLen) { + public static void addPkcs12KDMacData(String algo, long kdfMech, + int keyLen) { kdfDataMap.put(algo, new KDFData(kdfMech, -1, "Generic", keyLen, Operation.AUTHENTICATION)); } @@ -115,19 +115,19 @@ public static void addPkcs12KDData(String algo, long kdfMech, KDFData.addPbkdf2Data("PBKDF2WithHmacSHA512", CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512); - KDFData.addPkcs12KDData("HmacPBESHA1", + KDFData.addPkcs12KDMacData("HmacPBESHA1", CKM_PBA_SHA1_WITH_SHA1_HMAC, 160); - KDFData.addPkcs12KDData("HmacPBESHA224", + KDFData.addPkcs12KDMacData("HmacPBESHA224", CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, 224); - KDFData.addPkcs12KDData("HmacPBESHA256", + KDFData.addPkcs12KDMacData("HmacPBESHA256", CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, 256); - KDFData.addPkcs12KDData("HmacPBESHA384", + KDFData.addPkcs12KDMacData("HmacPBESHA384", CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, 384); - KDFData.addPkcs12KDData("HmacPBESHA512", + KDFData.addPkcs12KDMacData("HmacPBESHA512", CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); - KDFData.addPkcs12KDData("HmacPBESHA512/224", + KDFData.addPkcs12KDMacData("HmacPBESHA512/224", CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); - KDFData.addPkcs12KDData("HmacPBESHA512/256", + KDFData.addPkcs12KDMacData("HmacPBESHA512/256", CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); } From 7e3ef9b300805c1f3fa42d41c06286236325c035 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Fri, 4 Nov 2022 20:09:10 -0300 Subject: [PATCH 03/24] Implement sun.security.pkcs11.P11Key.P11PBEKey This key implements javax.crypto.interfaces.PBEKey as well as SunJCE's com.sun.crypto.provider.PBKDF2KeyImpl, which also performs derivation. The idea behind this is to improve interoperability with SunJCE, as its SecretKeyFactory will now accept and translate SunPKCS11's P11PBEKey. Also, this keeps the full PBE information in the key and provides a quick way to identify SunPKCS11 PBE keys via instanceof. --- .../classes/sun/security/pkcs11/P11Key.java | 42 +++++++++++++++++++ .../security/pkcs11/P11SecretKeyFactory.java | 20 ++++----- 2 files changed, 52 insertions(+), 10 deletions(-) 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 5696b904979fa..5fc0ba4338d4e 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 @@ -341,6 +341,18 @@ static SecretKey secretKey(Session session, long keyID, String algorithm, attributes); } + static SecretKey pbeKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, + char[] password, byte[] salt, int iterationCount) { + attributes = getAttributes(session, keyID, attributes, 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, + attributes, password, salt, iterationCount); + } + static SecretKey masterSecretKey(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) { attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { @@ -507,6 +519,36 @@ byte[] getEncodedInternal() { } } + private static class P11PBEKey extends P11SecretKey implements PBEKey { + private static final long serialVersionUID = -7828241727014329084L; + private final 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; + this.salt = salt; + this.iterationCount = iterationCount; + } + + @Override + public char[] getPassword() { + return password; + } + + @Override + public byte[] getSalt() { + return salt; + } + + @Override + public int getIterationCount() { + return iterationCount; + } + } + @SuppressWarnings("deprecation") private static class P11TlsMasterSecretKey extends P11SecretKey implements TlsMasterSecret { 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 7ea9b4c5e7f61..e664634335ff7 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 @@ -236,6 +236,7 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) } } else { // PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2) + char[] expPassword = password; if (P11Util.isNSS(token)) { // According to PKCS #11, "password" in CK_PBE_PARAMS has // a CK_UTF8CHAR_PTR type. This suggests that it is encoded @@ -252,22 +253,21 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) // the 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": - // char[] => [ 0x0061, 0x0000 ] - // / \ / \ - // Expansion => [0x0000, 0x0061, 0x0000, 0x0000] - // | | | | - // BMPString => [ 0x00, 0x61, 0x00, 0x00] + // char[] password => [ 0x0061, 0x0000 ] + // / \ / \ + // char[] expPassword => [0x0000, 0x0061, 0x0000, 0x0000] + // | | | | + // byte[] BMPString => [ 0x00, 0x61, 0x00, 0x00] // int inputLength = (password == null) ? 0 : password.length; - char[] expPassword = new char[inputLength * 2 + 2]; + expPassword = new char[inputLength * 2 + 2]; for (int i = 0, j = 0; i < inputLength; i++, j += 2) { expPassword[j] = (char) ((password[i] >>> 8) & 0xFF); expPassword[j + 1] = (char) (password[i] & 0xFF); } - password = expPassword; } ckMech = new CK_MECHANISM(kdfData.kdfMech, - new CK_PBE_PARAMS(password, salt, itCount)); + new CK_PBE_PARAMS(expPassword, salt, itCount)); } long keyType = getKeyType(kdfData.keyAlgo); @@ -290,8 +290,8 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) CK_ATTRIBUTE[] attr = token.getAttributes( O_GENERATE, CKO_SECRET_KEY, keyType, attrs); long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); - return (P11Key)P11Key.secretKey( - session, keyID, kdfData.keyAlgo, keySize, attr); + return (P11Key) P11Key.pbeKey(session, keyID, kdfData.keyAlgo, + keySize, attr, password, salt, itCount); } catch (PKCS11Exception e) { throw new InvalidKeySpecException("Could not create key", e); } finally { From 25d53d49785041d5240ba14d7b667f537d8ecda8 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 4 Oct 2022 13:34:59 -0300 Subject: [PATCH 04/24] P11SKF: preserve the algorithm of PBKDF2* keys This is aligned with SunJCE's SecretKeyFactory, which keeps the whole PBKDF2* algorithm name as the generated SecretKey's algorithm. --- .../share/classes/sun/security/pkcs11/P11Util.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 02effdf6a9c57..47a4ee5145623 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 @@ -64,7 +64,7 @@ public enum Operation {ENCRYPTION, AUTHENTICATION, GENERIC} public static void addPbkdf2Data(String algo, long kdfMech, long prfMech) { kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - "Generic", -1, Operation.GENERIC)); + algo, -1, Operation.GENERIC)); } public static void addPbkdf2AesData(String algo, long kdfMech, From 8ee2c3c399581786dc299d67e1fc06fb536f9f47 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 4 Oct 2022 14:01:08 -0300 Subject: [PATCH 05/24] Introduce itCount and keySize checks By doing the first two checks in Java, we prevent lower level errors (which would look severer to the end user, as shown in OPENJDK-991). The third check is for consistency, we could just go ahead and return a shorter key as SunJCE does (because of the integer floor-division by 8, performed as 'keySize >> 3'), but that would later fail somewhere. --- .../sun/security/pkcs11/P11SecretKeyFactory.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 e664634335ff7..761867b622d95 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 @@ -218,6 +218,18 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) "Key length is invalid for " + algo); } } + if (itCount < 1) { + throw new InvalidKeySpecException( + "Iteration count must be a non-zero positive integer"); + } + if (keySize < 1) { + throw new InvalidKeySpecException( + "Key length must be a non-zero positive integer"); + } + if (keySize % 8 != 0) { + throw new InvalidKeySpecException( + "Key length (in bits) must be a multiple of 8"); + } if (kdfData.kdfMech == CKM_PKCS5_PBKD2) { CK_INFO p11Info = token.p11.getInfo(); From 11358d37c05eeb7ca3ad00c3c80ffb038e97e5d5 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 18 Oct 2022 19:08:06 -0300 Subject: [PATCH 06/24] Remove P11Util.KDFData.Operation.GENERIC GENERIC means both ENCRYPTION (Cipher) and AUTHENTICATION (Mac), but only AUTHENTICATION works where GENERIC is used. Keys derived with SunPKCS11's SecretKeyFactory for PBKDF2* algorithms are of type CKK_GENERIC_SECRET. In NSS, these keys work for signature mechanisms (used by Mac services), but not for symmetric encryption ones (used by Cipher services). For example, CKM_AES_CBC expects CKK_AES, see https://github.com/nss-dev/nss/blob/NSS_3_67_RTM/lib/softoken/pkcs11c.c#L1240-L1243 Since CKK_GENERIC_SECRET is the most intuitive key type, let's keep that one, and prepare the keys only for AUTHENTICATION operations, by just setting CKA_SIGN=true, since CKA_ENCRYPT=true would be useless. I've tested SunJCE's SecretKeyFactory derived PBKDF2* keys, and they don't work for Mac nor Cipher algorithms, so I guess they are only meant to get their bytes with key.getEncoded(), which is also supported by SunPKCS11, leveraging the work of #1. --- .../security/pkcs11/P11SecretKeyFactory.java | 23 +++++++------------ .../classes/sun/security/pkcs11/P11Util.java | 4 ++-- 2 files changed, 10 insertions(+), 17 deletions(-) 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 761867b622d95..9289e58dea358 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 @@ -283,22 +283,15 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) } long keyType = getKeyType(kdfData.keyAlgo); - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[ + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3), + new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), switch (kdfData.op) { - case ENCRYPTION, AUTHENTICATION -> 4; - case GENERIC -> 5; - }]; - 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, keyType); - switch (kdfData.op) { - case ENCRYPTION -> attrs[3] = CK_ATTRIBUTE.ENCRYPT_TRUE; - case AUTHENTICATION -> attrs[3] = CK_ATTRIBUTE.SIGN_TRUE; - case GENERIC -> { - attrs[3] = CK_ATTRIBUTE.ENCRYPT_TRUE; - attrs[4] = CK_ATTRIBUTE.SIGN_TRUE; - } - } + case ENCRYPTION -> CK_ATTRIBUTE.ENCRYPT_TRUE; + case AUTHENTICATION -> CK_ATTRIBUTE.SIGN_TRUE; + }, + }; CK_ATTRIBUTE[] attr = token.getAttributes( O_GENERATE, CKO_SECRET_KEY, keyType, attrs); long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); 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 47a4ee5145623..7241002375c3f 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 @@ -46,7 +46,7 @@ public final class P11Util { // Used by PBE static final class KDFData { - public enum Operation {ENCRYPTION, AUTHENTICATION, GENERIC} + public enum Operation {ENCRYPTION, AUTHENTICATION} public long kdfMech; public long prfMech; public String keyAlgo; @@ -64,7 +64,7 @@ public enum Operation {ENCRYPTION, AUTHENTICATION, GENERIC} public static void addPbkdf2Data(String algo, long kdfMech, long prfMech) { kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - algo, -1, Operation.GENERIC)); + algo, -1, Operation.AUTHENTICATION)); } public static void addPbkdf2AesData(String algo, long kdfMech, From 94006d9f97cdeec9f967ac49aef80bb38cbe886b Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 18 Oct 2022 19:42:44 -0300 Subject: [PATCH 07/24] Replace Operation enum with direct attributes With the last commit changes, we ended up having a bijection: Operation.ENCRYPTION <-> CK_ATTRIBUTE.ENCRYPT_TRUE Operation.AUTHENTICATION <-> CK_ATTRIBUTE.SIGN_TRUE We can simplify the code, as we no longer need an enum to hold that information, which can be stored as a CK_ATTRIBUTE instance. --- .../sun/security/pkcs11/P11SecretKeyFactory.java | 5 +---- .../share/classes/sun/security/pkcs11/P11Util.java | 14 +++++++------- 2 files changed, 8 insertions(+), 11 deletions(-) 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 9289e58dea358..7a69d29b625b7 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 @@ -287,10 +287,7 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3), new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), - switch (kdfData.op) { - case ENCRYPTION -> CK_ATTRIBUTE.ENCRYPT_TRUE; - case AUTHENTICATION -> CK_ATTRIBUTE.SIGN_TRUE; - }, + kdfData.encrypt_or_sign_true, }; CK_ATTRIBUTE[] attr = token.getAttributes( O_GENERATE, CKO_SECRET_KEY, keyType, attrs); 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 7241002375c3f..3fc4d3e3c2ab8 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 @@ -29,6 +29,7 @@ import java.security.*; import java.util.HashMap; import java.util.Map; +import sun.security.pkcs11.wrapper.CK_ATTRIBUTE; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -46,37 +47,36 @@ public final class P11Util { // Used by PBE static final class KDFData { - public enum Operation {ENCRYPTION, AUTHENTICATION} public long kdfMech; public long prfMech; public String keyAlgo; public int keyLen; - public Operation op; + public CK_ATTRIBUTE encrypt_or_sign_true; KDFData(long kdfMech, long prfMech, String keyAlgo, - int keyLen, Operation op) { + int keyLen, CK_ATTRIBUTE encrypt_or_sign_true) { this.kdfMech = kdfMech; this.prfMech = prfMech; this.keyAlgo = keyAlgo; this.keyLen = keyLen; - this.op = op; + this.encrypt_or_sign_true = encrypt_or_sign_true; } public static void addPbkdf2Data(String algo, long kdfMech, long prfMech) { kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - algo, -1, Operation.AUTHENTICATION)); + algo, -1, CK_ATTRIBUTE.SIGN_TRUE)); } public static void addPbkdf2AesData(String algo, long kdfMech, long prfMech, int keyLen) { kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - "AES", keyLen, Operation.ENCRYPTION)); + "AES", keyLen, CK_ATTRIBUTE.ENCRYPT_TRUE)); } public static void addPkcs12KDMacData(String algo, long kdfMech, int keyLen) { kdfDataMap.put(algo, new KDFData(kdfMech, -1, - "Generic", keyLen, Operation.AUTHENTICATION)); + "Generic", keyLen, CK_ATTRIBUTE.SIGN_TRUE)); } } From d5fe023fc352f7ed8f9ddef5ee0a22dfbd5174e8 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 18 Oct 2022 19:44:20 -0300 Subject: [PATCH 08/24] Make P11Util.KDFData instances immutable Add the final qualifier to all the P11Util.KDFData fields, since we don't expect them to change during the execution. --- .../share/classes/sun/security/pkcs11/P11Util.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 3fc4d3e3c2ab8..d3f102e1dce48 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 @@ -47,11 +47,12 @@ public final class P11Util { // Used by PBE static final class KDFData { - public long kdfMech; - public long prfMech; - public String keyAlgo; - public int keyLen; - public CK_ATTRIBUTE encrypt_or_sign_true; + public final long kdfMech; + public final long prfMech; + public final String keyAlgo; + public final int keyLen; + public final CK_ATTRIBUTE encrypt_or_sign_true; + KDFData(long kdfMech, long prfMech, String keyAlgo, int keyLen, CK_ATTRIBUTE encrypt_or_sign_true) { this.kdfMech = kdfMech; From d1638c2056a2b06247a4107b673b9b6ee6c3b21d Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 20 Oct 2022 19:35:05 -0300 Subject: [PATCH 09/24] Improve and align PBAMac and PBECipher tests Tolerate NoSuchAlgorithmException only when the provider is SunJCE (i.e. when computing the expected results). Reorder fields to reduce differences among tests, align comments' wording, make testWith() and compute*() methods static. Transform getCipher() into computeCipherText() for more similarity with PBAMac, and implement computeExpectedCipherText() for the same reason. --- .../sun/security/pkcs11/Cipher/PBECipher.java | 56 +++++++++---------- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 35 +++++------- 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index a184a16973280..02b2c13e58dee 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -57,8 +57,8 @@ public static void main(String[] args) throws Exception { final class PBECipher2 extends PKCS11Test { private static final char[] password = "123456".toCharArray(); private static final byte[] salt = "abcdefgh".getBytes(); - private static final byte[] iv = new byte[16]; private static final int iterations = 1000; + private static final byte[] iv = new byte[16]; private static final String plainText = "This is a know plain text!"; private static final String sep = "========================================================================="; @@ -67,7 +67,7 @@ private static enum Configuration { // Provide salt and iterations through a PBEParameterSpec instance PBEParameterSpec, - // Provide salt and iterations through a AlgorithmParameters instance + // Provide salt and iterations through an AlgorithmParameters instance AlgorithmParameters, // Provide salt and iterations through an anonymous class implementing @@ -125,33 +125,16 @@ public void main(Provider sunPKCS11) throws Exception { System.out.println("TEST PASS - OK"); } - private void testWith(Provider sunPKCS11, String algorithm, + private static void testWith(Provider sunPKCS11, String algorithm, Configuration conf) throws Exception { System.out.println(sep + System.lineSeparator() + algorithm + " (with " + conf.name() + ")"); - Cipher pbeCipher = getCipher(sunPKCS11, algorithm, conf); - BigInteger cipherText = new BigInteger(1, pbeCipher.doFinal( - plainText.getBytes())); + BigInteger cipherText = computeCipherText(sunPKCS11, algorithm, conf); printByteArray("Cipher Text", cipherText); - BigInteger expectedCipherText = null; - if (sunJCE != null) { - Cipher c = getCipher(sunJCE, algorithm, conf); - if (c != null) { - expectedCipherText = new BigInteger(1, c.doFinal( - plainText.getBytes())); - } else { - // Move to assertionData as it's unlikely that any of - // the algorithms are available. - sunJCE = null; - } - } - if (expectedCipherText == null) { - // If SunJCE or the algorithm are not available, assertionData - // is used instead. - expectedCipherText = assertionData.get(algorithm); - } + BigInteger expectedCipherText = + computeExpectedCipherText(algorithm, conf); if (!cipherText.equals(expectedCipherText)) { printByteArray("Expected Cipher Text", expectedCipherText); @@ -159,14 +142,9 @@ private void testWith(Provider sunPKCS11, String algorithm, } } - private Cipher getCipher(Provider p, String algorithm, + private static BigInteger computeCipherText(Provider p, String algorithm, Configuration conf) throws Exception { - Cipher pbeCipher = null; - try { - pbeCipher = Cipher.getInstance(algorithm, p); - } catch (NoSuchAlgorithmException e) { - return null; - } + Cipher pbeCipher = Cipher.getInstance(algorithm, p); switch (conf) { case PBEParameterSpec, AlgorithmParameters -> { SecretKey key = getPasswordOnlyPBEKey(); @@ -189,7 +167,23 @@ private Cipher getCipher(Provider p, String algorithm, pbeCipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); } } - return pbeCipher; + return new BigInteger(1, pbeCipher.doFinal(plainText.getBytes())); + } + + private static BigInteger computeExpectedCipherText( + String algorithm, Configuration conf) throws Exception { + if (sunJCE != null) { + try { + return computeCipherText(sunJCE, algorithm, conf); + } catch (NoSuchAlgorithmException e) { + // Move to assertionData as it's unlikely that any of + // the algorithms are available. + sunJCE = null; + } + } + // If SunJCE or the algorithm are not available, assertionData + // is used instead. + return assertionData.get(algorithm); } private static SecretKey getPasswordOnlyPBEKey() throws Exception { diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java index 6b5662f6b4cae..23d4ec2980876 100644 --- a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -60,14 +60,16 @@ final class PBAMac2 extends PKCS11Test { "========================================================================="; private static enum Configuration { - // Provide salt & iterations through a PBEParameterSpec instance + // Provide salt and iterations through a PBEParameterSpec instance PBEParameterSpec, - // Provide salt & iterations through an anonymous class implementing + // Provide salt and iterations through an anonymous class implementing // the javax.crypto.interfaces.PBEKey interface AnonymousPBEKey, } + private static Provider sunJCE = Security.getProvider("SunJCE"); + // Generated with SunJCE private static final Map assertionData = Map.of( "HmacPBESHA1", new BigInteger("febd26da5d63ce819770a2af1fc2857e" + @@ -84,8 +86,6 @@ private static enum Configuration { "e8373c980b4072136d2e2810f4e7276024a3e9081cc1", 16) ); - private static Provider sunJCE = Security.getProvider("SunJCE"); - public void main(Provider sunPKCS11) throws Exception { System.out.println("SunPKCS11: " + sunPKCS11.getName()); for (Configuration conf : Configuration.values()) { @@ -98,7 +98,7 @@ public void main(Provider sunPKCS11) throws Exception { System.out.println("TEST PASS - OK"); } - private void testWith(Provider sunPKCS11, String algorithm, + private static void testWith(Provider sunPKCS11, String algorithm, Configuration conf) throws Exception { System.out.println(sep + System.lineSeparator() + algorithm + " (with " + conf.name() + ")"); @@ -114,14 +114,9 @@ private void testWith(Provider sunPKCS11, String algorithm, } } - private BigInteger computeMac(Provider p, String algorithm, + private static BigInteger computeMac(Provider p, String algorithm, Configuration conf) throws Exception { - Mac pbaMac; - try { - pbaMac = Mac.getInstance(algorithm, p); - } catch (NoSuchAlgorithmException e) { - return null; - } + Mac pbaMac = Mac.getInstance(algorithm, p); switch (conf) { case PBEParameterSpec -> { SecretKey key = getPasswordOnlyPBEKey(); @@ -135,16 +130,16 @@ private BigInteger computeMac(Provider p, String algorithm, return new BigInteger(1, pbaMac.doFinal(plainText.getBytes())); } - private BigInteger computeExpectedMac(String algorithm, Configuration conf) - throws Exception { + private static BigInteger computeExpectedMac( + String algorithm, Configuration conf) throws Exception { if (sunJCE != null) { - BigInteger macResult = computeMac(sunJCE, algorithm, conf); - if (macResult != null) { - return macResult; + try { + return computeMac(sunJCE, algorithm, conf); + } catch (NoSuchAlgorithmException e) { + // Move to assertionData as it's unlikely that any of + // the algorithms are available. + sunJCE = null; } - // Move to assertionData as it's unlikely that any of - // the algorithms are available. - sunJCE = null; } // If SunJCE or the algorithm are not available, assertionData // is used instead. From 8ce64382871b1a916207513ddee454fa7edf67bb Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 20 Oct 2022 19:38:49 -0300 Subject: [PATCH 10/24] Extend PBAMac and PBECipher tests Implement tests for the new scenarios we want to support: use SunPKCS11's SecretKeyFactory PBE derived keys with SunPKCS11's Mac or Cipher services. --- .../sun/security/pkcs11/Cipher/PBECipher.java | 34 +++++++++++++++---- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 22 ++++++++++-- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index 02b2c13e58dee..dd8ce156511e3 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -58,7 +58,10 @@ final class PBECipher2 extends PKCS11Test { private static final char[] password = "123456".toCharArray(); private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; - private static final byte[] iv = new byte[16]; + private static final IvParameterSpec ivSpec = + new IvParameterSpec(new byte[16]); + private static final PBEParameterSpec pbeSpec = + new PBEParameterSpec(salt, iterations, ivSpec); private static final String plainText = "This is a know plain text!"; private static final String sep = "========================================================================="; @@ -67,6 +70,10 @@ private static enum Configuration { // Provide salt and iterations through a PBEParameterSpec instance PBEParameterSpec, + // Derive the key using SunPKCS11's SecretKeyFactory, providing salt + // & iterations through a PBEParameterSpec, then use the derived key + SunPKCS11SecretKeyFactoryDerivedKey, + // Provide salt and iterations through an AlgorithmParameters instance AlgorithmParameters, @@ -148,20 +155,23 @@ private static BigInteger computeCipherText(Provider p, String algorithm, switch (conf) { case PBEParameterSpec, AlgorithmParameters -> { SecretKey key = getPasswordOnlyPBEKey(); - PBEParameterSpec paramSpec = new PBEParameterSpec( - salt, iterations, new IvParameterSpec(iv)); switch (conf) { case PBEParameterSpec -> { - pbeCipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); + pbeCipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec); } case AlgorithmParameters -> { AlgorithmParameters algoParams = AlgorithmParameters.getInstance("PBES2"); - algoParams.init(paramSpec); + algoParams.init(pbeSpec); pbeCipher.init(Cipher.ENCRYPT_MODE, key, algoParams); } } } + case SunPKCS11SecretKeyFactoryDerivedKey -> { + SecretKey key = getDerivedSecretKey(p, algorithm); + pbeCipher.init(Cipher.ENCRYPT_MODE, key, + p == sunJCE ? pbeSpec : ivSpec); + } case AnonymousPBEKey -> { SecretKey key = getPasswordSaltIterationsPBEKey(); pbeCipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); @@ -187,8 +197,18 @@ private static BigInteger computeExpectedCipherText( } private static SecretKey getPasswordOnlyPBEKey() throws Exception { - PBEKeySpec keySpec = new PBEKeySpec(password); - SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); + return getSecretKey(new PBEKeySpec(password), + SecretKeyFactory.getInstance("PBE")); + } + + private static SecretKey getDerivedSecretKey( + Provider sunPKCS11, String algorithm) throws Exception { + return getSecretKey(new PBEKeySpec(password, salt, iterations), + SecretKeyFactory.getInstance(algorithm, sunPKCS11)); + } + + private static SecretKey getSecretKey( + PBEKeySpec keySpec, SecretKeyFactory skFac) throws Exception { SecretKey skey = skFac.generateSecret(keySpec); keySpec.clearPassword(); return skey; diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java index 23d4ec2980876..6f7263922e908 100644 --- a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -63,6 +63,10 @@ private static enum Configuration { // Provide salt and iterations through a PBEParameterSpec instance PBEParameterSpec, + // Derive the key using SunPKCS11's SecretKeyFactory, providing salt + // & iterations through a PBEParameterSpec, then use the derived key + SunPKCS11SecretKeyFactoryDerivedKey, + // Provide salt and iterations through an anonymous class implementing // the javax.crypto.interfaces.PBEKey interface AnonymousPBEKey, @@ -122,6 +126,10 @@ private static BigInteger computeMac(Provider p, String algorithm, SecretKey key = getPasswordOnlyPBEKey(); pbaMac.init(key, new PBEParameterSpec(salt, iterations)); } + case SunPKCS11SecretKeyFactoryDerivedKey -> { + SecretKey key = getDerivedSecretKey(p, algorithm); + pbaMac.init(key); + } case AnonymousPBEKey -> { SecretKey key = getPasswordSaltIterationsPBEKey(); pbaMac.init(key); @@ -147,8 +155,18 @@ private static BigInteger computeExpectedMac( } private static SecretKey getPasswordOnlyPBEKey() throws Exception { - PBEKeySpec keySpec = new PBEKeySpec(password); - SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); + return getSecretKey(new PBEKeySpec(password), + SecretKeyFactory.getInstance("PBE")); + } + + private static SecretKey getDerivedSecretKey( + Provider sunPKCS11, String algorithm) throws Exception { + return getSecretKey(new PBEKeySpec(password, salt, iterations), + SecretKeyFactory.getInstance(algorithm, sunPKCS11)); + } + + private static SecretKey getSecretKey( + PBEKeySpec keySpec, SecretKeyFactory skFac) throws Exception { SecretKey skey = skFac.generateSecret(keySpec); keySpec.clearPassword(); return skey; From 58da09bb79fae40e0bc61bd50d0d3385a6179684 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 25 Oct 2022 12:40:20 -0300 Subject: [PATCH 11/24] Reorder code Extract some variables, reuse others. --- .../classes/sun/security/pkcs11/P11PBECipher.java | 12 +++++------- .../sun/security/pkcs11/P11SecretKeyFactory.java | 6 ++++-- 2 files changed, 9 insertions(+), 9 deletions(-) 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 index ae4262703e63e..d23aebd5f616f 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -133,17 +133,15 @@ protected void engineInit(int opmode, Key key, throws InvalidKeyException, InvalidAlgorithmParameterException { - PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec(blkSize, keyLen, - opmode, key, params, random); - - Key derivedKey; + PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec( + blkSize, keyLen, opmode, key, params, random); try { - derivedKey = P11SecretKeyFactory.derivePBEKey( - token, pbeSpec, pbeAlg); + key = P11SecretKeyFactory.derivePBEKey(token, pbeSpec, pbeAlg); } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } - cipher.engineInit(opmode, derivedKey, pbes2Helper.getIvSpec(), random); + params = pbes2Helper.getIvSpec(); + cipher.engineInit(opmode, key, params, random); } // see JCE spec 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 7a69d29b625b7..bf84ce3e4e72e 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 @@ -140,13 +140,15 @@ static P11Key convertKey(Token token, Key key, String algo, if (key instanceof SecretKey == false) { throw new InvalidKeyException("Key must be a SecretKey"); } + + final String keyAlgo = key.getAlgorithm(); long algoType; if (algo == null) { - algo = key.getAlgorithm(); + algo = keyAlgo; algoType = getKeyType(algo); } else { algoType = getKeyType(algo); - long keyAlgorithmType = getKeyType(key.getAlgorithm()); + long keyAlgorithmType = getKeyType(keyAlgo); if (algoType != keyAlgorithmType) { if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) { // ignore key algorithm for MACs From f65221e33e38c0ab89f55ac119e69e2058951b91 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 25 Oct 2022 13:21:53 -0300 Subject: [PATCH 12/24] Use proper key types when deriving When passing the CKA_KEY_TYPE attribute to the token, call getPKCS11KeyType(), so we only get a valid PKCS#11 CKK_* constant. This allows us to use the "HMAC" key type for HmacPBE* algorithms, since getPKCS11KeyType("HMAC") returns CKK_GENERIC. For the attributes' template manager, call getKeyType(), which keeps SunPKCS11's 'pseudo' key types (PCKK_* constants, such as PCKK_HMAC). For the Java key, keep the original algorithm of the SecretKeyFactory, to align with SunJCE behaviour (this also keeps a trace of the algorithm used to derive the key). --- .../sun/security/pkcs11/P11SecretKeyFactory.java | 10 +++++----- .../share/classes/sun/security/pkcs11/P11Util.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) 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 bf84ce3e4e72e..f7aab0555f61a 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 @@ -284,17 +284,17 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) new CK_PBE_PARAMS(expPassword, salt, itCount)); } - long keyType = getKeyType(kdfData.keyAlgo); CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3), - new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), + new CK_ATTRIBUTE(CKA_KEY_TYPE, + getPKCS11KeyType(kdfData.keyAlgo)), kdfData.encrypt_or_sign_true, }; - CK_ATTRIBUTE[] attr = token.getAttributes( - O_GENERATE, CKO_SECRET_KEY, keyType, attrs); + CK_ATTRIBUTE[] attr = token.getAttributes(O_GENERATE, + CKO_SECRET_KEY, getKeyType(kdfData.keyAlgo), attrs); long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); - return (P11Key) P11Key.pbeKey(session, keyID, kdfData.keyAlgo, + return (P11Key) P11Key.pbeKey(session, keyID, algo, keySize, attr, password, salt, itCount); } catch (PKCS11Exception e) { throw new InvalidKeySpecException("Could not create key", e); 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 d3f102e1dce48..1535ba5e1332f 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 @@ -77,7 +77,7 @@ public static void addPbkdf2AesData(String algo, long kdfMech, public static void addPkcs12KDMacData(String algo, long kdfMech, int keyLen) { kdfDataMap.put(algo, new KDFData(kdfMech, -1, - "Generic", keyLen, CK_ATTRIBUTE.SIGN_TRUE)); + "HMAC", keyLen, CK_ATTRIBUTE.SIGN_TRUE)); } } From a6b72f05b6f9bb4f4666741fe0aee7e1d08ed097 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 25 Oct 2022 19:02:51 -0300 Subject: [PATCH 13/24] Make P11SecretKeyFactory usable with PBE Perform derivation in P11Mac (for PBE algorithms) and P11PBECipher, only if the key is not a P11Key. When proceeding with P11Key instances (which are already derived), the key is used as is, and P11SecretKeyFactory.convertKey() gets called: in the P11Mac case, from the same P11Mac instance; in the P11PBECipher case, from the underlying P11Cipher instance (*). In P11SecretKeyFactory.convertKey(), handle keys with known PBE algorithms as a special case: return P11Key instances intact and derive javax.crypto.interfaces.PBEKey instances. (*): these keys reach P11SecretKeyFactory.convertKey(), with algo="AES" and keyAlgo="PBEWithHmacAnd". Also, kdfData.keyAlgo="AES". --- .../classes/sun/security/pkcs11/P11Mac.java | 4 +++- .../sun/security/pkcs11/P11PBECipher.java | 23 ++++++++++++------- .../security/pkcs11/P11SecretKeyFactory.java | 13 ++++++++--- 3 files changed, 28 insertions(+), 12 deletions(-) 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 4840a116b34a7..52e0cc55f32a0 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 @@ -205,7 +205,9 @@ protected void engineReset() { // see JCE spec protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - if (algorithm.startsWith("HmacPBE")) { + if (algorithm.startsWith("HmacPBE") && !(key instanceof P11Key)) { + // Derive for compatibility with SunJCE's + // password-only com.sun.crypto.provider.PBEKey PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); reset(true); try { 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 index d23aebd5f616f..39d9307d51b2e 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -38,6 +38,7 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -132,15 +133,21 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - - PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec( - blkSize, keyLen, opmode, key, params, random); - try { - key = P11SecretKeyFactory.derivePBEKey(token, pbeSpec, pbeAlg); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException(e); + if (!(key instanceof P11Key)) { + // Derive for compatibility with SunJCE's + // password-only com.sun.crypto.provider.PBEKey + PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec( + blkSize, keyLen, opmode, key, params, random); + try { + key = P11SecretKeyFactory.derivePBEKey(token, pbeSpec, pbeAlg); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + params = pbes2Helper.getIvSpec(); + } else if (!(params instanceof IvParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: IV expected"); } - params = pbes2Helper.getIvSpec(); cipher.engineInit(opmode, key, params, random); } 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 f7aab0555f61a..bd3c1ff7ce7fd 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 @@ -142,6 +142,16 @@ static P11Key convertKey(Token token, Key key, String algo, } final String keyAlgo = key.getAlgorithm(); + if (key instanceof PBEKey && keyAlgo != null && algo != null) { + P11Util.KDFData kdfData = P11Util.kdfDataMap.get(keyAlgo); + if (kdfData != null && (keyAlgo.equals(algo) || + kdfData.keyAlgo.equals(algo))) { + return key instanceof P11Key ? + (P11Key) key : // already derived + derivePBEKey(token, (PBEKey) key, algo); + } + } + long algoType; if (algo == null) { algo = keyAlgo; @@ -505,9 +515,6 @@ protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) // see JCE spec protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { - if (key instanceof PBEKey) { - return (SecretKey)derivePBEKey(token, (PBEKey)key, algorithm); - } return (SecretKey)convertKey(token, key, algorithm); } From 0bbfa0ba0209f26c51d02cbfba1d7ab9fa2d3fa3 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 25 Oct 2022 22:27:40 -0300 Subject: [PATCH 14/24] Implement TestPBKD translation cases Perform derivation by translating a javax.crypto.interfaces.PBEKey instance providing password, salt and iteration count. PBKDF2* algorithms don't imply a key length in their names, so we can't perform derivation through translation, because the key length must be provided through a PBEKeySpec instance, only accepted in key generation. Please note that the javax.crypto.interfaces.PBEKey instance's getAlgorithm() method must return the exact algorithm, "PBE" (used in PBAMac and PBECipher tests) doesn't work since the key algorithm must be recognized as a SunPKCS11 known PBE algorithm, by having its entry in P11Util.kdfDataMap. --- .../pkcs11/SecretKeyFactory/TestPBKD.java | 139 +++++++++++------- 1 file changed, 88 insertions(+), 51 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 67c3cee597081..495f2d3d135af 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -31,7 +31,9 @@ import java.util.HashMap; import java.util.Map; +import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; +import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.PBEKeySpec; /* @@ -58,6 +60,15 @@ final class TestPBKD2 extends PKCS11Test { private static final String sep = "========================================================================="; + private static enum Configuration { + // Provide salt and iterations through a PBEKeySpec instance + PBEKeySpec, + + // Provide salt and iterations through an anonymous class implementing + // the javax.crypto.interfaces.PBEKey interface + AnonymousPBEKey, + } + private static Provider sunJCE = Security.getProvider("SunJCE"); // Generated with SunJCE @@ -202,73 +213,88 @@ public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) public void main(Provider sunPKCS11) throws Exception { System.out.println("SunPKCS11: " + sunPKCS11.getName()); - testWith(sunPKCS11, "HmacPBESHA1", - new P12PBKDAssertData(20, "SHA-1", 64)); - testWith(sunPKCS11, "HmacPBESHA224", - new P12PBKDAssertData(28, "SHA-224", 64)); - testWith(sunPKCS11, "HmacPBESHA256", - new P12PBKDAssertData(32, "SHA-256", 64)); - testWith(sunPKCS11, "HmacPBESHA384", - new P12PBKDAssertData(48, "SHA-384", 128)); - testWith(sunPKCS11, "HmacPBESHA512", - new P12PBKDAssertData(64, "SHA-512", 128)); - - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA1", 128)); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA224", 128)); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA256", 128)); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA384", 128)); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA512", 128)); - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA1", 256)); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA224", 256)); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA256", 256)); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA384", 256)); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA512", 256)); - - // Use 1,5 * digest size as the testing derived key length (in bits) - testWith(sunPKCS11, "PBKDF2WithHmacSHA1", 240, - new PBKD2AssertData("PBKDF2WithHmacSHA1")); - testWith(sunPKCS11, "PBKDF2WithHmacSHA224", 336, - new PBKD2AssertData("PBKDF2WithHmacSHA224")); - testWith(sunPKCS11, "PBKDF2WithHmacSHA256", 384, - new PBKD2AssertData("PBKDF2WithHmacSHA256")); - testWith(sunPKCS11, "PBKDF2WithHmacSHA384", 576, - new PBKD2AssertData("PBKDF2WithHmacSHA384")); - testWith(sunPKCS11, "PBKDF2WithHmacSHA512", 768, - new PBKD2AssertData("PBKDF2WithHmacSHA512")); + for (Configuration conf : Configuration.values()) { + testWith(sunPKCS11, "HmacPBESHA1", + new P12PBKDAssertData(20, "SHA-1", 64), conf); + testWith(sunPKCS11, "HmacPBESHA224", + new P12PBKDAssertData(28, "SHA-224", 64), conf); + testWith(sunPKCS11, "HmacPBESHA256", + new P12PBKDAssertData(32, "SHA-256", 64), conf); + testWith(sunPKCS11, "HmacPBESHA384", + new P12PBKDAssertData(48, "SHA-384", 128), conf); + testWith(sunPKCS11, "HmacPBESHA512", + new P12PBKDAssertData(64, "SHA-512", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA1", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA224", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA256", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA384", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA512", 128), conf); + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA1", 256), conf); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA224", 256), conf); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA256", 256), conf); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA384", 256), conf); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA512", 256), conf); + + // Use 1,5 * digest size as the testing derived key length (in bits) + testWith(sunPKCS11, "PBKDF2WithHmacSHA1", 240, + new PBKD2AssertData("PBKDF2WithHmacSHA1"), conf); + testWith(sunPKCS11, "PBKDF2WithHmacSHA224", 336, + new PBKD2AssertData("PBKDF2WithHmacSHA224"), conf); + testWith(sunPKCS11, "PBKDF2WithHmacSHA256", 384, + new PBKD2AssertData("PBKDF2WithHmacSHA256"), conf); + testWith(sunPKCS11, "PBKDF2WithHmacSHA384", 576, + new PBKD2AssertData("PBKDF2WithHmacSHA384"), conf); + testWith(sunPKCS11, "PBKDF2WithHmacSHA512", 768, + new PBKD2AssertData("PBKDF2WithHmacSHA512"), conf); + } System.out.println("TEST PASS - OK"); } private static void testWith(Provider sunPKCS11, String algorithm, - AssertData assertData) throws Exception { + AssertData assertData, Configuration conf) throws Exception { + SecretKey key = getPasswordSaltIterationsPBEKey(algorithm); PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations); - testWith(sunPKCS11, algorithm, keySpec, assertData); + testWith(sunPKCS11, algorithm, key, keySpec, assertData, conf); } private static void testWith(Provider sunPKCS11, String algorithm, - int keyLen, AssertData assertData) throws Exception { + int keyLen, AssertData assertData, Configuration conf) + throws Exception { + if (conf == Configuration.AnonymousPBEKey) { + // Skip, we can't specify the key length in this scenario, and + // we would get "Key length must be a non-zero positive integer" + return; + } PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); - testWith(sunPKCS11, algorithm, keySpec, assertData); + testWith(sunPKCS11, algorithm, null, keySpec, assertData, conf); } private static void testWith(Provider sunPKCS11, String algorithm, - PBEKeySpec keySpec, AssertData assertData) throws Exception { - System.out.println(sep + System.lineSeparator() + algorithm); + SecretKey key, PBEKeySpec keySpec, AssertData assertData, + Configuration conf) + throws Exception { + System.out.println(sep + System.lineSeparator() + algorithm + + " (with " + conf.name() + ")"); SecretKeyFactory skFac = SecretKeyFactory.getInstance( algorithm, sunPKCS11); - BigInteger derivedKey = new BigInteger(1, - skFac.generateSecret(keySpec).getEncoded()); + + SecretKey derivedKeyObj = switch (conf) { + case PBEKeySpec -> skFac.generateSecret(keySpec); + case AnonymousPBEKey -> skFac.translateKey(key); + }; + BigInteger derivedKey = new BigInteger(1, derivedKeyObj.getEncoded()); printByteArray("Derived Key", derivedKey); BigInteger expectedDerivedKey = assertData.derive(algorithm, keySpec); @@ -279,6 +305,17 @@ private static void testWith(Provider sunPKCS11, String algorithm, } } + private static SecretKey getPasswordSaltIterationsPBEKey(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; } // unused + }; + } + private static void printByteArray(String title, BigInteger b) { String repr = (b == null) ? "buffer is null" : b.toString(16); System.out.println(title + ": " + repr + System.lineSeparator()); From cfa2649f414a24725f4655f27a195b32d48f3287 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 3 Nov 2022 11:27:00 -0300 Subject: [PATCH 15/24] Rename printByteArray() method This method was originally printing a byte[], but at some point we replaced that with BigInteger, printHex() is more generic. --- test/jdk/sun/security/pkcs11/Cipher/PBECipher.java | 6 +++--- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 6 +++--- test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index dd8ce156511e3..876b47ddb963e 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -138,13 +138,13 @@ private static void testWith(Provider sunPKCS11, String algorithm, + " (with " + conf.name() + ")"); BigInteger cipherText = computeCipherText(sunPKCS11, algorithm, conf); - printByteArray("Cipher Text", cipherText); + printHex("Cipher Text", cipherText); BigInteger expectedCipherText = computeExpectedCipherText(algorithm, conf); if (!cipherText.equals(expectedCipherText)) { - printByteArray("Expected Cipher Text", expectedCipherText); + printHex("Expected Cipher Text", expectedCipherText); throw new Exception("Expected Cipher Text did not match"); } } @@ -230,7 +230,7 @@ public byte[] getEncoded() { }; } - private static void printByteArray(String title, BigInteger b) { + 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()); } diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java index 6f7263922e908..90bc838e80cd7 100644 --- a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -108,12 +108,12 @@ private static void testWith(Provider sunPKCS11, String algorithm, + " (with " + conf.name() + ")"); BigInteger macResult = computeMac(sunPKCS11, algorithm, conf); - printByteArray("HMAC Result", macResult); + printHex("HMAC Result", macResult); BigInteger expectedMacResult = computeExpectedMac(algorithm, conf); if (!macResult.equals(expectedMacResult)) { - printByteArray("Expected HMAC Result", expectedMacResult); + printHex("Expected HMAC Result", expectedMacResult); throw new Exception("Expected HMAC Result did not match"); } } @@ -183,7 +183,7 @@ private static SecretKey getPasswordSaltIterationsPBEKey() { }; } - private static void printByteArray(String title, BigInteger b) { + 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()); } diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 495f2d3d135af..0b2c98f248c86 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -295,12 +295,12 @@ private static void testWith(Provider sunPKCS11, String algorithm, case AnonymousPBEKey -> skFac.translateKey(key); }; BigInteger derivedKey = new BigInteger(1, derivedKeyObj.getEncoded()); - printByteArray("Derived Key", derivedKey); + printHex("Derived Key", derivedKey); BigInteger expectedDerivedKey = assertData.derive(algorithm, keySpec); if (!derivedKey.equals(expectedDerivedKey)) { - printByteArray("Expected Derived Key", expectedDerivedKey); + printHex("Expected Derived Key", expectedDerivedKey); throw new Exception("Expected Derived Key did not match"); } } @@ -316,7 +316,7 @@ private static SecretKey getPasswordSaltIterationsPBEKey(String algorithm) { }; } - private static void printByteArray(String title, BigInteger b) { + 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()); } From c5ea3dbcaab524a0f37a068b70a39609c0695871 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Fri, 4 Nov 2022 20:51:37 -0300 Subject: [PATCH 16/24] Implement SunPKCS11's SKF.getKeySpec() This also improves the javax.crypto.interfaces.PBEKey derivation. If possible, obtain the key length from the encoded key, as SunJCE in the case of PBKDF2* algorithms, see PBKDF2Core.engineGetKeySpec(): https://github.com/openjdk/jdk17u/blob/jdk-17.0.3-ga/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2Core.java#L101-L102 --- .../security/pkcs11/P11SecretKeyFactory.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) 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 bd3c1ff7ce7fd..2a76da3b2b8cd 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 @@ -313,6 +313,22 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) } } + private static PBEKeySpec getPbeKeySpec(PBEKey pbeKey) { + int keyLength = 0; + if ("RAW".equals(pbeKey.getFormat())) { + byte[] encoded = pbeKey.getEncoded(); + if (encoded != null) { + keyLength = encoded.length << 3; + } + } + int ic = pbeKey.getIterationCount(); + char[] pwd = pbeKey.getPassword(); + byte[] salt = pbeKey.getSalt(); + return keyLength == 0 ? + new PBEKeySpec(pwd, salt, ic) : + new PBEKeySpec(pwd, salt, ic, keyLength); + } + static P11Key derivePBEKey(Token token, PBEKey key, String algo) throws InvalidKeyException { token.ensureValid(); @@ -324,8 +340,7 @@ static P11Key derivePBEKey(Token token, PBEKey key, String algo) return p11Key; } try { - p11Key = derivePBEKey(token, new PBEKeySpec(key.getPassword(), - key.getSalt(), key.getIterationCount()), algo); + p11Key = derivePBEKey(token, getPbeKeySpec(key), algo); } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } @@ -507,6 +522,9 @@ protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } + } else if (keySpec.isAssignableFrom(PBEKeySpec.class) && + key instanceof PBEKey pbeKey) { + return getPbeKeySpec(pbeKey); } throw new InvalidKeySpecException ("Unsupported spec: " + keySpec.getName()); From 8b675d825a7c037aa907708882a37fe4183520a0 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Mon, 12 Dec 2022 21:14:40 -0300 Subject: [PATCH 17/24] Review: fix P11PBEKey, rename PBES2Helper Give a new serialVersionUID to P11PBEKey and return clones of salt and password. Rename PBES2Helper to PBES2Params and move the DEFAULT_COUNT and DEFAULT_SALT_LENGTH constants to it (both SunJCE and SunPKCS11 were using the same values). Also reset P11Mac in a single point of engineInit and fix some imports order and whitespace inconsistencies. Co-Authored-By: Martin Balao --- .../com/sun/crypto/provider/PBES2Core.java | 15 ++-- .../classes/sun/security/util/PBEUtil.java | 70 +++++++++---------- .../classes/sun/security/pkcs11/P11Key.java | 9 +-- .../classes/sun/security/pkcs11/P11Mac.java | 3 +- .../sun/security/pkcs11/P11PBECipher.java | 19 ++--- 5 files changed, 52 insertions(+), 64 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java index 07e34e95c05d3..440b72ca6862a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java @@ -44,10 +44,6 @@ * @see javax.crypto.Cipher */ abstract class PBES2Core extends CipherSpi { - - private static final int DEFAULT_SALT_LENGTH = 20; - private static final int DEFAULT_COUNT = 4096; - // the encapsulated cipher private final CipherCore cipher; private final int keyLength; // in bits @@ -55,8 +51,7 @@ abstract class PBES2Core extends CipherSpi { private final PBKDF2Core kdf; private final String pbeAlgo; private final String cipherAlgo; - private final PBEUtil.PBES2Helper pbes2Helper = new PBEUtil.PBES2Helper( - DEFAULT_SALT_LENGTH, DEFAULT_COUNT); + private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params(); /** * Creates an instance of PBE Scheme 2 according to the selected @@ -129,7 +124,7 @@ protected byte[] engineGetIV() { } protected AlgorithmParameters engineGetParameters() { - return pbes2Helper.getAlgorithmParameters( + return pbes2Params.getAlgorithmParameters( blkSize, pbeAlgo, SunJCE.getInstance(), SunJCE.getRandom()); } @@ -150,7 +145,7 @@ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec(blkSize, keyLength, + PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec(blkSize, keyLength, opmode, key, params, random); PBKDF2KeyImpl s; @@ -170,13 +165,13 @@ protected void engineInit(int opmode, Key key, SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo); // initialize the underlying cipher - cipher.init(opmode, cipherKey, pbes2Helper.getIvSpec(), random); + cipher.init(opmode, cipherKey, pbes2Params.getIvSpec(), random); } protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - engineInit(opmode, key, PBEUtil.PBES2Helper.getParameterSpec(params), + engineInit(opmode, key, PBEUtil.PBES2Params.getParameterSpec(params), random); } diff --git a/src/java.base/share/classes/sun/security/util/PBEUtil.java b/src/java.base/share/classes/sun/security/util/PBEUtil.java index dc8bc72fccbb2..73ddb523925c9 100644 --- a/src/java.base/share/classes/sun/security/util/PBEUtil.java +++ b/src/java.base/share/classes/sun/security/util/PBEUtil.java @@ -44,30 +44,26 @@ public final class PBEUtil { // Used by SunJCE and SunPKCS11 - public final static class PBES2Helper { + public final static 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; - private final int defaultSaltLength; - private final int defaultCount; - - public PBES2Helper(int defaultSaltLength, int defaultCount) { - this.defaultSaltLength = defaultSaltLength; - this.defaultCount = defaultCount; - } public IvParameterSpec getIvSpec() { return ivSpec; } - public AlgorithmParameters getAlgorithmParameters( - int blkSize, String pbeAlgo, Provider p, SecureRandom random) { + public AlgorithmParameters getAlgorithmParameters(int blkSize, + String pbeAlgo, Provider p, SecureRandom random) { AlgorithmParameters params = null; if (salt == null) { // generate random salt and use default iteration count - salt = new byte[defaultSaltLength]; + salt = new byte[DEFAULT_SALT_LENGTH]; random.nextBytes(salt); - iCount = defaultCount; + iCount = DEFAULT_ITERATIONS; } if (ivSpec == null) { // generate random IV @@ -93,11 +89,9 @@ public AlgorithmParameters getAlgorithmParameters( return params; } - public PBEKeySpec getPBEKeySpec( - int blkSize, int keyLength, int opmode, Key key, - AlgorithmParameterSpec params, SecureRandom random) - throws InvalidKeyException, InvalidAlgorithmParameterException { - + 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"); } @@ -123,7 +117,7 @@ public PBEKeySpec getPBEKeySpec( iCount = ((javax.crypto.interfaces.PBEKey)key) .getIterationCount(); if (iCount == 0) { - iCount = defaultCount; + iCount = DEFAULT_ITERATIONS; } else if (iCount < 0) { throw new InvalidAlgorithmParameterException( "Iteration count must be a positive number"); @@ -135,9 +129,9 @@ public PBEKeySpec getPBEKeySpec( if (params == null) { if (salt == null) { // generate random salt and use default iteration count - salt = new byte[defaultSaltLength]; + salt = new byte[DEFAULT_SALT_LENGTH]; random.nextBytes(salt); - iCount = defaultCount; + iCount = DEFAULT_ITERATIONS; } if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) { @@ -148,8 +142,8 @@ public PBEKeySpec getPBEKeySpec( } } else { if (!(params instanceof PBEParameterSpec)) { - throw new InvalidAlgorithmParameterException - ("Wrong parameter type: PBE expected"); + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: PBE expected"); } // salt and iteration count from the params take precedence byte[] specSalt = ((PBEParameterSpec) params).getSalt(); @@ -161,7 +155,7 @@ public PBEKeySpec getPBEKeySpec( int specICount = ((PBEParameterSpec) params) .getIterationCount(); if (specICount == 0) { - specICount = defaultCount; + specICount = DEFAULT_ITERATIONS; } else if (specICount < 0) { throw new InvalidAlgorithmParameterException( "Iteration count must be a positive number"); @@ -220,7 +214,8 @@ public static AlgorithmParameterSpec getParameterSpec( } // Used by SunJCE and SunPKCS11 - public static PBEKeySpec getPBAKeySpec(Key key, AlgorithmParameterSpec params) + public static PBEKeySpec getPBAKeySpec(Key key, + AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { char[] passwdChars; byte[] salt = null; @@ -252,27 +247,30 @@ public static PBEKeySpec getPBAKeySpec(Key key, AlgorithmParameterSpec params) // 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"); + throw new InvalidAlgorithmParameterException( + "PBEParameterSpec required for salt " + + "and iteration count"); } } else if (!(params instanceof PBEParameterSpec)) { - throw new InvalidAlgorithmParameterException - ("PBEParameterSpec type required"); + 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"); + 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"); + throw new InvalidAlgorithmParameterException( + "Different iteration count " + + "between key and params"); } } else { iCount = pbeParams.getIterationCount(); @@ -282,12 +280,12 @@ public static PBEKeySpec getPBAKeySpec(Key key, AlgorithmParameterSpec params) // 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"); + throw new InvalidAlgorithmParameterException( + "Salt must be at least 8 bytes long"); } if (iCount <= 0) { - throw new InvalidAlgorithmParameterException - ("IterationCount must be a positive number"); + throw new InvalidAlgorithmParameterException( + "IterationCount must be a positive number"); } return new PBEKeySpec(passwdChars, salt, iCount); } finally { 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 5fc0ba4338d4e..71bf67a097ae5 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 @@ -519,8 +519,9 @@ byte[] getEncodedInternal() { } } - private static class P11PBEKey extends P11SecretKey implements PBEKey { - private static final long serialVersionUID = -7828241727014329084L; + private static final class P11PBEKey extends P11SecretKey + implements PBEKey { + private static final long serialVersionUID = 6847576994253634876L; private final char[] password; private final byte[] salt; private final int iterationCount; @@ -535,12 +536,12 @@ private static class P11PBEKey extends P11SecretKey implements PBEKey { @Override public char[] getPassword() { - return password; + return password.clone(); } @Override public byte[] getSalt() { - return salt; + return salt.clone(); } @Override 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 52e0cc55f32a0..0ff773218b2b9 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 @@ -205,11 +205,11 @@ protected void engineReset() { // see JCE spec protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { + reset(true); if (algorithm.startsWith("HmacPBE") && !(key instanceof P11Key)) { // Derive for compatibility with SunJCE's // password-only com.sun.crypto.provider.PBEKey PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); - reset(true); try { p11Key = P11SecretKeyFactory.derivePBEKey( token, pbeSpec, algorithm); @@ -221,7 +221,6 @@ protected void engineInit(Key key, AlgorithmParameterSpec params) throw new InvalidAlgorithmParameterException ("Parameters not supported"); } - reset(true); p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); } try { 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 index 39d9307d51b2e..83e369c686344 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -26,9 +26,9 @@ package sun.security.pkcs11; import java.security.AlgorithmParameters; -import java.security.Key; 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; @@ -41,23 +41,18 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; -import static sun.security.pkcs11.wrapper.PKCS11Constants.*; 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 static final int DEFAULT_SALT_LENGTH = 20; - private static final int DEFAULT_COUNT = 4096; - private final Token token; private final String pbeAlg; private final P11Cipher cipher; private final int blkSize; private final int keyLen; - private final PBEUtil.PBES2Helper pbes2Helper = new PBEUtil.PBES2Helper( - DEFAULT_SALT_LENGTH, DEFAULT_COUNT); + private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params(); P11PBECipher(Token token, String pbeAlg, long cipherMech) throws PKCS11Exception, NoSuchAlgorithmException { @@ -112,7 +107,7 @@ protected byte[] engineGetIV() { // see JCE spec @Override protected AlgorithmParameters engineGetParameters() { - return pbes2Helper.getAlgorithmParameters( + return pbes2Params.getAlgorithmParameters( blkSize, pbeAlg, null, JCAUtil.getSecureRandom()); } @@ -136,14 +131,14 @@ protected void engineInit(int opmode, Key key, if (!(key instanceof P11Key)) { // Derive for compatibility with SunJCE's // password-only com.sun.crypto.provider.PBEKey - PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec( + PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( blkSize, keyLen, opmode, key, params, random); try { key = P11SecretKeyFactory.derivePBEKey(token, pbeSpec, pbeAlg); } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } - params = pbes2Helper.getIvSpec(); + params = pbes2Params.getIvSpec(); } else if (!(params instanceof IvParameterSpec)) { throw new InvalidAlgorithmParameterException( "Wrong parameter type: IV expected"); @@ -157,7 +152,7 @@ protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - engineInit(opmode, key, PBEUtil.PBES2Helper.getParameterSpec(params), + engineInit(opmode, key, PBEUtil.PBES2Params.getParameterSpec(params), random); } From 6041151b6803f345bd90f791ce183761e3669404 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Mon, 12 Dec 2022 22:00:58 -0300 Subject: [PATCH 18/24] Review: implement AlgorithmParameterSpec check Unify this check for Mac and Cipher services. Allow passing both a consistent PBEParameterSpec (with the derived key's salt and iteration count) or directly an IvParameterSpec to PBE Cipher services, when using an already derived P11PBEKey. Co-Authored-By: Martin Balao --- .../classes/sun/security/util/PBEUtil.java | 22 ++++++++++++++ .../classes/sun/security/pkcs11/P11Mac.java | 29 ++++++++++++------- .../sun/security/pkcs11/P11PBECipher.java | 12 ++++---- .../sun/security/pkcs11/Cipher/PBECipher.java | 3 +- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/sun/security/util/PBEUtil.java b/src/java.base/share/classes/sun/security/util/PBEUtil.java index 73ddb523925c9..73d4be784352c 100644 --- a/src/java.base/share/classes/sun/security/util/PBEUtil.java +++ b/src/java.base/share/classes/sun/security/util/PBEUtil.java @@ -292,4 +292,26 @@ public static PBEKeySpec getPBAKeySpec(Key key, Arrays.fill(passwdChars, '\0'); } } + + public static AlgorithmParameterSpec checkKeyParams(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"); + } + return pbeParams.getParameterSpec(); + } + } else { + throw new InvalidKeyException( + "Cannot use a " + algorithm + " service with a key that " + + "does not implement javax.crypto.interfaces.PBEKey"); + } + return params; + } } 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 0ff773218b2b9..48613a2b7c6e5 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 @@ -64,6 +64,9 @@ final class P11Mac extends MacSpi { // algorithm name private final String algorithm; + // whether the algorithm is a PBE one + private final boolean isPbeAlg; + // mechanism object private final CK_MECHANISM ckMechanism; @@ -87,6 +90,7 @@ final class P11Mac extends MacSpi { super(); this.token = token; this.algorithm = algorithm; + this.isPbeAlg = algorithm.startsWith("HmacPBE"); Long params = null; switch ((int)mechanism) { case (int)CKM_MD5_HMAC: @@ -206,17 +210,22 @@ protected void engineReset() { protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { reset(true); - if (algorithm.startsWith("HmacPBE") && !(key instanceof P11Key)) { - // Derive for compatibility with SunJCE's - // password-only com.sun.crypto.provider.PBEKey - PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); - try { - p11Key = P11SecretKeyFactory.derivePBEKey( - token, pbeSpec, algorithm); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException(e); + if (isPbeAlg) { + if (key instanceof P11Key) { + params = PBEUtil.checkKeyParams(key, params, algorithm); + } else { + // The key is a plain password. Use SunPKCS11's PBE + // key derivation mechanism to obtain a P11Key. + PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); + try { + p11Key = P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, algorithm); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } } - } else { + } + if (!isPbeAlg || key instanceof P11Key) { if (params != null) { throw new InvalidAlgorithmParameterException ("Parameters not supported"); 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 index 83e369c686344..6fd28972281c3 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -38,7 +38,6 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import sun.security.jca.JCAUtil; @@ -128,9 +127,11 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - if (!(key instanceof P11Key)) { - // Derive for compatibility with SunJCE's - // password-only com.sun.crypto.provider.PBEKey + if (key instanceof P11Key) { + params = PBEUtil.checkKeyParams(key, params, pbeAlg); + } else { + // The key is a plain password. Use SunPKCS11's PBE + // key derivation mechanism to obtain a P11Key. PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( blkSize, keyLen, opmode, key, params, random); try { @@ -139,9 +140,6 @@ protected void engineInit(int opmode, Key key, throw new InvalidKeyException(e); } params = pbes2Params.getIvSpec(); - } else if (!(params instanceof IvParameterSpec)) { - throw new InvalidAlgorithmParameterException( - "Wrong parameter type: IV expected"); } cipher.engineInit(opmode, key, params, random); } diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index 876b47ddb963e..bd112cbc3aed9 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -169,8 +169,7 @@ private static BigInteger computeCipherText(Provider p, String algorithm, } case SunPKCS11SecretKeyFactoryDerivedKey -> { SecretKey key = getDerivedSecretKey(p, algorithm); - pbeCipher.init(Cipher.ENCRYPT_MODE, key, - p == sunJCE ? pbeSpec : ivSpec); + pbeCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); } case AnonymousPBEKey -> { SecretKey key = getPasswordSaltIterationsPBEKey(); From 6b5e4a7f4c18b982e92d7f4cb1659395167698dd Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 13 Dec 2022 09:29:40 -0300 Subject: [PATCH 19/24] Review: integrate KDFData into P11SecretKeyFactory Move the P11Util.KDFData map to P11SecretKeyFactory, renaming KDFData to KeyInfo and merging the map with the keyTypes map. KeyInfo has a PBEKeyInfo specialization when the algorithm it represents is a PBE one. Implement all the service and key algorithms consistency checks inside P11SecretKeyFactory.KeyInfo.checkUse. Store the PBEKeyInfo entry in advance for the service's algorithm in P11Mac, P11SecretKeyFactory and P11PBECipher (when the algorithm is a PBE one, which for P11PBECipher this means always). Keep a single version of P11SecretKeyFactory.derivePBEKey, which now receives a PBEKeyInfo instance, instead of the algorithm as a string. Add some checks in P11SecretKeyFactory.convertKey and remove unnecessary ones. Partially undo 'Use proper key types when deriving' changes and pass the native key type to Token.getAttributes, as P11SecretKeyFactory.createKey does the same. We've also checked that the template system ignores the passed SunPKCS11 pseudo key type when getting attributes. Adjust all the remaining code for these changes and make some cosmetic changes, such as using pattern matching for instanceof (JEP 305) or reordering some bits in P11SecretKeyFactory.engineGenerateSecret and P11SecretKeyFactory.engineGetKeySpec. Co-Authored-By: Martin Balao --- .../classes/sun/security/pkcs11/P11Mac.java | 8 +- .../sun/security/pkcs11/P11PBECipher.java | 11 +- .../security/pkcs11/P11SecretKeyFactory.java | 458 +++++++++++------- .../classes/sun/security/pkcs11/P11Util.java | 92 ---- 4 files changed, 301 insertions(+), 268 deletions(-) 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 48613a2b7c6e5..d225434ff8064 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 @@ -64,6 +64,9 @@ final class P11Mac extends MacSpi { // algorithm name private final String algorithm; + // PBEKeyInfo if algorithm is PBE + private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi; + // whether the algorithm is a PBE one private final boolean isPbeAlg; @@ -90,7 +93,8 @@ final class P11Mac extends MacSpi { super(); this.token = token; this.algorithm = algorithm; - this.isPbeAlg = algorithm.startsWith("HmacPBE"); + this.svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(algorithm); + this.isPbeAlg = this.svcPbeKi != null; Long params = null; switch ((int)mechanism) { case (int)CKM_MD5_HMAC: @@ -219,7 +223,7 @@ protected void engineInit(Key key, AlgorithmParameterSpec params) PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); try { p11Key = P11SecretKeyFactory.derivePBEKey( - token, pbeSpec, algorithm); + token, pbeSpec, svcPbeKi); } catch (InvalidKeySpecException e) { throw new InvalidKeyException(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 index 6fd28972281c3..be94d328978b5 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -50,7 +50,7 @@ final class P11PBECipher extends CipherSpi { private final String pbeAlg; private final P11Cipher cipher; private final int blkSize; - private final int keyLen; + private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi; private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params(); P11PBECipher(Token token, String pbeAlg, long cipherMech) @@ -65,9 +65,9 @@ final class P11PBECipher extends CipherSpi { } cipher = new P11Cipher(token, cipherTrans, cipherMech); blkSize = cipher.engineGetBlockSize(); - assert P11Util.kdfDataMap.get(pbeAlg) != null; - keyLen = P11Util.kdfDataMap.get(pbeAlg).keyLen; this.pbeAlg = pbeAlg; + svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(pbeAlg); + assert svcPbeKi != null : "algorithm must be in KeyInfo map"; this.token = token; } @@ -133,9 +133,10 @@ protected void engineInit(int opmode, Key key, // The key is a plain password. Use SunPKCS11's PBE // key derivation mechanism to obtain a P11Key. PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( - blkSize, keyLen, opmode, key, params, random); + blkSize, svcPbeKi.keyLen, opmode, key, params, random); try { - key = P11SecretKeyFactory.derivePBEKey(token, pbeSpec, pbeAlg); + key = P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, svcPbeKi); } catch (InvalidKeySpecException e) { throw new InvalidKeyException(e); } 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 2a76da3b2b8cd..9db4f19038090 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 @@ -53,76 +53,206 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { // algorithm name private final String algorithm; + // PBEKeyInfo if algorithm is PBE + 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 { - 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); + static PBEKeyInfo getPBEKeyInfo(String algo) { + if (getKeyInfo(algo) instanceof PBEKeyInfo pbeKi) { + return pbeKi; + } + return null; + } - // 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); + 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; - addKeyType("TlsPremasterSecret", PCKK_TLSPREMASTER); - addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER); - addKeyType("TlsMasterSecret", PCKK_TLSMASTER); - addKeyType("Generic", CKK_GENERIC_SECRET); + KeyInfo(String algo, long keyType) { + this.algo = algo; + this.keyType = keyType; + } + + 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; + } + return ki.keyType == si.keyType; + } } - private static void addKeyType(String name, long id) { - Long l = Long.valueOf(id); - keyTypes.put(name, l); - keyTypes.put(name.toUpperCase(Locale.ENGLISH), l); + 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 { + 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. + putKeyInfo(new KeyInfo("RC2", CKK_RC2)); + putKeyInfo(new KeyInfo("IDEA", CKK_IDEA)); + + 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)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA512/224", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512)); + putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA512/256", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512)); } - // returns the PKCS11 key type of the specified algorithm - // no pseudo KeyTypes static long getPKCS11KeyType(String algorithm) { long kt = getKeyType(algorithm); if (kt == -1 || kt > PCKK_ANY) { + // Replace pseudo key type. kt = CKK_GENERIC_SECRET; } 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); } /** @@ -130,42 +260,33 @@ static P11Key convertKey(Token token, Key key, String algo) * P11Key of provider. * Used in P11KeyStore.storeSkey. */ - static P11Key convertKey(Token token, Key key, String algo, + 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 == false) { + if (!(key instanceof SecretKey)) { throw new InvalidKeyException("Key must be a SecretKey"); } - final String keyAlgo = key.getAlgorithm(); - if (key instanceof PBEKey && keyAlgo != null && algo != null) { - P11Util.KDFData kdfData = P11Util.kdfDataMap.get(keyAlgo); - if (kdfData != null && (keyAlgo.equals(algo) || - kdfData.keyAlgo.equals(algo))) { - return key instanceof P11Key ? - (P11Key) key : // already derived - derivePBEKey(token, (PBEKey) key, algo); - } + if (keyAlgo == null) { + throw new InvalidKeyException("Key must specify its algorithm"); } - - long algoType; - if (algo == null) { - algo = keyAlgo; - algoType = getKeyType(algo); - } else { - algoType = getKeyType(algo); - long keyAlgorithmType = getKeyType(keyAlgo); - if (algoType != keyAlgorithmType) { - if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) { - // ignore key algorithm for MACs - } else { - throw new InvalidKeyException - ("Key algorithm must be " + algo); - } + 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) { @@ -198,17 +319,34 @@ static P11Key convertKey(Token token, Key key, String algo, if (p11Key != null) { return p11Key; } - if ("RAW".equalsIgnoreCase(key.getFormat()) == false) { - throw new InvalidKeyException("Encoded format must be RAW"); + if (key instanceof PBEKey pbeKey) { + ki = ki == null ? getKeyInfo(keyAlgo) : ki; + if (ki instanceof PBEKeyInfo pbeKi) { + try { + p11Key = derivePBEKey(token, getPbeKeySpec(pbeKey), pbeKi); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } 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(); + p11Key = createKey(token, encoded, svcAlgo, si.keyType, extraAttrs); } - byte[] encoded = key.getEncoded(); - p11Key = createKey(token, encoded, algo, algoType, extraAttrs); token.secretCache.put(key, p11Key); return p11Key; } - static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) - throws InvalidKeySpecException { + static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, + PBEKeyInfo pbeKi) throws InvalidKeySpecException { token.ensureValid(); if (keySpec == null) { throw new InvalidKeySpecException("PBEKeySpec must not be null"); @@ -216,95 +354,97 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) Session session = null; try { session = token.getObjSession(); - P11Util.KDFData kdfData = P11Util.kdfDataMap.get(algo); CK_MECHANISM ckMech; char[] password = keySpec.getPassword(); byte[] salt = keySpec.getSalt(); int itCount = keySpec.getIterationCount(); int keySize = keySpec.getKeyLength(); - if (kdfData.keyLen != -1) { - if (keySize == 0) { - keySize = kdfData.keyLen; - } else if (keySize != kdfData.keyLen) { - throw new InvalidKeySpecException( - "Key length is invalid for " + algo); - } + 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"); + throw new InvalidKeySpecException("Iteration count must be " + + "a non-zero positive integer"); } - if (keySize < 1) { - throw new InvalidKeySpecException( - "Key length 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 % 8 != 0) { - throw new InvalidKeySpecException( - "Key length (in bits) must be a multiple of 8"); + if (keySize < 1 || keySize % 8 != 0) { + throw new InvalidKeySpecException("Key length must be " + + "multiple of 8 and greater than zero"); } - if (kdfData.kdfMech == CKM_PKCS5_PBKD2) { + if (pbeKi.kdfMech == CKM_PKCS5_PBKD2) { CK_INFO p11Info = token.p11.getInfo(); CK_VERSION p11Ver = (p11Info != null ? p11Info.cryptokiVersion : null); if (P11Util.isNSS(token) || p11Ver != null && (p11Ver.major < 2 || p11Ver.major == 2 && p11Ver.minor < 40)) { - // NSS keeps using the old structure beyond PKCS #11 v2.40 - ckMech = new CK_MECHANISM(kdfData.kdfMech, + // NSS keeps using the old structure beyond PKCS #11 v2.40. + ckMech = new CK_MECHANISM(pbeKi.kdfMech, new CK_PKCS5_PBKD2_PARAMS(password, salt, - itCount, kdfData.prfMech)); + itCount, pbeKi.prfMech)); } else { - ckMech = new CK_MECHANISM(kdfData.kdfMech, + ckMech = new CK_MECHANISM(pbeKi.kdfMech, new CK_PKCS5_PBKD2_PARAMS2(password, salt, - itCount, kdfData.prfMech)); + itCount, pbeKi.prfMech)); } } else { - // PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2) + // PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2). char[] expPassword = password; if (P11Util.isNSS(token)) { - // According to PKCS #11, "password" in CK_PBE_PARAMS has - // a CK_UTF8CHAR_PTR type. This suggests that it is encoded - // in UTF-8. However, NSS expects the password to be encoded - // as BMPString with a NULL terminator when C_GenerateKey - // is called for a PKCS #12 "General Method" derivation - // (see RFC 7292, Appendix B.1). - // - // The char size in Java is 2 bytes. When a char is - // converted to a CK_UTF8CHAR, the high-order byte is - // discarded (see jCharArrayToCKUTF8CharArray in - // p11_util.c). In order to have a BMPString passed to - // C_GenerateKey, we need to account for that and expand: - // the 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": - // char[] password => [ 0x0061, 0x0000 ] - // / \ / \ - // char[] expPassword => [0x0000, 0x0061, 0x0000, 0x0000] - // | | | | - // byte[] BMPString => [ 0x00, 0x61, 0x00, 0x00] - // - int inputLength = (password == null) ? 0 : password.length; - expPassword = new char[inputLength * 2 + 2]; - for (int i = 0, j = 0; i < inputLength; i++, j += 2) { + /* According to PKCS #11, "password" in CK_PBE_PARAMS has + * a CK_UTF8CHAR_PTR type. This suggests that it is encoded + * in UTF-8. However, NSS expects the password to be encoded + * as BMPString with a NULL terminator when C_GenerateKey + * is called for a PKCS #12 "General Method" derivation + * (see RFC 7292, Appendix B.1). + * + * The char size in Java is 2 bytes. When a char is + * converted to a CK_UTF8CHAR, the high-order byte is + * discarded (see jCharArrayToCKUTF8CharArray in + * p11_util.c). In order to have a BMPString passed to + * C_GenerateKey, we need to account for that and expand: + * the 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": + * char[] password => [ 0x0061, 0x0000 ] + * / \ / \ + * char[] expPassword => [0x0000, 0x0061, 0x0000, 0x0000] + * | | | | + * byte[] BMPString => [ 0x00, 0x61, 0x00, 0x00] + */ + expPassword = new char[password.length * 2 + 2]; + for (int i = 0, j = 0; i < password.length; i++, j += 2) { expPassword[j] = (char) ((password[i] >>> 8) & 0xFF); expPassword[j + 1] = (char) (password[i] & 0xFF); } } - ckMech = new CK_MECHANISM(kdfData.kdfMech, + ckMech = new CK_MECHANISM(pbeKi.kdfMech, new CK_PBE_PARAMS(expPassword, salt, itCount)); } - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), - new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3), - new CK_ATTRIBUTE(CKA_KEY_TYPE, - getPKCS11KeyType(kdfData.keyAlgo)), - kdfData.encrypt_or_sign_true, - }; - CK_ATTRIBUTE[] attr = token.getAttributes(O_GENERATE, - CKO_SECRET_KEY, getKeyType(kdfData.keyAlgo), attrs); + 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) P11Key.pbeKey(session, keyID, algo, + return (P11Key) P11Key.pbeKey(session, keyID, pbeKi.algo, keySize, attr, password, salt, itCount); } catch (PKCS11Exception e) { throw new InvalidKeySpecException("Could not create key", e); @@ -315,7 +455,7 @@ static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) private static PBEKeySpec getPbeKeySpec(PBEKey pbeKey) { int keyLength = 0; - if ("RAW".equals(pbeKey.getFormat())) { + if ("RAW".equalsIgnoreCase(pbeKey.getFormat())) { byte[] encoded = pbeKey.getEncoded(); if (encoded != null) { keyLength = encoded.length << 3; @@ -329,25 +469,6 @@ private static PBEKeySpec getPbeKeySpec(PBEKey pbeKey) { new PBEKeySpec(pwd, salt, ic, keyLength); } - static P11Key derivePBEKey(Token token, PBEKey key, String algo) - throws InvalidKeyException { - token.ensureValid(); - if (key == null) { - throw new InvalidKeyException("PBEKey must not be null"); - } - P11Key p11Key = token.secretCache.get(key); - if (p11Key != null) { - return p11Key; - } - try { - p11Key = derivePBEKey(token, getPbeKeySpec(key), algo); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException(e); - } - token.secretCache.put(key, p11Key); - return p11Key; - } - static void fixDESParity(byte[] key, int offset) { for (int i = 0; i < 8; i++) { int b = key[offset] & 0xfe; @@ -455,28 +576,28 @@ protected SecretKey engineGenerateSecret(KeySpec keySpec) 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 (SecretKey) derivePBEKey(token, pbeKeySpec, svcPbeKi); } else if (algorithm.equalsIgnoreCase("DES")) { - if (keySpec instanceof DESKeySpec) { - byte[] keyBytes = ((DESKeySpec)keySpec).getKey(); + if (keySpec instanceof DESKeySpec desKeySpec) { + byte[] keyBytes = desKeySpec.getKey(); keySpec = new SecretKeySpec(keyBytes, "DES"); return engineGenerateSecret(keySpec); } } else if (algorithm.equalsIgnoreCase("DESede")) { - if (keySpec instanceof DESedeKeySpec) { - byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey(); + if (keySpec instanceof DESedeKeySpec desEdeKeySpec) { + byte[] keyBytes = desEdeKeySpec.getKey(); keySpec = new SecretKeySpec(keyBytes, "DESede"); return engineGenerateSecret(keySpec); } - } else if (keySpec instanceof PBEKeySpec) { - return (SecretKey)derivePBEKey(token, - (PBEKeySpec)keySpec, algorithm); } throw new InvalidKeySpecException ("Unsupported spec: " + keySpec.getClass().getName()); @@ -485,9 +606,8 @@ protected SecretKey engineGenerateSecret(KeySpec keySpec) private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException { try { key = engineTranslateKey(key); - if ("RAW".equalsIgnoreCase(key.getFormat()) == false) { - throw new InvalidKeySpecException - ("Could not obtain key bytes"); + if (!"RAW".equalsIgnoreCase(key.getFormat())) { + throw new InvalidKeySpecException("Could not obtain key bytes"); } byte[] k = key.getEncoded(); return k; @@ -506,6 +626,9 @@ protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) } 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)) { @@ -522,9 +645,6 @@ protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } - } else if (keySpec.isAssignableFrom(PBEKeySpec.class) && - key instanceof PBEKey pbeKey) { - return getPbeKeySpec(pbeKey); } throw new InvalidKeySpecException ("Unsupported spec: " + keySpec.getName()); 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 1535ba5e1332f..cabee4493464f 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 @@ -27,11 +27,6 @@ import java.math.BigInteger; import java.security.*; -import java.util.HashMap; -import java.util.Map; -import sun.security.pkcs11.wrapper.CK_ATTRIBUTE; - -import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * Collection of static utility methods. @@ -45,93 +40,6 @@ public final class P11Util { private static volatile Provider sun, sunRsaSign, sunJce; - // Used by PBE - static final class KDFData { - public final long kdfMech; - public final long prfMech; - public final String keyAlgo; - public final int keyLen; - public final CK_ATTRIBUTE encrypt_or_sign_true; - - KDFData(long kdfMech, long prfMech, String keyAlgo, - int keyLen, CK_ATTRIBUTE encrypt_or_sign_true) { - this.kdfMech = kdfMech; - this.prfMech = prfMech; - this.keyAlgo = keyAlgo; - this.keyLen = keyLen; - this.encrypt_or_sign_true = encrypt_or_sign_true; - } - - public static void addPbkdf2Data(String algo, long kdfMech, - long prfMech) { - kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - algo, -1, CK_ATTRIBUTE.SIGN_TRUE)); - } - - public static void addPbkdf2AesData(String algo, long kdfMech, - long prfMech, int keyLen) { - kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, - "AES", keyLen, CK_ATTRIBUTE.ENCRYPT_TRUE)); - } - - public static void addPkcs12KDMacData(String algo, long kdfMech, - int keyLen) { - kdfDataMap.put(algo, new KDFData(kdfMech, -1, - "HMAC", keyLen, CK_ATTRIBUTE.SIGN_TRUE)); - } - } - - static final Map kdfDataMap = new HashMap<>(); - - static { - KDFData.addPbkdf2AesData("PBEWithHmacSHA1AndAES_128", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1, 128); - KDFData.addPbkdf2AesData("PBEWithHmacSHA224AndAES_128", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224, 128); - KDFData.addPbkdf2AesData("PBEWithHmacSHA256AndAES_128", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256, 128); - KDFData.addPbkdf2AesData("PBEWithHmacSHA384AndAES_128", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384, 128); - KDFData.addPbkdf2AesData("PBEWithHmacSHA512AndAES_128", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512, 128); - KDFData.addPbkdf2AesData("PBEWithHmacSHA1AndAES_256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1, 256); - KDFData.addPbkdf2AesData("PBEWithHmacSHA224AndAES_256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224, 256); - KDFData.addPbkdf2AesData("PBEWithHmacSHA256AndAES_256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256, 256); - KDFData.addPbkdf2AesData("PBEWithHmacSHA384AndAES_256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384, 256); - KDFData.addPbkdf2AesData("PBEWithHmacSHA512AndAES_256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512, 256); - - KDFData.addPbkdf2Data("PBKDF2WithHmacSHA1", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1); - KDFData.addPbkdf2Data("PBKDF2WithHmacSHA224", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224); - KDFData.addPbkdf2Data("PBKDF2WithHmacSHA256", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256); - KDFData.addPbkdf2Data("PBKDF2WithHmacSHA384", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384); - KDFData.addPbkdf2Data("PBKDF2WithHmacSHA512", - CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512); - - KDFData.addPkcs12KDMacData("HmacPBESHA1", - CKM_PBA_SHA1_WITH_SHA1_HMAC, 160); - KDFData.addPkcs12KDMacData("HmacPBESHA224", - CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, 224); - KDFData.addPkcs12KDMacData("HmacPBESHA256", - CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, 256); - KDFData.addPkcs12KDMacData("HmacPBESHA384", - CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, 384); - KDFData.addPkcs12KDMacData("HmacPBESHA512", - CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); - KDFData.addPkcs12KDMacData("HmacPBESHA512/224", - CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); - KDFData.addPkcs12KDMacData("HmacPBESHA512/256", - CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); - } - private P11Util() { // empty } From 2cda36af006e028e187edd40f4125df447efedfc Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 13 Dec 2022 09:59:54 -0300 Subject: [PATCH 20/24] Review: minor tests adjustments Reorder code, rename variables, methods and constants, slightly change exception handling, inline some small methods. Co-Authored-By: Martin Balao --- .../sun/security/pkcs11/Cipher/PBECipher.java | 83 +++++++++---------- .../pkcs11/KeyStore/ImportKeyToP12.java | 7 +- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 79 +++++++++--------- .../pkcs11/SecretKeyFactory/TestPBKD.java | 65 ++++++++------- 4 files changed, 115 insertions(+), 119 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index bd112cbc3aed9..7393d090ccca8 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -24,7 +24,7 @@ import java.math.BigInteger; import java.security.AlgorithmParameters; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; @@ -58,27 +58,27 @@ final class PBECipher2 extends PKCS11Test { private static final char[] password = "123456".toCharArray(); private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; - private static final IvParameterSpec ivSpec = - new IvParameterSpec(new byte[16]); - private static final PBEParameterSpec pbeSpec = - new PBEParameterSpec(salt, iterations, ivSpec); + 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 know plain text!"; - private static final String sep = - "========================================================================="; + private static final String sep = "======================================" + + "==================================="; - private static enum Configuration { - // Provide salt and iterations through a PBEParameterSpec instance + private enum Configuration { + // Pass salt and iterations to a Cipher through a PBEParameterSpec. PBEParameterSpec, - // Derive the key using SunPKCS11's SecretKeyFactory, providing salt - // & iterations through a PBEParameterSpec, then use the derived key - SunPKCS11SecretKeyFactoryDerivedKey, + // Derive a key using SunPKCS11's SecretKeyFactory (wrapping password, + // salt and iterations in a PBEKeySpec), and pass it to a Cipher. + SecretKeyFactoryDerivedKey, - // Provide salt and iterations through an AlgorithmParameters instance + // Pass salt and iterations to a Cipher through an AlgorithmParameters. AlgorithmParameters, - // Provide salt and iterations through an anonymous class implementing - // the javax.crypto.interfaces.PBEKey interface + // Pass password, salt and iterations and iterations to + // a Cipher through an anonymous class implementing the + // javax.crypto.interfaces.PBEKey interface. AnonymousPBEKey, } @@ -110,9 +110,7 @@ private static enum Configuration { private static final class NoRandom extends SecureRandom { @Override - public void nextBytes(byte[] bytes) { - return; - } + public void nextBytes(byte[] bytes) {} } public void main(Provider sunPKCS11) throws Exception { @@ -150,41 +148,42 @@ private static void testWith(Provider sunPKCS11, String algorithm, } private static BigInteger computeCipherText(Provider p, String algorithm, - Configuration conf) throws Exception { - Cipher pbeCipher = Cipher.getInstance(algorithm, p); + Configuration conf) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance(algorithm, p); switch (conf) { case PBEParameterSpec, AlgorithmParameters -> { SecretKey key = getPasswordOnlyPBEKey(); switch (conf) { case PBEParameterSpec -> { - pbeCipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec); + cipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec); } case AlgorithmParameters -> { AlgorithmParameters algoParams = AlgorithmParameters.getInstance("PBES2"); algoParams.init(pbeSpec); - pbeCipher.init(Cipher.ENCRYPT_MODE, key, algoParams); + cipher.init(Cipher.ENCRYPT_MODE, key, algoParams); } } } - case SunPKCS11SecretKeyFactoryDerivedKey -> { + case SecretKeyFactoryDerivedKey -> { SecretKey key = getDerivedSecretKey(p, algorithm); - pbeCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); + cipher.init(Cipher.ENCRYPT_MODE, key, + pbeSpec.getParameterSpec()); } case AnonymousPBEKey -> { - SecretKey key = getPasswordSaltIterationsPBEKey(); - pbeCipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); + SecretKey key = getAnonymousPBEKey(); + cipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); } } - return new BigInteger(1, pbeCipher.doFinal(plainText.getBytes())); + return new BigInteger(1, cipher.doFinal(plainText.getBytes())); } - private static BigInteger computeExpectedCipherText( - String algorithm, Configuration conf) throws Exception { + private static BigInteger computeExpectedCipherText(String algorithm, + Configuration conf) { if (sunJCE != null) { try { return computeCipherText(sunJCE, algorithm, conf); - } catch (NoSuchAlgorithmException e) { + } catch (GeneralSecurityException e) { // Move to assertionData as it's unlikely that any of // the algorithms are available. sunJCE = null; @@ -195,25 +194,19 @@ private static BigInteger computeExpectedCipherText( return assertionData.get(algorithm); } - private static SecretKey getPasswordOnlyPBEKey() throws Exception { - return getSecretKey(new PBEKeySpec(password), - SecretKeyFactory.getInstance("PBE")); - } - - private static SecretKey getDerivedSecretKey( - Provider sunPKCS11, String algorithm) throws Exception { - return getSecretKey(new PBEKeySpec(password, salt, iterations), - SecretKeyFactory.getInstance(algorithm, sunPKCS11)); + private static SecretKey getPasswordOnlyPBEKey() + throws GeneralSecurityException { + return SecretKeyFactory.getInstance("PBE") + .generateSecret(new PBEKeySpec(password)); } - private static SecretKey getSecretKey( - PBEKeySpec keySpec, SecretKeyFactory skFac) throws Exception { - SecretKey skey = skFac.generateSecret(keySpec); - keySpec.clearPassword(); - return skey; + private static SecretKey getDerivedSecretKey(Provider sunPKCS11, + String algorithm) throws GeneralSecurityException { + return SecretKeyFactory.getInstance(algorithm, sunPKCS11) + .generateSecret(new PBEKeySpec(password, salt, iterations)); } - private static SecretKey getPasswordSaltIterationsPBEKey() { + private static SecretKey getAnonymousPBEKey() { return new PBEKey() { public byte[] getSalt() { return salt.clone(); } public int getIterationCount() { return iterations; } diff --git a/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java b/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java index 360e11c339d2a..7ed572dabbcbc 100644 --- a/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java +++ b/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java @@ -69,8 +69,8 @@ final class ImportKeyToP122 extends PKCS11Test { "HmacPBESHA384", "HmacPBESHA512" }; private static final KeyStore p12; - private static final String sep = - "========================================================================="; + private static final String sep = "======================================" + + "==================================="; static { KeyStore tP12 = null; @@ -128,7 +128,8 @@ private void testWith(Provider sunPKCS11, String pbeCipherAlg, 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); + System.out.println("Secret key import successful" + + System.lineSeparator() + sep); } public static void main(String[] args) throws Exception { diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java index 90bc838e80cd7..0e4b7f9cd99d9 100644 --- a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -23,7 +23,7 @@ */ import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.Provider; import java.security.Security; import java.util.Map; @@ -56,19 +56,20 @@ final class PBAMac2 extends PKCS11Test { private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; private static final String plainText = "This is a know plain text!"; - private static final String sep = - "========================================================================="; + private static final String sep = "======================================" + + "==================================="; - private static enum Configuration { - // Provide salt and iterations through a PBEParameterSpec instance + private enum Configuration { + // Pass salt and iterations to a Mac through a PBEParameterSpec. PBEParameterSpec, - // Derive the key using SunPKCS11's SecretKeyFactory, providing salt - // & iterations through a PBEParameterSpec, then use the derived key - SunPKCS11SecretKeyFactoryDerivedKey, + // Derive a key using SunPKCS11's SecretKeyFactory (wrapping password, + // salt and iterations in a PBEKeySpec), and pass it to a Mac. + SecretKeyFactoryDerivedKey, - // Provide salt and iterations through an anonymous class implementing - // the javax.crypto.interfaces.PBEKey interface + // Pass password, salt and iterations and iterations to + // a Mac through an anonymous class implementing the + // javax.crypto.interfaces.PBEKey interface. AnonymousPBEKey, } @@ -107,43 +108,43 @@ private static void testWith(Provider sunPKCS11, String algorithm, System.out.println(sep + System.lineSeparator() + algorithm + " (with " + conf.name() + ")"); - BigInteger macResult = computeMac(sunPKCS11, algorithm, conf); - printHex("HMAC Result", macResult); + BigInteger mac = computeMac(sunPKCS11, algorithm, conf); + printHex("HMAC", mac); - BigInteger expectedMacResult = computeExpectedMac(algorithm, conf); + BigInteger expectedMac = computeExpectedMac(algorithm, conf); - if (!macResult.equals(expectedMacResult)) { - printHex("Expected HMAC Result", expectedMacResult); - throw new Exception("Expected HMAC Result did not match"); + if (!mac.equals(expectedMac)) { + printHex("Expected HMAC", expectedMac); + throw new Exception("Expected HMAC did not match"); } } private static BigInteger computeMac(Provider p, String algorithm, - Configuration conf) throws Exception { - Mac pbaMac = Mac.getInstance(algorithm, p); + Configuration conf) throws GeneralSecurityException { + Mac mac = Mac.getInstance(algorithm, p); switch (conf) { case PBEParameterSpec -> { SecretKey key = getPasswordOnlyPBEKey(); - pbaMac.init(key, new PBEParameterSpec(salt, iterations)); + mac.init(key, new PBEParameterSpec(salt, iterations)); } - case SunPKCS11SecretKeyFactoryDerivedKey -> { + case SecretKeyFactoryDerivedKey -> { SecretKey key = getDerivedSecretKey(p, algorithm); - pbaMac.init(key); + mac.init(key); } case AnonymousPBEKey -> { - SecretKey key = getPasswordSaltIterationsPBEKey(); - pbaMac.init(key); + SecretKey key = getAnonymousPBEKey(); + mac.init(key); } } - return new BigInteger(1, pbaMac.doFinal(plainText.getBytes())); + return new BigInteger(1, mac.doFinal(plainText.getBytes())); } - private static BigInteger computeExpectedMac( - String algorithm, Configuration conf) throws Exception { + private static BigInteger computeExpectedMac(String algorithm, + Configuration conf) { if (sunJCE != null) { try { return computeMac(sunJCE, algorithm, conf); - } catch (NoSuchAlgorithmException e) { + } catch (GeneralSecurityException e) { // Move to assertionData as it's unlikely that any of // the algorithms are available. sunJCE = null; @@ -154,25 +155,19 @@ private static BigInteger computeExpectedMac( return assertionData.get(algorithm); } - private static SecretKey getPasswordOnlyPBEKey() throws Exception { - return getSecretKey(new PBEKeySpec(password), - SecretKeyFactory.getInstance("PBE")); - } - - private static SecretKey getDerivedSecretKey( - Provider sunPKCS11, String algorithm) throws Exception { - return getSecretKey(new PBEKeySpec(password, salt, iterations), - SecretKeyFactory.getInstance(algorithm, sunPKCS11)); + private static SecretKey getPasswordOnlyPBEKey() + throws GeneralSecurityException { + return SecretKeyFactory.getInstance("PBE") + .generateSecret(new PBEKeySpec(password)); } - private static SecretKey getSecretKey( - PBEKeySpec keySpec, SecretKeyFactory skFac) throws Exception { - SecretKey skey = skFac.generateSecret(keySpec); - keySpec.clearPassword(); - return skey; + private static SecretKey getDerivedSecretKey(Provider sunPKCS11, + String algorithm) throws GeneralSecurityException { + return SecretKeyFactory.getInstance(algorithm, sunPKCS11) + .generateSecret(new PBEKeySpec(password, salt, iterations)); } - private static SecretKey getPasswordSaltIterationsPBEKey() { + private static SecretKey getAnonymousPBEKey() { return new PBEKey() { public byte[] getSalt() { return salt.clone(); } public int getIterationCount() { return iterations; } diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 0b2c98f248c86..79b3c74cbe7f5 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -24,6 +24,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.ReflectiveOperationException; import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import java.security.Provider; @@ -39,7 +40,7 @@ /* * @test * @bug 9999999 - * @summary test key derivation on SunPKCS11's SecretKeyFactory service + * @summary test key derivation on a SunPKCS11 SecretKeyFactory service * @requires (jdk.version.major >= 8) * @library /test/lib .. * @modules java.base/com.sun.crypto.provider:open @@ -57,15 +58,17 @@ final class TestPBKD2 extends PKCS11Test { private static final char[] password = "123456".toCharArray(); private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; - private static final String sep = - "========================================================================="; + private static final String sep = "======================================" + + "==================================="; - private static enum Configuration { - // Provide salt and iterations through a PBEKeySpec instance + private enum Configuration { + // Pass password, salt and iterations to a + // SecretKeyFactory through a PBEKeySpec. PBEKeySpec, - // Provide salt and iterations through an anonymous class implementing - // the javax.crypto.interfaces.PBEKey interface + // Pass password, salt and iterations and iterations to a + // SecretKeyFactory through an anonymous class implementing + // the javax.crypto.interfaces.PBEKey interface. AnonymousPBEKey, } @@ -147,25 +150,30 @@ static final class P12PBKDAssertData implements AssertData { } @Override - public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) - throws Exception { - // Since we need to access an internal SunJCE API, we use reflection - Class 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); - - return new BigInteger(1, (byte[]) deriveMethod.invoke(null, - keySpec.getPassword(), keySpec.getSalt(), - keySpec.getIterationCount(), this.outLen, - MAC_KEY, this.kdfAlgo, this.blockLen)); + public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) { + try { + // Since we need to access an internal + // SunJCE API, we use reflection. + Class 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); + + return new BigInteger(1, (byte[]) deriveMethod.invoke(null, + keySpec.getPassword(), keySpec.getSalt(), + keySpec.getIterationCount(), this.outLen, + MAC_KEY, this.kdfAlgo, this.blockLen)); + } catch (ReflectiveOperationException ignored) { + return assertionData.get(pbAlgo); + } } } @@ -246,7 +254,6 @@ public void main(Provider sunPKCS11) throws Exception { testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", new PBKD2AssertData("PBKDF2WithHmacSHA512", 256), conf); - // Use 1,5 * digest size as the testing derived key length (in bits) testWith(sunPKCS11, "PBKDF2WithHmacSHA1", 240, new PBKD2AssertData("PBKDF2WithHmacSHA1"), conf); testWith(sunPKCS11, "PBKDF2WithHmacSHA224", 336, @@ -263,7 +270,7 @@ public void main(Provider sunPKCS11) throws Exception { private static void testWith(Provider sunPKCS11, String algorithm, AssertData assertData, Configuration conf) throws Exception { - SecretKey key = getPasswordSaltIterationsPBEKey(algorithm); + SecretKey key = getAnonymousPBEKey(algorithm); PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations); testWith(sunPKCS11, algorithm, key, keySpec, assertData, conf); } @@ -305,7 +312,7 @@ private static void testWith(Provider sunPKCS11, String algorithm, } } - private static SecretKey getPasswordSaltIterationsPBEKey(String algorithm) { + private static SecretKey getAnonymousPBEKey(String algorithm) { return new PBEKey() { public byte[] getSalt() { return salt.clone(); } public int getIterationCount() { return iterations; } From 1687d37c7285b62489f20bdfef72d39370343081 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 13 Dec 2022 13:12:52 -0300 Subject: [PATCH 21/24] Review: improve tests assertion data Implement centralized AssertionData and leverage it to store associated non-PBE Cipher/Mac algorithms. Test P11PBEKey with non-PBE Cipher/Mac supported scenarios. Also, SunJCE assertion data is now calculated just once, with a fixed configuration (static data is used when SunJCE is not available). Fix know -> known typo in plainText and adjust static assertion data for this change. Co-Authored-By: Martin Balao --- .../sun/security/pkcs11/Cipher/PBECipher.java | 162 ++++---- test/jdk/sun/security/pkcs11/Mac/PBAMac.java | 116 +++--- .../pkcs11/SecretKeyFactory/TestPBKD.java | 361 +++++++----------- 3 files changed, 292 insertions(+), 347 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java index 7393d090ccca8..9de99321da3d7 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -28,7 +28,6 @@ import java.security.Provider; import java.security.SecureRandom; import java.security.Security; -import java.util.Map; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -61,7 +60,7 @@ final class PBECipher2 extends PKCS11Test { 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 know plain text!"; + private static final String plainText = "This is a known plain text!"; private static final String sep = "======================================" + "==================================="; @@ -84,29 +83,61 @@ private enum Configuration { private static Provider sunJCE = Security.getProvider("SunJCE"); - // Generated with SunJCE - private static final Map assertionData = Map.of( - "PBEWithHmacSHA1AndAES_128", new BigInteger("8eebe98a580fb09d026" + - "dbfe60b3733b079e0de9ea7b0b1ccba011a1652d1e257", 16), - "PBEWithHmacSHA224AndAES_128", new BigInteger("1cbabdeb5d483af4a" + - "841942f4b1095b7d6f60e46fabfd2609c015adc38cc227", 16), - "PBEWithHmacSHA256AndAES_128", new BigInteger("4d82f6591df3508d2" + - "4531f06cdc4f90f4bdab7aeb07fbb57a3712e999d5b6f59", 16), - "PBEWithHmacSHA384AndAES_128", new BigInteger("3a0ed0959d51f40b9" + - "ba9f506a5277f430521f2fbe1ba94bae368835f221b6cb9", 16), - "PBEWithHmacSHA512AndAES_128", new BigInteger("1388287a446009309" + - "1418f4eca3ba1735b1fa025423d74ced36ce578d8ebf9da", 16), - "PBEWithHmacSHA1AndAES_256", new BigInteger("80f8208daab27ed02dd" + - "8a354ef6f23ff7813c84dd1c8a1b081d6f4dee27182a2", 16), - "PBEWithHmacSHA224AndAES_256", new BigInteger("7e3b9ce20aec2e52f" + - "f6c781602d4f79a55a88495b5217f1e22e1a068268e6247", 16), - "PBEWithHmacSHA256AndAES_256", new BigInteger("9d6a8b6a351dfd0dd" + - "9e9f45924b2860dca7719c4c07e207a64ebc1acd16cc157", 16), - "PBEWithHmacSHA384AndAES_256", new BigInteger("6f1b386cee3a8e2d9" + - "8c2e81828da0467dec8b989d22258efeab5932580d01d53", 16), - "PBEWithHmacSHA512AndAES_256", new BigInteger("30aaa346b2edd394f" + - "50916187876ac32f1287b19d55c5eea6f7ef9b84aaf291e", 16) - ); + private record AssertionData(String pbeCipherAlgo, String cipherAlgo, + BigInteger expectedCiphertext) {} + + private static AssertionData cipherAssertionData(String pbeCipherAlgo, + String cipherAlgo, String staticExpectedCiphertext) { + BigInteger expectedCiphertext = null; + if (sunJCE != null) { + try { + expectedCiphertext = computeCipherText(sunJCE, pbeCipherAlgo, + pbeCipherAlgo, Configuration.PBEParameterSpec); + } catch (GeneralSecurityException e) { + // Move to staticExpectedCiphertext as it's unlikely + // that any of the algorithms are available. + sunJCE = null; + } + } + if (expectedCiphertext == null) { + expectedCiphertext = new BigInteger(staticExpectedCiphertext, 16); + } + return new AssertionData(pbeCipherAlgo, cipherAlgo, expectedCiphertext); + } + + // 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 @@ -116,40 +147,37 @@ public void nextBytes(byte[] bytes) {} public void main(Provider sunPKCS11) throws Exception { System.out.println("SunPKCS11: " + sunPKCS11.getName()); for (Configuration conf : Configuration.values()) { - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", conf); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", conf); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", conf); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", conf); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", conf); - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", conf); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", conf); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", conf); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", conf); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", conf); + 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, String algorithm, - Configuration conf) throws Exception { - System.out.println(sep + System.lineSeparator() + algorithm + 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, algorithm, conf); + BigInteger cipherText = computeCipherText(sunPKCS11, svcAlgo, + data.pbeCipherAlgo, conf); printHex("Cipher Text", cipherText); - BigInteger expectedCipherText = - computeExpectedCipherText(algorithm, conf); - - if (!cipherText.equals(expectedCipherText)) { - printHex("Expected Cipher Text", expectedCipherText); + 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 algorithm, - Configuration conf) throws GeneralSecurityException { - Cipher cipher = Cipher.getInstance(algorithm, p); + 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(); @@ -166,34 +194,19 @@ private static BigInteger computeCipherText(Provider p, String algorithm, } } case SecretKeyFactoryDerivedKey -> { - SecretKey key = getDerivedSecretKey(p, algorithm); + SecretKey key = getDerivedSecretKey(p, keyAlgo); cipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec.getParameterSpec()); } case AnonymousPBEKey -> { - SecretKey key = getAnonymousPBEKey(); + SecretKey key = getAnonymousPBEKey(keyAlgo, + svcAlgo.equals(keyAlgo)); cipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); } } return new BigInteger(1, cipher.doFinal(plainText.getBytes())); } - private static BigInteger computeExpectedCipherText(String algorithm, - Configuration conf) { - if (sunJCE != null) { - try { - return computeCipherText(sunJCE, algorithm, conf); - } catch (GeneralSecurityException e) { - // Move to assertionData as it's unlikely that any of - // the algorithms are available. - sunJCE = null; - } - } - // If SunJCE or the algorithm are not available, assertionData - // is used instead. - return assertionData.get(algorithm); - } - private static SecretKey getPasswordOnlyPBEKey() throws GeneralSecurityException { return SecretKeyFactory.getInstance("PBE") @@ -206,18 +219,23 @@ private static SecretKey getDerivedSecretKey(Provider sunPKCS11, .generateSecret(new PBEKeySpec(password, salt, iterations)); } - private static SecretKey getAnonymousPBEKey() { + 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 "PBE"; } + public String getAlgorithm() { return algorithm; } public String getFormat() { return "RAW"; } - public char[] getPassword() { return null; } // unused in PBE Cipher + public char[] getPassword() { return password.clone(); } public byte[] getEncoded() { - byte[] passwdBytes = new byte[password.length]; - for (int i = 0; i < password.length; i++) - passwdBytes[i] = (byte) (password[i] & 0x7f); - return passwdBytes; + 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; } }; } diff --git a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java index 0e4b7f9cd99d9..e6b320bdffb0f 100644 --- a/test/jdk/sun/security/pkcs11/Mac/PBAMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -26,7 +26,6 @@ import java.security.GeneralSecurityException; import java.security.Provider; import java.security.Security; -import java.util.Map; import javax.crypto.Mac; import javax.crypto.SecretKey; @@ -55,7 +54,7 @@ final class PBAMac2 extends PKCS11Test { private static final char[] password = "123456".toCharArray(); private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; - private static final String plainText = "This is a know plain text!"; + private static final String plainText = "This is a known plain text!"; private static final String sep = "======================================" + "==================================="; @@ -75,86 +74,95 @@ private enum Configuration { private static Provider sunJCE = Security.getProvider("SunJCE"); - // Generated with SunJCE - private static final Map assertionData = Map.of( - "HmacPBESHA1", new BigInteger("febd26da5d63ce819770a2af1fc2857e" + - "e2c9c41c", 16), - "HmacPBESHA224", new BigInteger("aa6a3a1c35a4b266fea62d1a871508" + - "bd45f8ec326bcf16e09699063", 16), - "HmacPBESHA256", new BigInteger("af4d71121fd4e9d52eb42944d99b77" + - "8ff64376fcf6af8d1dca3ec688dfada5c8", 16), - "HmacPBESHA384", new BigInteger("5d6d37764205985ffca7e4a6222752" + - "a8bbd0520858da08ecafdc57e6246894675e375b9ba084f9ce7142" + - "35f202cc3452", 16), - "HmacPBESHA512", new BigInteger("f586c2006cc2de73fd5743e5cca701" + - "c942d3741a7a54a2a649ea36898996cf3c483f2d734179b47751db" + - "e8373c980b4072136d2e2810f4e7276024a3e9081cc1", 16) - ); + private record AssertionData(String pbeHmacAlgo, String hmacAlgo, + BigInteger expectedMac) {} + + private static AssertionData macAssertionData(String pbeHmacAlgo, + String hmacAlgo, String staticExpectedMac) { + BigInteger expectedMac = null; + if (sunJCE != null) { + try { + expectedMac = computeMac(sunJCE, pbeHmacAlgo, + pbeHmacAlgo, Configuration.PBEParameterSpec); + } catch (GeneralSecurityException e) { + // Move to staticExpectedMac as it's unlikely + // that any of the algorithms are available. + sunJCE = null; + } + } + if (expectedMac == null) { + expectedMac = new BigInteger(staticExpectedMac, 16); + } + return new AssertionData(pbeHmacAlgo, hmacAlgo, expectedMac); + } + + // 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()) { - testWith(sunPKCS11, "HmacPBESHA1", conf); - testWith(sunPKCS11, "HmacPBESHA224", conf); - testWith(sunPKCS11, "HmacPBESHA256", conf); - testWith(sunPKCS11, "HmacPBESHA384", conf); - testWith(sunPKCS11, "HmacPBESHA512", conf); + 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, String algorithm, - Configuration conf) throws Exception { - System.out.println(sep + System.lineSeparator() + algorithm + 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, algorithm, conf); + BigInteger mac = computeMac(sunPKCS11, svcAlgo, data.pbeHmacAlgo, conf); printHex("HMAC", mac); - BigInteger expectedMac = computeExpectedMac(algorithm, conf); - - if (!mac.equals(expectedMac)) { - printHex("Expected HMAC", expectedMac); + 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 algorithm, - Configuration conf) throws GeneralSecurityException { - Mac mac = Mac.getInstance(algorithm, p); + 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, algorithm); + SecretKey key = getDerivedSecretKey(p, keyAlgo); mac.init(key); } case AnonymousPBEKey -> { - SecretKey key = getAnonymousPBEKey(); + SecretKey key = getAnonymousPBEKey(keyAlgo); mac.init(key); } } return new BigInteger(1, mac.doFinal(plainText.getBytes())); } - private static BigInteger computeExpectedMac(String algorithm, - Configuration conf) { - if (sunJCE != null) { - try { - return computeMac(sunJCE, algorithm, conf); - } catch (GeneralSecurityException e) { - // Move to assertionData as it's unlikely that any of - // the algorithms are available. - sunJCE = null; - } - } - // If SunJCE or the algorithm are not available, assertionData - // is used instead. - return assertionData.get(algorithm); - } - private static SecretKey getPasswordOnlyPBEKey() throws GeneralSecurityException { return SecretKeyFactory.getInstance("PBE") @@ -167,14 +175,14 @@ private static SecretKey getDerivedSecretKey(Provider sunPKCS11, .generateSecret(new PBEKeySpec(password, salt, iterations)); } - private static SecretKey getAnonymousPBEKey() { + private static SecretKey getAnonymousPBEKey(String algorithm) { return new PBEKey() { public byte[] getSalt() { return salt.clone(); } public int getIterationCount() { return iterations; } - public String getAlgorithm() { return "PBE"; } + public String getAlgorithm() { return algorithm; } public String getFormat() { return "RAW"; } public char[] getPassword() { return password.clone(); } - public byte[] getEncoded() { return null; } // unused in PBA Mac + public byte[] getEncoded() { return null; } }; } diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 79b3c74cbe7f5..16804873722e9 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -29,8 +29,7 @@ import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.Security; -import java.util.HashMap; -import java.util.Map; +import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; @@ -55,9 +54,6 @@ public static void main(String[] args) throws Exception { } final class TestPBKD2 extends PKCS11Test { - private static final char[] password = "123456".toCharArray(); - private static final byte[] salt = "abcdefgh".getBytes(); - private static final int iterations = 1000; private static final String sep = "======================================" + "==================================="; @@ -74,252 +70,175 @@ private enum Configuration { private static Provider sunJCE = Security.getProvider("SunJCE"); - // Generated with SunJCE - private static final Map assertionData = - new HashMap<>() {{ - put("HmacPBESHA1", new BigInteger("5f7d1c360d1703cede76f47db" + - "2fa3facc62e7694", 16)); - put("HmacPBESHA224", new BigInteger("289563f799b708f522ab2a3" + - "8d283d0afa8fc1d3d227fcb9236c3a035", 16)); - put("HmacPBESHA256", new BigInteger("888defcf4ef37eb0647014a" + - "d172dd6fa3b3e9d024b962dba47608eea9b9c4b79", 16)); - put("HmacPBESHA384", new BigInteger("f5464b34253fadab8838d0d" + - "b11980c1787a99bf6f6304f2d8c942e30bada523494f9d5a0f3" + - "741e411de21add8b5718a8", 16)); - put("HmacPBESHA512", new BigInteger("18ae94337b132c68c611bc2" + - "e723ac24dcd44a46d900dae2dd6170380d4c34f90fef7bdeb5f" + - "6fddeb0d2230003e329b7a7eefcd35810d364ba95d31b68bb61" + - "e52", 16)); - put("PBEWithHmacSHA1AndAES_128", new BigInteger("fdb3dcc2e81" + - "244d4d56bf7ec8dd61dd7", 16)); - put("PBEWithHmacSHA224AndAES_128", new BigInteger("5ef9e5c6f" + - "df7c355f3b424233a9f24c2", 16)); - put("PBEWithHmacSHA256AndAES_128", new BigInteger("c5af597b0" + - "1b4f6baac8f62ff6f22bfb1", 16)); - put("PBEWithHmacSHA384AndAES_128", new BigInteger("c3208ebc5" + - "d6db88858988ec00153847d", 16)); - put("PBEWithHmacSHA512AndAES_128", new BigInteger("b27e8f7fb" + - "6a4bd5ebea892cd9a7f5043", 16)); - put("PBEWithHmacSHA1AndAES_256", new BigInteger("fdb3dcc2e81" + - "244d4d56bf7ec8dd61dd78a1b6fb3ad11d9ebd7f62027a2ccde" + - "98", 16)); - put("PBEWithHmacSHA224AndAES_256", new BigInteger("5ef9e5c6f" + - "df7c355f3b424233a9f24c2c9c41793cb0948b8ea3aac240b8d" + - "f64d", 16)); - put("PBEWithHmacSHA256AndAES_256", new BigInteger("c5af597b0" + - "1b4f6baac8f62ff6f22bfb1f319c3278c8b31cc616294716d4e" + - "ab08", 16)); - put("PBEWithHmacSHA384AndAES_256", new BigInteger("c3208ebc5" + - "d6db88858988ec00153847d5b1b7a8723640a022dc332bcaefe" + - "b356", 16)); - put("PBEWithHmacSHA512AndAES_256", new BigInteger("b27e8f7fb" + - "6a4bd5ebea892cd9a7f5043cefff9c38b07e599721e8d116189" + - "5482", 16)); - put("PBKDF2WithHmacSHA1", new BigInteger("fdb3dcc2e81244d4d5" + - "6bf7ec8dd61dd78a1b6fb3ad11d9ebd7f62027a2cc", 16)); - put("PBKDF2WithHmacSHA224", new BigInteger("5ef9e5c6fdf7c355" + - "f3b424233a9f24c2c9c41793cb0948b8ea3aac240b8df64d1a0" + - "736ec1c69eef1c7b2", 16)); - put("PBKDF2WithHmacSHA256", new BigInteger("c5af597b01b4f6ba" + - "ac8f62ff6f22bfb1f319c3278c8b31cc616294716d4eab080b9" + - "add9db34a42ceb2fea8d27adc00f4", 16)); - put("PBKDF2WithHmacSHA384", new BigInteger("c3208ebc5d6db888" + - "58988ec00153847d5b1b7a8723640a022dc332bcaefeb356995" + - "d076a949d35c42c7e1e1ca936c12f8dc918e497edf279a522b7" + - "c99580e2613846b3919af637da", 16)); - put("PBKDF2WithHmacSHA512", new BigInteger("b27e8f7fb6a4bd5e" + - "bea892cd9a7f5043cefff9c38b07e599721e8d1161895482da2" + - "55746844cc1030be37ba1969df10ff59554d1ac5468fa9b7297" + - "7bb7fd52103a0a7b488cdb8957616c3e23a16bca92120982180" + - "c6c11a4f14649b50d0ade3a", 16)); - }}; - - static interface AssertData { - BigInteger derive(String pbAlgo, PBEKeySpec keySpec) throws Exception; + private static BigInteger i(byte[] data) { + return new BigInteger(1, data); } - static final class P12PBKDAssertData implements AssertData { - private final int outLen; - private final String kdfAlgo; - private final int blockLen; + private record AssertionData(String algo, PBEKeySpec keySpec, + BigInteger expectedKey) {} - P12PBKDAssertData(int outLen, String kdfAlgo, int blockLen) { - this.outLen = outLen; - this.kdfAlgo = kdfAlgo; - this.blockLen = blockLen; + private static AssertionData p12PBKDAssertionData(String algo, + char[] password, int keyLen, String hashAlgo, int blockLen, + String staticExpectedKey) { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); + BigInteger expectedKey; + try { + // Since we need to access an internal + // SunJCE API, we use reflection. + Class 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)); + } catch (ReflectiveOperationException ignored) { + expectedKey = new BigInteger(staticExpectedKey, 16); } + return new AssertionData(algo, keySpec, expectedKey); + } - @Override - public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) { + private static AssertionData pbkd2AssertionData(String algo, + char[] password, int keyLen, String kdfAlgo, + String staticExpectedKey) { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); + BigInteger expectedKey = null; + if (sunJCE != null) { try { - // Since we need to access an internal - // SunJCE API, we use reflection. - Class 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); - - return new BigInteger(1, (byte[]) deriveMethod.invoke(null, - keySpec.getPassword(), keySpec.getSalt(), - keySpec.getIterationCount(), this.outLen, - MAC_KEY, this.kdfAlgo, this.blockLen)); - } catch (ReflectiveOperationException ignored) { - return assertionData.get(pbAlgo); + expectedKey = i(SecretKeyFactory.getInstance(kdfAlgo, sunJCE) + .generateSecret(keySpec).getEncoded()); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + // Move to staticExpectedKey as it's unlikely + // that any of the algorithms are available. + sunJCE = null; } } - } - - static final class PBKD2AssertData implements AssertData { - private final String kdfAlgo; - private final int keyLen; - - PBKD2AssertData(String kdfAlgo, int keyLen) { - // Key length is pinned by the algorithm name (not kdfAlgo, - // but the algorithm under test: PBEWithHmacSHA*AndAES_*) - this.kdfAlgo = kdfAlgo; - this.keyLen = keyLen; + if (expectedKey == null) { + expectedKey = new BigInteger(staticExpectedKey, 16); } + return new AssertionData(algo, keySpec, expectedKey); + } - PBKD2AssertData(String kdfAlgo) { - // Key length is variable for the algorithm under test - // (kdfAlgo is the algorithm under test: PBKDF2WithHmacSHA*) - this(kdfAlgo, -1); - } + private static final char[] pwd = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes(); + private static final int iterations = 1000; - @Override - public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) - throws Exception { - if (this.keyLen != -1) { - keySpec = new PBEKeySpec( - keySpec.getPassword(), keySpec.getSalt(), - keySpec.getIterationCount(), this.keyLen); - } - if (sunJCE != null) { - try { - return new BigInteger(1, SecretKeyFactory.getInstance( - this.kdfAlgo, sunJCE).generateSecret(keySpec) - .getEncoded()); - } catch (NoSuchAlgorithmException e) { - // Move to assertionData as it's unlikely that any of - // the algorithms are available. - sunJCE = null; - } - } - // If SunJCE or the algorithm are not available, assertionData - // is used instead. - return assertionData.get(pbAlgo); - } - } + private static final AssertionData[] assertionData = new AssertionData[]{ + p12PBKDAssertionData("HmacPBESHA1", pwd, 160, "SHA-1", 64, + "5f7d1c360d1703cede76f47db2fa3facc62e7694"), + p12PBKDAssertionData("HmacPBESHA224", pwd, 224, "SHA-224", 64, + "289563f799b708f522ab2a38d283d0afa8fc1d3d227fcb9236c3a035"), + p12PBKDAssertionData("HmacPBESHA256", pwd, 256, "SHA-256", 64, + "888defcf4ef37eb0647014ad172dd6fa3b3e9d024b962dba47608eea" + + "9b9c4b79"), + p12PBKDAssertionData("HmacPBESHA384", pwd, 384, "SHA-384", 128, + "f5464b34253fadab8838d0db11980c1787a99bf6f6304f2d8c942e30" + + "bada523494f9d5a0f3741e411de21add8b5718a8"), + p12PBKDAssertionData("HmacPBESHA512", pwd, 512, "SHA-512", 128, + "18ae94337b132c68c611bc2e723ac24dcd44a46d900dae2dd6170380" + + "d4c34f90fef7bdeb5f6fddeb0d2230003e329b7a7eefcd35810d364b" + + "a95d31b68bb61e52"), + pbkd2AssertionData("PBEWithHmacSHA1AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA1", "fdb3dcc2e81244d4d56bf7ec8dd61dd7"), + pbkd2AssertionData("PBEWithHmacSHA224AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA224", "5ef9e5c6fdf7c355f3b424233a9f24c2"), + pbkd2AssertionData("PBEWithHmacSHA256AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA256", "c5af597b01b4f6baac8f62ff6f22bfb1"), + pbkd2AssertionData("PBEWithHmacSHA384AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA384", "c3208ebc5d6db88858988ec00153847d"), + pbkd2AssertionData("PBEWithHmacSHA512AndAES_128", pwd, 128, + "PBKDF2WithHmacSHA512", "b27e8f7fb6a4bd5ebea892cd9a7f5043"), + pbkd2AssertionData("PBEWithHmacSHA1AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA1", "fdb3dcc2e81244d4d56bf7ec8dd61dd78a" + + "1b6fb3ad11d9ebd7f62027a2ccde98"), + pbkd2AssertionData("PBEWithHmacSHA224AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA224", "5ef9e5c6fdf7c355f3b424233a9f24c2" + + "c9c41793cb0948b8ea3aac240b8df64d"), + pbkd2AssertionData("PBEWithHmacSHA256AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA256", "c5af597b01b4f6baac8f62ff6f22bfb1" + + "f319c3278c8b31cc616294716d4eab08"), + pbkd2AssertionData("PBEWithHmacSHA384AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA384", "c3208ebc5d6db88858988ec00153847d" + + "5b1b7a8723640a022dc332bcaefeb356"), + pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA512", "b27e8f7fb6a4bd5ebea892cd9a7f5043" + + "cefff9c38b07e599721e8d1161895482"), + pbkd2AssertionData("PBKDF2WithHmacSHA1", pwd, 240, + "PBKDF2WithHmacSHA1", "fdb3dcc2e81244d4d56bf7ec8dd61dd78a" + + "1b6fb3ad11d9ebd7f62027a2cc"), + pbkd2AssertionData("PBKDF2WithHmacSHA224", pwd, 336, + "PBKDF2WithHmacSHA224", "5ef9e5c6fdf7c355f3b424233a9f24c2" + + "c9c41793cb0948b8ea3aac240b8df64d1a0736ec1c69eef1c7b2"), + pbkd2AssertionData("PBKDF2WithHmacSHA256", pwd, 384, + "PBKDF2WithHmacSHA256", "c5af597b01b4f6baac8f62ff6f22bfb1" + + "f319c3278c8b31cc616294716d4eab080b9add9db34a42ceb2fea8d2" + + "7adc00f4"), + pbkd2AssertionData("PBKDF2WithHmacSHA384", pwd, 576, + "PBKDF2WithHmacSHA384", "c3208ebc5d6db88858988ec00153847d" + + "5b1b7a8723640a022dc332bcaefeb356995d076a949d35c42c7e1e1c" + + "a936c12f8dc918e497edf279a522b7c99580e2613846b3919af637da"), + pbkd2AssertionData("PBKDF2WithHmacSHA512", pwd, 768, + "PBKDF2WithHmacSHA512", "b27e8f7fb6a4bd5ebea892cd9a7f5043" + + "cefff9c38b07e599721e8d1161895482da255746844cc1030be37ba1" + + "969df10ff59554d1ac5468fa9b72977bb7fd52103a0a7b488cdb8957" + + "616c3e23a16bca92120982180c6c11a4f14649b50d0ade3a"), + }; public void main(Provider sunPKCS11) throws Exception { System.out.println("SunPKCS11: " + sunPKCS11.getName()); - for (Configuration conf : Configuration.values()) { - testWith(sunPKCS11, "HmacPBESHA1", - new P12PBKDAssertData(20, "SHA-1", 64), conf); - testWith(sunPKCS11, "HmacPBESHA224", - new P12PBKDAssertData(28, "SHA-224", 64), conf); - testWith(sunPKCS11, "HmacPBESHA256", - new P12PBKDAssertData(32, "SHA-256", 64), conf); - testWith(sunPKCS11, "HmacPBESHA384", - new P12PBKDAssertData(48, "SHA-384", 128), conf); - testWith(sunPKCS11, "HmacPBESHA512", - new P12PBKDAssertData(64, "SHA-512", 128), conf); - - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA1", 128), conf); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA224", 128), conf); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA256", 128), conf); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA384", 128), conf); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", - new PBKD2AssertData("PBKDF2WithHmacSHA512", 128), conf); - testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA1", 256), conf); - testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA224", 256), conf); - testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA256", 256), conf); - testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA384", 256), conf); - testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", - new PBKD2AssertData("PBKDF2WithHmacSHA512", 256), conf); - testWith(sunPKCS11, "PBKDF2WithHmacSHA1", 240, - new PBKD2AssertData("PBKDF2WithHmacSHA1"), conf); - testWith(sunPKCS11, "PBKDF2WithHmacSHA224", 336, - new PBKD2AssertData("PBKDF2WithHmacSHA224"), conf); - testWith(sunPKCS11, "PBKDF2WithHmacSHA256", 384, - new PBKD2AssertData("PBKDF2WithHmacSHA256"), conf); - testWith(sunPKCS11, "PBKDF2WithHmacSHA384", 576, - new PBKD2AssertData("PBKDF2WithHmacSHA384"), conf); - testWith(sunPKCS11, "PBKDF2WithHmacSHA512", 768, - new PBKD2AssertData("PBKDF2WithHmacSHA512"), conf); + // Test valid cases. + for (Configuration conf : Configuration.values()) { + for (AssertionData data : assertionData) { + testValidWith(sunPKCS11, data, conf); + } } System.out.println("TEST PASS - OK"); } - private static void testWith(Provider sunPKCS11, String algorithm, - AssertData assertData, Configuration conf) throws Exception { - SecretKey key = getAnonymousPBEKey(algorithm); - PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations); - testWith(sunPKCS11, algorithm, key, keySpec, assertData, conf); - } - - private static void testWith(Provider sunPKCS11, String algorithm, - int keyLen, AssertData assertData, Configuration conf) - throws Exception { - if (conf == Configuration.AnonymousPBEKey) { - // Skip, we can't specify the key length in this scenario, and - // we would get "Key length must be a non-zero positive integer" - return; - } - PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); - testWith(sunPKCS11, algorithm, null, keySpec, assertData, conf); - } - - private static void testWith(Provider sunPKCS11, String algorithm, - SecretKey key, PBEKeySpec keySpec, AssertData assertData, - Configuration conf) - throws Exception { - System.out.println(sep + System.lineSeparator() + algorithm + private static void testValidWith(Provider sunPKCS11, AssertionData data, + Configuration conf) throws Exception { + System.out.println(sep + System.lineSeparator() + data.algo + " (with " + conf.name() + ")"); - SecretKeyFactory skFac = SecretKeyFactory.getInstance( - algorithm, sunPKCS11); - - SecretKey derivedKeyObj = switch (conf) { - case PBEKeySpec -> skFac.generateSecret(keySpec); - case AnonymousPBEKey -> skFac.translateKey(key); + 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 derivedKey = new BigInteger(1, derivedKeyObj.getEncoded()); - printHex("Derived Key", derivedKey); - - BigInteger expectedDerivedKey = assertData.derive(algorithm, keySpec); + BigInteger derivedKeyValue = i(derivedKey.getEncoded()); + printHex("Derived Key", derivedKeyValue); - if (!derivedKey.equals(expectedDerivedKey)) { - printHex("Expected Derived Key", expectedDerivedKey); + if (!derivedKeyValue.equals(data.expectedKey)) { + printHex("Expected Derived Key", data.expectedKey); throw new Exception("Expected Derived Key did not match"); } } - private static SecretKey getAnonymousPBEKey(String algorithm) { + private static SecretKey getAnonymousPBEKey(String algorithm, + PBEKeySpec keySpec) { return new PBEKey() { - public byte[] getSalt() { return salt.clone(); } - public int getIterationCount() { return iterations; } + 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 password.clone(); } - public byte[] getEncoded() { return null; } // unused + public char[] getPassword() { return keySpec.getPassword(); } + public byte[] getEncoded() { + return new byte[keySpec.getKeyLength() / 8]; + } }; } From 326482da2ed83a17788558564c9de2cc67afcd8b Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 13 Dec 2022 13:21:26 -0300 Subject: [PATCH 22/24] Review: increase P11SecretKeyFactory coverage Implement empty password cases. Implement translation of a P11PBEKey from the same token, which must return exactly the same object. Implement SecretKeyFactory.getKeySpec test. Co-Authored-By: Martin Balao --- .../pkcs11/SecretKeyFactory/TestPBKD.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 16804873722e9..7e6cb3f7ad017 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -30,11 +30,14 @@ import java.security.Provider; import java.security.Security; import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; /* * @test @@ -129,9 +132,11 @@ private static AssertionData pbkd2AssertionData(String algo, } private static final char[] pwd = "123456".toCharArray(); + private static final char[] emptyPwd = new char[0]; private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; + // Generated with SunJCE. private static final AssertionData[] assertionData = new AssertionData[]{ p12PBKDAssertionData("HmacPBESHA1", pwd, 160, "SHA-1", 64, "5f7d1c360d1703cede76f47db2fa3facc62e7694"), @@ -191,6 +196,13 @@ private static AssertionData pbkd2AssertionData(String algo, "cefff9c38b07e599721e8d1161895482da255746844cc1030be37ba1" + "969df10ff59554d1ac5468fa9b72977bb7fd52103a0a7b488cdb8957" + "616c3e23a16bca92120982180c6c11a4f14649b50d0ade3a"), + 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 { @@ -224,6 +236,17 @@ private static void testValidWith(Provider sunPKCS11, AssertionData data, 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, @@ -247,6 +270,75 @@ private static void printHex(String title, BigInteger b) { 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()); + } + } + public static void main(String[] args) throws Exception { TestPBKD2 test = new TestPBKD2(); Provider p = Security.getProvider("SunPKCS11-NSS-FIPS"); From c430dc48841a1c953289b033e68ca91c3b484204 Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 13 Dec 2022 13:28:39 -0300 Subject: [PATCH 23/24] Review: add P11SecretKeyFactory invalid cases Add test cases in which consistency checks must trigger exceptions: translateKey Non-PBEKey key to PBE SecretKeyFactory PBEKey key to PBE SecretKeyFactory of a different algorithm Non-AES PBEKey key to AES SecretKeyFactory Inconsistent key length between key and algorithm Invalid key length in bits generateSecret Missing salt and iteration count Inconsistent key length between spec and algorithm Invalid key length in bits PBEKeySpec to non-PBE SecretKeyFactory getKeySpec null KeySpec class Invalid key type for PBEKeySpec Invalid PBE key and PBEKeySpec for AES SecretKeyFactory Co-Authored-By: Martin Balao --- .../pkcs11/SecretKeyFactory/TestPBKD.java | 186 ++++++++++++++++-- 1 file changed, 175 insertions(+), 11 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java index 7e6cb3f7ad017..3c834a57f7720 100644 --- a/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.ReflectiveOperationException; import java.math.BigInteger; +import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.Security; @@ -136,12 +137,26 @@ private static AssertionData pbkd2AssertionData(String algo, private static final byte[] salt = "abcdefgh".getBytes(); private static final int iterations = 1000; - // Generated with SunJCE. - private static final AssertionData[] assertionData = new AssertionData[]{ + // 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, - "5f7d1c360d1703cede76f47db2fa3facc62e7694"), + "5f7d1c360d1703cede76f47db2fa3facc62e7694"); + private static final AssertionData hmacPBESHA224Data = p12PBKDAssertionData("HmacPBESHA224", pwd, 224, "SHA-224", 64, - "289563f799b708f522ab2a38d283d0afa8fc1d3d227fcb9236c3a035"), + "289563f799b708f522ab2a38d283d0afa8fc1d3d227fcb9236c3a035"); + private static final AssertionData pbeWithHmacSHA512AndAES256Data = + pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", pwd, 256, + "PBKDF2WithHmacSHA512", "b27e8f7fb6a4bd5ebea892cd9a7f5043" + + "cefff9c38b07e599721e8d1161895482"); + private static final AssertionData pbkdf2WithHmacSHA256Data = + pbkd2AssertionData("PBKDF2WithHmacSHA256", pwd, 384, + "PBKDF2WithHmacSHA256", "c5af597b01b4f6baac8f62ff6f22bfb1" + + "f319c3278c8b31cc616294716d4eab080b9add9db34a42ceb2fea8d2" + + "7adc00f4"); + private static final AssertionData[] assertionData = new AssertionData[]{ + hmacPBESHA1Data, + hmacPBESHA224Data, p12PBKDAssertionData("HmacPBESHA256", pwd, 256, "SHA-256", 64, "888defcf4ef37eb0647014ad172dd6fa3b3e9d024b962dba47608eea" + "9b9c4b79"), @@ -174,19 +189,14 @@ private static AssertionData pbkd2AssertionData(String algo, pbkd2AssertionData("PBEWithHmacSHA384AndAES_256", pwd, 256, "PBKDF2WithHmacSHA384", "c3208ebc5d6db88858988ec00153847d" + "5b1b7a8723640a022dc332bcaefeb356"), - pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", pwd, 256, - "PBKDF2WithHmacSHA512", "b27e8f7fb6a4bd5ebea892cd9a7f5043" + - "cefff9c38b07e599721e8d1161895482"), + pbeWithHmacSHA512AndAES256Data, pbkd2AssertionData("PBKDF2WithHmacSHA1", pwd, 240, "PBKDF2WithHmacSHA1", "fdb3dcc2e81244d4d56bf7ec8dd61dd78a" + "1b6fb3ad11d9ebd7f62027a2cc"), pbkd2AssertionData("PBKDF2WithHmacSHA224", pwd, 336, "PBKDF2WithHmacSHA224", "5ef9e5c6fdf7c355f3b424233a9f24c2" + "c9c41793cb0948b8ea3aac240b8df64d1a0736ec1c69eef1c7b2"), - pbkd2AssertionData("PBKDF2WithHmacSHA256", pwd, 384, - "PBKDF2WithHmacSHA256", "c5af597b01b4f6baac8f62ff6f22bfb1" + - "f319c3278c8b31cc616294716d4eab080b9add9db34a42ceb2fea8d2" + - "7adc00f4"), + pbkdf2WithHmacSHA256Data, pbkd2AssertionData("PBKDF2WithHmacSHA384", pwd, 576, "PBKDF2WithHmacSHA384", "c3208ebc5d6db88858988ec00153847d" + "5b1b7a8723640a022dc332bcaefeb356995d076a949d35c42c7e1e1c" + @@ -214,6 +224,12 @@ public void main(Provider sunPKCS11) throws Exception { testValidWith(sunPKCS11, data, conf); } } + + // Test invalid cases. + testInvalidTranslateKey(sunPKCS11); + testInvalidGenerateSecret(sunPKCS11); + testInvalidGetKeySpec(sunPKCS11); + System.out.println("TEST PASS - OK"); } @@ -339,6 +355,154 @@ private static void testTranslateP11PBEKeyToSunJCE(String algorithm, } } + @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 { TestPBKD2 test = new TestPBKD2(); Provider p = Security.getProvider("SunPKCS11-NSS-FIPS"); From 709f49ed620f89f3fa7bd201e480ee15f664989b Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Fri, 16 Dec 2022 20:19:35 -0300 Subject: [PATCH 24/24] Fix broken Mac tests outside PBE new ones --- test/jdk/sun/security/pkcs11/Mac/MacSameTest.java | 15 +++++++++++++-- test/jdk/sun/security/pkcs11/Mac/ReinitMac.java | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java b/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java index f009fbb95e56f..b7386b3f57c00 100644 --- a/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java +++ b/test/jdk/sun/security/pkcs11/Mac/MacSameTest.java @@ -42,6 +42,8 @@ import javax.crypto.Mac; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class MacSameTest extends PKCS11Test { @@ -75,9 +77,18 @@ public void main(Provider p) { // first try w/ java secret key object byte[] keyVal = new byte[KEY_SIZE]; srdm.nextBytes(keyVal); - SecretKey skey = new SecretKeySpec(keyVal, alg); - + SecretKey skey; try { + if (alg.startsWith("HmacPBE")) { + char[] pwd = new char[keyVal.length]; + for (int i = 0; i < keyVal.length; i++) { + pwd[i] = (char) keyVal[i]; + } + skey = SecretKeyFactory.getInstance(alg, p).generateSecret( + new PBEKeySpec(pwd, keyVal, 1000)); + } else { + skey = new SecretKeySpec(keyVal, alg); + } doTest(alg, skey, p); } catch (Exception e) { System.out.println("Unexpected exception: " + e); diff --git a/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java b/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java index 66fd7d88ce440..70bb80732e33b 100644 --- a/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java +++ b/test/jdk/sun/security/pkcs11/Mac/ReinitMac.java @@ -37,6 +37,9 @@ import java.util.Random; import java.util.List; import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class ReinitMac extends PKCS11Test { @@ -75,7 +78,17 @@ public void main(Provider p) throws Exception { private void doTest(String alg, Provider p, byte[] keyVal, byte[] data) throws Exception { System.out.println("Testing " + alg); - SecretKeySpec key = new SecretKeySpec(keyVal, alg); + SecretKey key; + if (alg.startsWith("HmacPBE")) { + char[] pwd = new char[keyVal.length]; + for (int i = 0; i < keyVal.length; i++) { + pwd[i] = (char) keyVal[i]; + } + key = SecretKeyFactory.getInstance(alg, p).generateSecret( + new PBEKeySpec(pwd, keyVal, 1000)); + } else { + key = new SecretKeySpec(keyVal, alg); + } Mac mac = Mac.getInstance(alg, p); mac.init(key); mac.init(key);