From 24cc8ab11ca733e212eb25110d51bc00a5ef7575 Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Thu, 19 Jan 2023 10:53:03 -0500 Subject: [PATCH] Support export/import of cryptographic keys in FIPS Mode Signed-off-by: Tao Liu --- src/java.base/share/classes/module-info.java | 3 +- .../share/lib/security/default.policy | 2 + .../classes/sun/security/pkcs11/P11Key.java | 118 ++++++++++++++++ .../sun/security/pkcs11/SunPKCS11.java | 131 +++++++++++++++++- .../sun/security/pkcs11/wrapper/PKCS11.java | 50 ++++++- 5 files changed, 301 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index ca290519311..74a1a1eb1aa 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -25,7 +25,7 @@ /* * =========================================================================== - * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved * =========================================================================== */ @@ -366,6 +366,7 @@ exports sun.util.resources to jdk.localedata; exports openj9.internal.security to + jdk.crypto.cryptoki, jdk.crypto.ec; // the service types defined by the APIs in this module diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy index d815ac778bd..70f2693918c 100644 --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -156,6 +156,8 @@ grant codeBase "jrt:/jdk.crypto.cryptoki" { "accessClassInPackage.sun.security.*"; permission java.lang.RuntimePermission "accessClassInPackage.sun.nio.ch"; permission java.lang.RuntimePermission "loadLibrary.j2pkcs11"; + permission java.lang.RuntimePermission + "accessClassInPackage.openj9.internal.security"; permission java.util.PropertyPermission "sun.security.pkcs11.allowSingleThreadedModules", "read"; permission java.util.PropertyPermission "sun.security.pkcs11.disableKeyExtraction", "read"; permission java.util.PropertyPermission "os.name", "read"; 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 cae28a06d7b..c7d60c3d3ba 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 @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11; import java.io.*; @@ -399,6 +405,20 @@ static PrivateKey privateKey(Session session, long keyID, String algorithm, boolean keySensitive = (attrs[0].getBoolean() || attrs[1].getBoolean() || !attrs[2].getBoolean()); + if (keySensitive && (SunPKCS11.mysunpkcs11 != null) && "RSA".equals(algorithm)) { + try { + byte[] key = SunPKCS11.mysunpkcs11.exportKey(session.id(), attrs, keyID); + RSAPrivateKey rsaPrivKey = RSAPrivateCrtKeyImpl.newKey(KeyType.RSA, "PKCS#8", key); + if (rsaPrivKey instanceof RSAPrivateCrtKeyImpl privImpl) { + return new P11RSAPrivateKeyFIPS(session, keyID, algorithm, keyLength, attrs, privImpl); + } else { + return new P11RSAPrivateNonCRTKeyFIPS(session, keyID, algorithm, keyLength, attrs, rsaPrivKey); + } + } catch (PKCS11Exception | InvalidKeyException e) { + // Attempt failed, create a P11PrivateKey object. + } + } + switch (algorithm) { case "RSA": return P11RSAPrivateKeyInternal.of(session, keyID, algorithm, @@ -587,6 +607,70 @@ public BigInteger getModulus() { } } + // RSA CRT private key when in FIPS mode + private static final class P11RSAPrivateKeyFIPS extends P11Key + implements RSAPrivateCrtKey { + + private static final long serialVersionUID = 9215872438913515220L; + private final RSAPrivateCrtKeyImpl key; + + P11RSAPrivateKeyFIPS(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attrs, RSAPrivateCrtKeyImpl key) { + super(PRIVATE, session, keyID, algorithm, keyLength, attrs); + this.key = key; + } + + @Override + public String getFormat() { + return "PKCS#8"; + } + + @Override + synchronized byte[] getEncodedInternal() { + return key.getEncoded(); + } + + @Override + public BigInteger getModulus() { + return key.getModulus(); + } + + @Override + public BigInteger getPublicExponent() { + return key.getPublicExponent(); + } + + @Override + public BigInteger getPrivateExponent() { + return key.getPrivateExponent(); + } + + @Override + public BigInteger getPrimeP() { + return key.getPrimeP(); + } + + @Override + public BigInteger getPrimeQ() { + return key.getPrimeQ(); + } + + @Override + public BigInteger getPrimeExponentP() { + return key.getPrimeExponentP(); + } + + @Override + public BigInteger getPrimeExponentQ() { + return key.getPrimeExponentQ(); + } + + @Override + public BigInteger getCrtCoefficient() { + return key.getCrtCoefficient(); + } + } + // RSA CRT private key private static final class P11RSAPrivateKey extends P11RSAPrivateKeyInternal implements RSAPrivateCrtKey { @@ -664,6 +748,40 @@ public BigInteger getCrtCoefficient() { } } + // RSA non-CRT private key in FIPS mode + private static final class P11RSAPrivateNonCRTKeyFIPS extends P11Key + implements RSAPrivateKey { + + private static final long serialVersionUID = 1137764983777411481L; + private final RSAPrivateKey key; + + P11RSAPrivateNonCRTKeyFIPS(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, RSAPrivateKey key) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + this.key = key; + } + + @Override + public String getFormat() { + return "PKCS#8"; + } + + @Override + synchronized byte[] getEncodedInternal() { + return key.getEncoded(); + } + + @Override + public BigInteger getModulus() { + return key.getModulus(); + } + + @Override + public BigInteger getPrivateExponent() { + return key.getPrivateExponent(); + } + } + // RSA non-CRT private key private static final class P11RSAPrivateNonCRTKey extends P11RSAPrivateKeyInternal implements RSAPrivateKey { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 0f65a8b3221..b884108ae06 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11; import java.io.*; @@ -30,8 +36,14 @@ import java.security.*; import java.security.interfaces.*; +import java.util.function.Consumer; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.crypto.interfaces.*; +import javax.crypto.spec.IvParameterSpec; import javax.security.auth.Subject; import javax.security.auth.login.LoginException; @@ -43,13 +55,14 @@ import com.sun.crypto.provider.ChaCha20Poly1305Parameters; import jdk.internal.misc.InnocuousThread; +import openj9.internal.security.FIPSConfigurator; import sun.security.util.Debug; import sun.security.util.ResourcesMgr; import static sun.security.util.SecurityConstants.PROVIDER_VER; import static sun.security.util.SecurityProviderConstants.getAliases; import sun.security.pkcs11.Secmod.*; - +import sun.security.pkcs11.TemplateManager; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*; @@ -96,6 +109,11 @@ public final class SunPKCS11 extends AuthProvider { static NativeResourceCleaner cleaner; + // This is the SunPKCS11 provider instance + // there can only be a single PKCS11 provider in + // FIPS mode. + static SunPKCS11 mysunpkcs11; + Token getToken() { return token; } @@ -384,6 +402,29 @@ private static T checkNull(T obj) { if (nssModule != null) { nssModule.setProvider(this); } + + // When FIPS mode is enabled, configure p11 object to FIPS mode + // and pass the parent object so it can callback. + if (FIPSConfigurator.enableFIPS()) { + if (debug != null) { + System.out.println("FIPS mode in SunPKCS11"); + } + + @SuppressWarnings("unchecked") + Consumer consumer = (Consumer) p11; + consumer.accept(this); + mysunpkcs11 = this; + + Session session = null; + try { + session = token.getOpSession(); + p11.C_Login(session.id(), CKU_USER, new char[] {}); + } catch (PKCS11Exception e) { + throw e; + } finally { + token.releaseSession(session); + } + } } catch (Exception e) { if (config.getHandleStartupErrors() == Config.ERR_IGNORE_ALL) { throw new UnsupportedOperationException @@ -416,6 +457,94 @@ public int hashCode() { return System.identityHashCode(this); } + byte[] exportKey(long hSession, CK_ATTRIBUTE[] attributes, long keyId) throws PKCS11Exception { + // Generating the secret key that will be used for wrapping and unwrapping. + CK_ATTRIBUTE[] wrapKeyAttributes = token.getAttributes(TemplateManager.O_GENERATE, CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3) }); + Session wrapKeyGenSession = token.getObjSession(); + P11Key wrapKey; + + try { + long genKeyId = token.p11.C_GenerateKey(wrapKeyGenSession.id(), new CK_MECHANISM(CKM_AES_KEY_GEN), wrapKeyAttributes); + wrapKey = (P11Key)P11Key.secretKey(wrapKeyGenSession, genKeyId, "AES", 256 >> 3, null); + } catch (PKCS11Exception e) { + throw e; + } finally { + token.releaseSession(wrapKeyGenSession); + } + + // Wrapping the private key inside the PKCS11 device using the generated secret key. + CK_MECHANISM wrapMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, new byte[16]); + long wrapKeyId = wrapKey.getKeyID(); + byte[] wrappedKeyBytes = token.p11.C_WrapKey(hSession, wrapMechanism, wrapKeyId, keyId); + + // Unwrapping to obtain the private key. + byte[] unwrappedKeyBytes; + try { + Cipher unwrapCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + unwrapCipher.init(Cipher.DECRYPT_MODE, wrapKey, new IvParameterSpec((byte[])wrapMechanism.pParameter), null); + unwrappedKeyBytes = unwrapCipher.doFinal(wrappedKeyBytes); + return unwrappedKeyBytes; + } catch (NoSuchPaddingException | NoSuchAlgorithmException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException e) { + throw new PKCS11Exception(0x00000005L, null); + } finally { + wrapKey.releaseKeyID(); + } + } + + public long importKey(long hSession, CK_ATTRIBUTE[] attributes) throws PKCS11Exception { + long keyClass = 0; + long keyType = 0; + byte[] keyBytes = null; + // Extract key information. + for (CK_ATTRIBUTE attr : attributes) { + if (attr.type == CKA_CLASS) { + keyClass = attr.getLong(); + } + if (attr.type == CKA_KEY_TYPE) { + keyType = attr.getLong(); + } + if (attr.type == CKA_VALUE) { + keyBytes = attr.getByteArray(); + } + } + + if ((keyClass == CKO_SECRET_KEY) && (keyBytes != null) && (keyBytes.length > 0)) { + // Generate key used for wrapping and unwrapping of the secret key. + CK_ATTRIBUTE[] wrapKeyAttributes = token.getAttributes(TemplateManager.O_GENERATE, CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3)}); + Session wrapKeyGenSession = token.getObjSession(); + P11Key wrapKey; + + try { + long keyId = token.p11.C_GenerateKey(wrapKeyGenSession.id(), new CK_MECHANISM(CKM_AES_KEY_GEN), wrapKeyAttributes); + wrapKey = (P11Key)P11Key.secretKey(wrapKeyGenSession, keyId, "AES", 256 >> 3, null); + } catch (PKCS11Exception e) { + throw e; + } finally { + token.releaseSession(wrapKeyGenSession); + } + + long wrapKeyId = wrapKey.getKeyID(); + try { + // Wrap the external secret key. + CK_MECHANISM wrapMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, new byte[16]); + Cipher wrapCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + wrapCipher.init(Cipher.ENCRYPT_MODE, wrapKey, new IvParameterSpec((byte[])wrapMechanism.pParameter), null); + byte[] wrappedBytes = wrapCipher.doFinal(keyBytes); + + // Unwrap the secret key. + CK_ATTRIBUTE[] unwrapAttributes = token.getAttributes(TemplateManager.O_IMPORT, keyClass, keyType, attributes); + return token.p11.C_UnwrapKey(hSession, wrapMechanism, wrapKeyId, wrappedBytes, unwrapAttributes); + } catch (PKCS11Exception | NoSuchPaddingException | NoSuchAlgorithmException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException e) { + throw new PKCS11Exception(0x00000005L, null); + } finally { + wrapKey.releaseKeyID(); + } + } else { + // Unsupported key type or invalid bytes. + throw new PKCS11Exception(0x00000005L, null); + } + } + private static final class Descriptor { final String type; final String algorithm; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java index 4b06daaf264..b1984f5015d 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java @@ -45,11 +45,18 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11.wrapper; import java.io.File; import java.io.IOException; import java.util.*; +import java.util.function.Consumer; import java.security.AccessController; import java.security.PrivilegedAction; @@ -57,6 +64,7 @@ import sun.security.util.Debug; import sun.security.pkcs11.P11Util; +import sun.security.pkcs11.SunPKCS11; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*; @@ -140,6 +148,43 @@ public static void loadNative() { private static final Map moduleMap = new HashMap(); + static boolean isKey(CK_ATTRIBUTE[] attrs) { + for (CK_ATTRIBUTE attr : attrs) { + if ((attr.type == CKA_CLASS) && (attr.getLong() == CKO_SECRET_KEY)) { + return true; + } + } + return false; + } + + // This is the SunPKCS11 provider instance + // there can only be a single PKCS11 provider in + // FIPS mode. + private static SunPKCS11 mysunpkcs11; + + private static final class InnerPKCS11 extends PKCS11 implements Consumer { + InnerPKCS11(String pkcs11ModulePath, String functionListName) throws IOException { + super(pkcs11ModulePath, functionListName); + } + + // Set PKCS11 instance to FIPS mode, called by SunPKCS11 provider. + @Override + public void accept(SunPKCS11 sunpkcs11) { + mysunpkcs11 = sunpkcs11; + } + + // Overriding the JNI method C_CreateObject so that first check if FIPS mode is on and the object is a + // secret key, in which case invoke the importKey method in SunPKCS11 provider to import the secret key + // into the PKCS11 device. + @Override + public synchronized long C_CreateObject(long hSession, CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + if ((mysunpkcs11 != null) && isKey(pTemplate)) { + return mysunpkcs11.importKey(hSession, pTemplate); + } + return super.C_CreateObject(hSession, pTemplate); + } + } + /** * Connects to the PKCS#11 driver given. The filename must contain the * path, if the driver is not in the system's search path. @@ -183,7 +228,7 @@ public static synchronized PKCS11 getInstance(String pkcs11ModulePath, if (pkcs11 == null) { if ((pInitArgs != null) && ((pInitArgs.flags & CKF_OS_LOCKING_OK) != 0)) { - pkcs11 = new PKCS11(pkcs11ModulePath, functionList); + pkcs11 = new InnerPKCS11(pkcs11ModulePath, functionList); } else { pkcs11 = new SynchronizedPKCS11(pkcs11ModulePath, functionList); } @@ -1758,6 +1803,9 @@ public synchronized void C_Logout(long hSession) throws PKCS11Exception { public synchronized long C_CreateObject(long hSession, CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + if ((mysunpkcs11 != null) && isKey(pTemplate)) { + return mysunpkcs11.importKey(hSession, pTemplate); + } return super.C_CreateObject(hSession, pTemplate); }