< prev index next >
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java
Print this page
@@ -27,11 +27,10 @@
import java.io.*;
import java.lang.ref.*;
import java.math.BigInteger;
import java.util.*;
-
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import javax.crypto.*;
@@ -43,10 +42,12 @@
import sun.security.rsa.RSAPrivateCrtKeyImpl;
import sun.security.internal.interfaces.TlsMasterSecret;
import sun.security.pkcs11.wrapper.*;
+
+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;
@@ -82,23 +83,34 @@
// algorithm name, returned by getAlgorithm(), etc.
final String algorithm;
// key id
- final long keyID;
+ long keyID;
// effective key length of the key, e.g. 56 for a DES key
final int keyLength;
// flags indicating whether the key is a token object, sensitive, extractable
final boolean tokenObject, sensitive, extractable;
// phantom reference notification clean up for session keys
private final SessionKeyRef sessionKeyRef;
+ // Native key objects may be temporal: only exist if in use.
+ private boolean tmpNativeKey;
+
+ private int nativeKeyRefCounting;
+
+ private static long nativeKeyWrapperKeyID = -1;
+
+ // Buffer to hold native key objects info in Java heap in order to
+ // re-create them if needed.
+ private byte[] nativeKeyInfo;
+
P11Key(String type, Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
this.type = type;
this.token = session.token;
this.keyID = keyID;
this.algorithm = algorithm;
this.keyLength = keyLength;
@@ -117,17 +129,147 @@
}
}
this.tokenObject = tokenObject;
this.sensitive = sensitive;
this.extractable = extractable;
+ if (token.tokenInfo.label[0] == 'N'
+ && token.tokenInfo.label[1] == 'S'
+ && token.tokenInfo.label[2] == 'S') {
+ this.tmpNativeKey = tmpNativeKey;
+ } else {
+ // Disabled if token is not NSS
+ this.tmpNativeKey = false;
+ }
+ // Disable temporary native keys if the key is not extractable or sensitive
+ // (when key is a token "secret").
+ // The foundation for extracting wrapped sensitive keys is in
+ // place but it's currently disabled for token secrets because:
+ // 1) there is a bug in NSSDB for persisting CKO_SECRET_KEY
+ // in legacy DB (key3.db) which corrupts the key; and,
+ // 2) sqlite DB (key4.db) is still not supported by SunPKCS11 (see JDK-8165996).
+ if (!extractable || (sensitive && type.equals(SECRET) && tokenObject)) {
+ this.tmpNativeKey = false;
+ }
+ if (this.tmpNativeKey) {
+ nativeKeyRefCounting = 0;
+ try {
+ if (sensitive && nativeKeyWrapperKeyID == -1) {
+ synchronized(this) {
+ if (nativeKeyWrapperKeyID == -1) {
+ // Create a global wrapping/unwrapping key
+ CK_ATTRIBUTE[] wrappingAttributes = token.getAttributes
+ (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 wrappingSession = null;
+ try {
+ wrappingSession = token.getObjSession();
+ nativeKeyWrapperKeyID = token.p11.C_GenerateKey
+ (wrappingSession.id(), new CK_MECHANISM(CKM_AES_KEY_GEN), wrappingAttributes);
+ } finally {
+ token.releaseSession(wrappingSession);
+ }
+ }
+ }
+ }
+ nativeKeyInfo = token.p11.getNativeKeyInfo(session.id(), keyID, nativeKeyWrapperKeyID);
+ if (nativeKeyInfo != null && nativeKeyInfo.length > 0) {
+ destroyNativeKey();
+ // If extracted, it's not a token object anymore
+ tokenObject = false;
+ } else {
+ this.tmpNativeKey = false;
+ }
+ } catch (PKCS11Exception e) {
+ // Unexpected behaviour when trying to manage the key life-time.
+ // Don't manage the native key.
+ this.tmpNativeKey = false;
+ }
+ }
if (tokenObject == false) {
- sessionKeyRef = new SessionKeyRef(this, keyID, session);
+ sessionKeyRef = new SessionKeyRef(this, this.keyID, session);
} else {
sessionKeyRef = null;
}
}
+ public void incNativeKeyRef() throws PKCS11Exception {
+ if (tmpNativeKey) {
+ synchronized(this) {
+ if (++nativeKeyRefCounting == 1 && nativeKeyInfo != null) {
+ // Create a Native Key
+ Session session = null;
+ try {
+ session = token.getObjSession();
+ keyID = token.p11.createNativeKey(session.id(), nativeKeyInfo, nativeKeyWrapperKeyID);
+ if (sessionKeyRef != null) {
+ sessionKeyRef.setKeyIDAndSession(keyID, session);
+ }
+ } catch (PKCS11Exception e) {
+ nativeKeyRefCounting--;
+ throw e;
+ } finally {
+ token.releaseSession(session);
+ }
+ }
+ }
+ }
+ }
+
+ public void makeNativeKeyPersistent() throws PKCS11Exception {
+ if (tmpNativeKey) {
+ synchronized(this) {
+ if (nativeKeyRefCounting == 0) {
+ this.incNativeKeyRef();
+ }
+
+ // This write is not sync protected because reads are done out of the
+ // synchronization block. It's just a best-effort.
+ tmpNativeKey = false;
+
+ // This write is sync protected and provides the real guarantee to avoid
+ // native key creation or destruction.
+ nativeKeyInfo = null;
+ }
+ }
+ }
+
+ public void decNativeKeyRef() {
+ if (tmpNativeKey) {
+ synchronized(this) {
+ if(--nativeKeyRefCounting == 0 && nativeKeyInfo != null) {
+ try {
+ destroyNativeKey();
+ } catch (PKCS11Exception e) {
+ // This is a best-effort.
+ }
+ }
+ if (nativeKeyRefCounting < 0) {
+ nativeKeyRefCounting = 0;
+ }
+ }
+ }
+ }
+
+ private void destroyNativeKey() throws PKCS11Exception {
+ // There is no synchronization needed between SessionKeyRef.disposeNative and
+ // this method. When SessionKeyRef.disposeNative method is executed, no P11Key
+ // object exists.
+ Session session = null;
+ try {
+ session = token.getObjSession();
+ token.p11.C_DestroyObject(session.id(), keyID);
+ } finally {
+ if (sessionKeyRef != null) {
+ sessionKeyRef.setKeyIDAndSession(0, null);
+ }
+ keyID = 0;
+ token.releaseSession(session);
+ }
+ }
+
// see JCA spec
public final String getAlgorithm() {
token.ensureValid();
return algorithm;
}
@@ -241,11 +383,16 @@
void fetchAttributes(CK_ATTRIBUTE[] attributes) {
Session tempSession = null;
try {
tempSession = token.getOpSession();
+ this.incNativeKeyRef();
+ try {
token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes);
+ } finally {
+ this.decNativeKeyRef();
+ }
} catch (PKCS11Exception e) {
throw new ProviderException(e);
} finally {
token.releaseSession(tempSession);
}
@@ -285,62 +432,64 @@
}
return desiredAttributes;
}
static SecretKey secretKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_TOKEN),
new CK_ATTRIBUTE(CKA_SENSITIVE),
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
});
- return new P11SecretKey(session, keyID, algorithm, keyLength, attributes);
+ return new P11SecretKey(session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
+ int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
+ boolean tmpNativeKey) {
attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_TOKEN),
new CK_ATTRIBUTE(CKA_SENSITIVE),
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
});
return new P11TlsMasterSecretKey
- (session, keyID, algorithm, keyLength, attributes, major, minor);
+ (session, keyID, algorithm, keyLength, attributes, major, minor,
+ tmpNativeKey);
}
// we assume that all components of public keys are always accessible
static PublicKey publicKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
switch (algorithm) {
case "RSA":
return new P11RSAPublicKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
case "DSA":
return new P11DSAPublicKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
case "DH":
return new P11DHPublicKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
case "EC":
return new P11ECPublicKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
default:
throw new ProviderException
("Unknown public key algorithm " + algorithm);
}
}
static PrivateKey privateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_TOKEN),
new CK_ATTRIBUTE(CKA_SENSITIVE),
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
});
if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
return new P11PrivateKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
} else {
switch (algorithm) {
case "RSA":
// In order to decide if this is RSA CRT key, we first query
// and see if all extra CRT attributes are available.
@@ -366,24 +515,24 @@
// ignore, assume not available
crtKey = false;
}
if (crtKey) {
return new P11RSAPrivateKey
- (session, keyID, algorithm, keyLength, attributes, attrs2);
+ (session, keyID, algorithm, keyLength, attributes, attrs2, tmpNativeKey);
} else {
return new P11RSAPrivateNonCRTKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
case "DSA":
return new P11DSAPrivateKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
case "DH":
return new P11DHPrivateKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
case "EC":
return new P11ECPrivateKey
- (session, keyID, algorithm, keyLength, attributes);
+ (session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
default:
throw new ProviderException
("Unknown private key algorithm " + algorithm);
}
}
@@ -393,12 +542,12 @@
private static final class P11PrivateKey extends P11Key
implements PrivateKey {
private static final long serialVersionUID = -2138581185214187615L;
P11PrivateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
// XXX temporary encoding for serialization purposes
public String getFormat() {
token.ensureValid();
return null;
@@ -411,12 +560,12 @@
private static class P11SecretKey extends P11Key implements SecretKey {
private static final long serialVersionUID = -7828241727014329084L;
private volatile byte[] encoded;
P11SecretKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(SECRET, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(SECRET, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
public String getFormat() {
token.ensureValid();
if (sensitive || (extractable == false)) {
return null;
@@ -438,12 +587,17 @@
try {
tempSession = token.getOpSession();
CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_VALUE),
};
+ this.incNativeKeyRef();
+ try {
token.p11.C_GetAttributeValue
(tempSession.id(), keyID, attributes);
+ } finally {
+ this.decNativeKeyRef();
+ }
b = attributes[0].getByteArray();
} catch (PKCS11Exception e) {
throw new ProviderException(e);
} finally {
token.releaseSession(tempSession);
@@ -461,12 +615,13 @@
implements TlsMasterSecret {
private static final long serialVersionUID = -1318560923770573441L;
private final int majorVersion, minorVersion;
P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
- super(session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor,
+ boolean tmpNativeKey) {
+ super(session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
this.majorVersion = major;
this.minorVersion = minor;
}
public int getMajorVersion() {
return majorVersion;
@@ -483,12 +638,12 @@
private static final long serialVersionUID = 9215872438913515220L;
private BigInteger n, e, d, p, q, pe, qe, coeff;
private byte[] encoded;
P11RSAPrivateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attrs);
+ int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attrs, tmpNativeKey);
for (CK_ATTRIBUTE a : crtAttrs) {
if (a.type == CKA_PUBLIC_EXPONENT) {
e = a.getBigInteger();
} else if (a.type == CKA_PRIME_1) {
@@ -570,12 +725,12 @@
private static final long serialVersionUID = 1137764983777411481L;
private BigInteger n, d;
private byte[] encoded;
P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (n != null) {
return;
@@ -623,12 +778,12 @@
implements RSAPublicKey {
private static final long serialVersionUID = -826726289023854455L;
private BigInteger n, e;
private byte[] encoded;
P11RSAPublicKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (n != null) {
return;
@@ -679,12 +834,12 @@
private BigInteger y;
private DSAParams params;
private byte[] encoded;
P11DSAPublicKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (y != null) {
return;
@@ -742,12 +897,12 @@
private BigInteger x;
private DSAParams params;
private byte[] encoded;
P11DSAPrivateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (x != null) {
return;
@@ -800,12 +955,12 @@
private BigInteger x;
private DHParameterSpec params;
private byte[] encoded;
P11DHPrivateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (x != null) {
return;
@@ -882,12 +1037,12 @@
private BigInteger y;
private DHParameterSpec params;
private byte[] encoded;
P11DHPublicKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (y != null) {
return;
@@ -969,12 +1124,12 @@
private BigInteger s;
private ECParameterSpec params;
private byte[] encoded;
P11ECPrivateKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PRIVATE, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (s != null) {
return;
@@ -1025,12 +1180,12 @@
private ECPoint w;
private ECParameterSpec params;
private byte[] encoded;
P11ECPublicKey(Session session, long keyID, String algorithm,
- int keyLength, CK_ATTRIBUTE[] attributes) {
- super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+ int keyLength, CK_ATTRIBUTE[] attributes, boolean tmpNativeKey) {
+ super(PUBLIC, session, keyID, algorithm, keyLength, attributes, tmpNativeKey);
}
private synchronized void fetchValues() {
token.ensureValid();
if (w != null) {
return;
@@ -1124,11 +1279,11 @@
if (next == null) {
break;
}
// If the token is still valid, try to remove the object
- if (next.session.token.isValid()) {
+ if (next.session != null && next.session.token.isValid()) {
// If this key's token is the same as the previous key, the
// same session can be used for C_DestroyObject.
try {
if (next.session.token != tkn || sess == null) {
// Release session if not using previous token
@@ -1158,25 +1313,36 @@
private long keyID;
private Session session;
SessionKeyRef(P11Key key , long keyID, Session session) {
super(key, refQueue);
+ setKeyIDAndSession(keyID, session);
+ refList.add(this);
+ drainRefQueueBounded();
+ }
+
+ public void setKeyIDAndSession(long keyID, Session session) {
+ if (this.session != null) {
+ this.session.removeObject();
+ }
this.keyID = keyID;
this.session = session;
+ if (this.session != null) {
this.session.addObject();
- refList.add(this);
- drainRefQueueBounded();
+ }
}
private void disposeNative(Session s) throws PKCS11Exception {
+ if(this.session != null) {
session.token.p11.C_DestroyObject(s.id(), keyID);
}
+ }
private void dispose() {
refList.remove(this);
this.clear();
- session.removeObject();
+ setKeyIDAndSession(0, null);
}
public int compareTo(SessionKeyRef other) {
if (this.keyID == other.keyID) {
return 0;
< prev index next >