< 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 >