Skip to content

Commit

Permalink
Merge pull request #662 from WilburZjh/pkcs12ks
Browse files Browse the repository at this point in the history
PKCS#12 file-based Keystore support for FIPS mode
  • Loading branch information
keithc-ca authored Jun 7, 2023
2 parents 03e9f40 + 48b3165 commit fd547bb
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved
* ===========================================================================
*/

package com.sun.crypto.provider;

Expand Down Expand Up @@ -113,7 +118,7 @@ private static byte[] getPasswordBytes(char[] passwd) {
} else if (keyLength < 0) {
throw new InvalidKeySpecException("Key length is negative");
}
this.prf = Mac.getInstance(prfAlgo, SunJCE.getInstance());
this.prf = Mac.getInstance(prfAlgo);
this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength);
} catch (NoSuchAlgorithmException nsae) {
// not gonna happen; re-throw just in case
Expand Down
3 changes: 2 additions & 1 deletion src/java.base/share/conf/security/java.security
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ RestrictedSecurity1.jce.provider.2 = SUN [{CertificateFactory, X.509, Implemente
{CertStore, com.sun.security.IndexedCollection, ImplementedIn=Software}, \
{Policy, JavaPolicy, *}, {Configuration, JavaLoginConfig, *}, \
{CertPathBuilder, PKIX, ValidationAlgorithm=RFC5280:ImplementedIn=Software}, \
{CertPathValidator, PKIX, ValidationAlgorithm=RFC5280:ImplementedIn=Software}]
{CertPathValidator, PKIX, ValidationAlgorithm=RFC5280:ImplementedIn=Software}, \
{KeyStore, PKCS12, *}]
RestrictedSecurity1.jce.provider.3 = SunEC [{KeyFactory, EC, ImplementedIn=Software: \
SupportedKeyClasses=java.security.interfaces.ECPublicKey|java.security.interfaces.ECPrivateKey: \
KeySize=256}, {AlgorithmParameters, EC, *}]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import static sun.security.pkcs11.TemplateManager.O_GENERATE;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;

import sun.security.util.Debug;
import sun.security.util.DerValue;
import sun.security.util.Length;
import sun.security.util.ECUtil;
Expand All @@ -77,6 +78,8 @@ abstract class P11Key implements Key, Length {

private static final long serialVersionUID = -2575874101938349339L;

private static final Debug debug = Debug.getInstance("p11key");

private final static String PUBLIC = "public";
private final static String PRIVATE = "private";
private final static String SECRET = "secret";
Expand Down Expand Up @@ -384,21 +387,40 @@ 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.

boolean keySensitive = (attributes[0].getBoolean() ||
attributes[1].getBoolean() || !attributes[2].getBoolean());

if (keySensitive && (SunPKCS11.mysunpkcs11 != null) && "RSA".equals(algorithm)) {
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 (debug != null) {
debug.println("Attempt failed, creating a P11PrivateKey object for RSA");
}
}
}

if (keySensitive && (SunPKCS11.mysunpkcs11 != null) && "EC".equals(algorithm)) {
try {
byte[] key = SunPKCS11.mysunpkcs11.exportKey(session.id(), attributes, keyID);
ECPrivateKey ecPrivKey = ECUtil.decodePKCS8ECPrivateKey(key);
return new P11ECPrivateKeyFIPS(session, keyID, algorithm, keyLength, attributes, ecPrivKey);
} catch (PKCS11Exception | InvalidKeySpecException e) {
// Attempt failed, create a P11PrivateKey object.
if (debug != null) {
debug.println("Attempt failed, creating a P11PrivateKey object for EC");
}
}
}

if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
return new P11PrivateKey
(session, keyID, algorithm, keyLength, attributes);
Expand Down Expand Up @@ -1126,6 +1148,39 @@ public boolean equals(Object obj) {
}
}

