Skip to content
This repository has been archived by the owner on Dec 13, 2024. It is now read-only.

Support export/import of cryptographic keys in FIPS Mode #63

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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