diff --git a/jdk/src/share/classes/sun/security/pkcs11/P11Key.java b/jdk/src/share/classes/sun/security/pkcs11/P11Key.java index bcffaf0ae60..cbe4bd2cd41 100644 --- a/jdk/src/share/classes/sun/security/pkcs11/P11Key.java +++ b/jdk/src/share/classes/sun/security/pkcs11/P11Key.java @@ -23,6 +23,12 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11; import java.io.*; @@ -366,6 +372,21 @@ static PrivateKey privateKey(Session session, long keyID, String algorithm, new CK_ATTRIBUTE(CKA_SENSITIVE), new CK_ATTRIBUTE(CKA_EXTRACTABLE), }); + if ((SunPKCS11.mysunpkcs11 != null) && "RSA".equals(algorithm)) { + if (attributes[0].getBoolean() || attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) { + try { + byte[] key = SunPKCS11.mysunpkcs11.exportKey(session.id(), attributes, keyID); + RSAPrivateKey rsaPrivKey = RSAPrivateCrtKeyImpl.newKey(key); + if (rsaPrivKey instanceof RSAPrivateCrtKeyImpl) { + return new P11RSAPrivateKeyFIPS(session, keyID, algorithm, keyLength, attributes, (RSAPrivateCrtKeyImpl)rsaPrivKey); + } else { + return new P11RSAPrivateNonCRTKeyFIPS(session, keyID, algorithm, keyLength, attributes, rsaPrivKey); + } + } catch (PKCS11Exception | InvalidKeyException e) { + // Attempt failed, create a P11PrivateKey object. + } + } + } if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) { return new P11PrivateKey (session, keyID, algorithm, keyLength, attributes); @@ -500,6 +521,70 @@ public int getMinorVersion() { } } + // 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 P11Key implements RSAPrivateCrtKey { @@ -588,6 +673,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 P11Key implements RSAPrivateKey { diff --git a/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java b/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java index fedcd7743ef..b83d92db62d 100644 --- a/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -23,15 +23,29 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11; import java.io.*; import java.util.*; import java.security.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; 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; @@ -42,10 +56,13 @@ import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.TextOutputCallback; +import openj9.internal.security.FIPSConfigurator; + import sun.security.util.Debug; import sun.security.util.ResourcesMgr; import sun.security.pkcs11.Secmod.*; +import sun.security.pkcs11.TemplateManager; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -89,6 +106,11 @@ public final class SunPKCS11 extends AuthProvider { private TokenPoller poller; + // This is the SunPKCS11 provider instance + // there can only be a single PKCS11 provider in + // FIPS mode. + static SunPKCS11 mysunpkcs11; + Token getToken() { return token; } @@ -368,6 +390,29 @@ public SunPKCS11(String configName, InputStream configStream) { 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 @@ -404,6 +449,94 @@ private static String[] s(String ...aliases) { return aliases; } + 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(CKR_GENERAL_ERROR); + } finally { + wrapKey.releaseKeyID(); + } + } + + long importKey(long hSession, CK_ATTRIBUTE[] attributes) throws PKCS11Exception { + long unwrappedKeyId, keyClass = 0, 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); + unwrappedKeyId = token.p11.C_UnwrapKey(hSession, wrapMechanism, wrapKeyId, wrappedBytes, unwrapAttributes); + } catch (PKCS11Exception | NoSuchPaddingException | NoSuchAlgorithmException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException e) { + throw new PKCS11Exception(CKR_GENERAL_ERROR); + } finally { + wrapKey.releaseKeyID(); + } + } else { + // Unsupported key type or invalid bytes. + throw new PKCS11Exception(CKR_GENERAL_ERROR); + } + return Long.valueOf(unwrappedKeyId); + } + private static final class Descriptor { final String type; final String algorithm; diff --git a/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11.java b/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11.java index 2e42d1d9fb0..2d26d29870c 100644 --- a/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11.java +++ b/jdk/src/share/classes/sun/security/pkcs11/wrapper/PKCS11.java @@ -45,15 +45,26 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved + * =========================================================================== + */ + package sun.security.pkcs11.wrapper; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.*; +import java.util.function.Consumer; import java.security.AccessController; import java.security.PrivilegedAction; +import sun.security.pkcs11.SunPKCS11; + import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** @@ -131,6 +142,48 @@ 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. + public synchronized long C_CreateObject(long hSession, CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + if ((mysunpkcs11 != null) && isKey(pTemplate)) { + try { + Method method = mysunpkcs11.getClass().getDeclaredMethod("importKey", long.class, CK_ATTRIBUTE[].class); + method.setAccessible(true); + return (Long)method.invoke(mysunpkcs11, hSession, pTemplate); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new PKCS11Exception(CKR_GENERAL_ERROR); + } + } + 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. @@ -154,7 +207,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); } @@ -1687,6 +1740,15 @@ 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)) { + try { + Method method = mysunpkcs11.getClass().getMethod("importKey", long.class, CK_ATTRIBUTE[].class); + method.setAccessible(true); + return (Long)method.invoke(mysunpkcs11, hSession, pTemplate); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new PKCS11Exception(CKR_GENERAL_ERROR); + } + } return super.C_CreateObject(hSession, pTemplate); }