--- old/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java 2018-06-04 19:27:21.338685458 -0300 +++ new/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java 2018-06-04 19:27:21.189684399 -0300 @@ -29,7 +29,6 @@ import java.lang.ref.*; import java.math.BigInteger; import java.util.*; - import java.security.*; import java.security.interfaces.*; import java.security.spec.*; @@ -45,6 +44,8 @@ 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; @@ -84,7 +85,7 @@ 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; @@ -95,8 +96,19 @@ // 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; @@ -119,13 +131,143 @@ 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(); @@ -243,7 +385,12 @@ Session tempSession = null; try { tempSession = token.getOpSession(); - token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes); + this.incNativeKeyRef(); + try { + token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes); + } finally { + this.decNativeKeyRef(); + } } catch (PKCS11Exception e) { throw new ProviderException(e); } finally { @@ -287,42 +434,44 @@ } 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); @@ -330,7 +479,7 @@ } 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), @@ -338,7 +487,7 @@ }); 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": @@ -368,20 +517,20 @@ } 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); @@ -395,8 +544,8 @@ 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() { @@ -413,8 +562,8 @@ 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(); @@ -440,8 +589,13 @@ CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE), }; - token.p11.C_GetAttributeValue - (tempSession.id(), keyID, attributes); + 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); @@ -463,8 +617,9 @@ 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; } @@ -485,8 +640,8 @@ 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) { @@ -572,8 +727,8 @@ 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(); @@ -625,8 +780,8 @@ 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(); @@ -681,8 +836,8 @@ 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(); @@ -744,8 +899,8 @@ 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(); @@ -802,8 +957,8 @@ 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(); @@ -884,8 +1039,8 @@ 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(); @@ -971,8 +1126,8 @@ 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(); @@ -1027,8 +1182,8 @@ 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(); @@ -1126,7 +1281,7 @@ } // 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 { @@ -1160,21 +1315,32 @@ SessionKeyRef(P11Key key , long keyID, Session session) { super(key, refQueue); - this.keyID = keyID; - this.session = session; - this.session.addObject(); + 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(); + } + } + private void disposeNative(Session s) throws PKCS11Exception { - session.token.p11.C_DestroyObject(s.id(), keyID); + 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) {