Skip to content

Commit

Permalink
Support export/import of cryptographic keys in FIPS Mode
Browse files Browse the repository at this point in the history
Signed-off-by: Tao Liu <tao.liu@ibm.com>
  • Loading branch information
taoliult committed Jan 19, 2023
1 parent 40656c4 commit 24cc8ab
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/java.base/share/classes/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
* ===========================================================================
*/

Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/java.base/share/lib/security/default.policy
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
118 changes: 118 additions & 0 deletions src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
* ===========================================================================
*/

package sun.security.pkcs11;

import java.io.*;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,27 @@
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
* ===========================================================================
*/

package sun.security.pkcs11;

import java.io.*;
import java.util.*;

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;
Expand All @@ -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.*;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -384,6 +402,29 @@ private static <T> 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<SunPKCS11> consumer = (Consumer<SunPKCS11>) 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
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 24cc8ab

Please sign in to comment.