// EC private key when in FIPS mode
private static final class P11ECPrivateKeyFIPS extends P11Key
implements ECPrivateKey {
private static final long serialVersionUID = -7786054399510515515L;
private final ECPrivateKey key;

P11ECPrivateKeyFIPS(Session session, long keyID, String algorithm,
int keyLength, CK_ATTRIBUTE[] attributes, ECPrivateKey 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 getS() {
return key.getS();
}

@Override
public ECParameterSpec getParams() {
return key.getParams();
}
}

private static final class P11ECPrivateKey extends P11Key
implements ECPrivateKey {
private static final long serialVersionUID = -7786054399510515515L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@

import java.io.*;
import java.util.*;
import java.math.BigInteger;

import java.security.*;
import java.security.interfaces.*;
import java.security.spec.InvalidKeySpecException;
import java.util.function.Consumer;

import javax.crypto.BadPaddingException;
Expand All @@ -56,13 +58,16 @@

import jdk.internal.misc.InnocuousThread;
import openj9.internal.security.RestrictedSecurity;
import sun.security.rsa.RSAUtil.KeyType;
import sun.security.util.Debug;
import sun.security.util.ECUtil;
import sun.security.util.ResourcesMgr;
import static sun.security.util.SecurityConstants.PROVIDER_VER;

import sun.security.pkcs11.Secmod.*;
import sun.security.pkcs11.TemplateManager;
import sun.security.pkcs11.wrapper.*;
import sun.security.rsa.RSAPrivateCrtKeyImpl;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;

/**
Expand Down Expand Up @@ -524,10 +529,17 @@ byte[] exportKey(long hSession, CK_ATTRIBUTE[] attributes, long keyId) throws PK
}
}

private static BigInteger getBigIntegerOrZero(Map<Long, CK_ATTRIBUTE> ckAttrsMap, long attributeType) {
CK_ATTRIBUTE attribute = ckAttrsMap.get(attributeType);
return (attribute != null) ? attribute.getBigInteger() : BigInteger.ZERO;
}

public long importKey(long hSession, CK_ATTRIBUTE[] attributes) throws PKCS11Exception {
long keyClass = 0;
long keyType = 0;
byte[] keyBytes = null;
Map<Long, CK_ATTRIBUTE> ckAttrsMap = new HashMap<>();

// Extract key information.
for (CK_ATTRIBUTE attr : attributes) {
if (attr.type == CKA_CLASS) {
Expand All @@ -536,12 +548,60 @@ public long importKey(long hSession, CK_ATTRIBUTE[] attributes) throws PKCS11Exc
if (attr.type == CKA_KEY_TYPE) {
keyType = attr.getLong();
}
if (attr.type == CKA_VALUE) {
keyBytes = attr.getByteArray();
ckAttrsMap.put(attr.type, attr);
}

if (keyClass == CKO_PRIVATE_KEY) {
if (keyType == CKK_RSA) {
try {
keyBytes = RSAPrivateCrtKeyImpl.newKey(
KeyType.RSA,
null,
getBigIntegerOrZero(ckAttrsMap, CKA_MODULUS),
getBigIntegerOrZero(ckAttrsMap, CKA_PUBLIC_EXPONENT),
getBigIntegerOrZero(ckAttrsMap, CKA_PRIVATE_EXPONENT),
getBigIntegerOrZero(ckAttrsMap, CKA_PRIME_1),
getBigIntegerOrZero(ckAttrsMap, CKA_PRIME_2),
getBigIntegerOrZero(ckAttrsMap, CKA_EXPONENT_1),
getBigIntegerOrZero(ckAttrsMap, CKA_EXPONENT_2),
getBigIntegerOrZero(ckAttrsMap, CKA_COEFFICIENT)
).getEncoded();
} catch (InvalidKeyException e) {
throw new PKCS11Exception(CKR_GENERAL_ERROR);
}
} else if (keyType == CKK_EC) {
CK_ATTRIBUTE ckaECParams = ckAttrsMap.get(CKA_EC_PARAMS);
if (ckaECParams == null) {
throw new PKCS11Exception(CKR_GENERAL_ERROR);
}
try {
keyBytes = ECUtil.generateECPrivateKey(
ckAttrsMap.getOrDefault(CKA_VALUE, new CK_ATTRIBUTE(CKA_VALUE, BigInteger.ZERO)).getBigInteger(),
ECUtil.getECParameterSpec(
Security.getProvider("SunEC"),
ckaECParams.getByteArray())
).getEncoded();
// If key is private and of EC type, NSS may require CKA_NETSCAPE_DB
// attribute to unwrap it. Otherwise, C_UnwrapKey will produce an Exception.
if (token.config.getNssNetscapeDbWorkaround() && !ckAttrsMap.containsKey(CKA_NETSCAPE_DB)) {
ckAttrsMap.put(CKA_NETSCAPE_DB, new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO));
}
} catch (IOException | InvalidKeySpecException e) {
throw new PKCS11Exception(CKR_GENERAL_ERROR);
}
}
} else if (keyClass == CKO_SECRET_KEY) {
CK_ATTRIBUTE ckaValue = ckAttrsMap.get(CKA_VALUE);
if (ckaValue == null) {
throw new PKCS11Exception(CKR_GENERAL_ERROR);
}
keyBytes = ckaValue.getByteArray();
}

if ((keyClass == CKO_SECRET_KEY) && (keyBytes != null) && (keyBytes.length > 0)) {
if ((keyBytes != null) && (keyBytes.length > 0)
&& ((keyClass == CKO_SECRET_KEY)
|| ((keyClass == CKO_PRIVATE_KEY) && ((keyType == CKK_EC) || (keyType == CKK_RSA))))
) {
// 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();
Expand All @@ -565,7 +625,12 @@ public long importKey(long hSession, CK_ATTRIBUTE[] attributes) throws PKCS11Exc
byte[] wrappedBytes = wrapCipher.doFinal(keyBytes);

// Unwrap the secret key.
CK_ATTRIBUTE[] unwrapAttributes = token.getAttributes(TemplateManager.O_IMPORT, keyClass, keyType, attributes);
// Need additional attributes for EC private key.
CK_ATTRIBUTE[] unwrapAttributes = token.getAttributes(
TemplateManager.O_IMPORT,
keyClass,
keyType,
ckAttrsMap.values().toArray(new CK_ATTRIBUTE[ckAttrsMap.size()]));
return token.p11.C_UnwrapKey(hSession, wrapMechanism, wrapKeyId, wrappedBytes, unwrapAttributes);
} catch (PKCS11Exception | NoSuchPaddingException | NoSuchAlgorithmException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException e) {
throw new PKCS11Exception(CKR_GENERAL_ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,25 @@ public static void loadNative() {
new HashMap<String,PKCS11>();

static boolean isKey(CK_ATTRIBUTE[] attrs) {
boolean isPrivateKey = false;
boolean hasKey = false;

for (CK_ATTRIBUTE attr : attrs) {
if ((attr.type == CKA_CLASS) && (attr.getLong() == CKO_SECRET_KEY)) {
return true;
if (attr.type == CKA_CLASS) {
if (attr.getLong() == CKO_SECRET_KEY) {
return true;
} else if (attr.getLong() == CKO_PRIVATE_KEY) {
isPrivateKey = true;
}
} else if (attr.type == CKA_KEY_TYPE) {
hasKey = true;
if (!((attr.getLong() == CKK_RSA) || (attr.getLong() == CKK_EC))) {
isPrivateKey = false;
}
}
}
return false;

return isPrivateKey && hasKey;
}

// This is the SunPKCS11 provider instance
Expand Down

0 comments on commit fd547bb

Please sign in to comment.