6913047: Long term memory leak when using PKCS11 and JCE exceeds 32 bit process address space
authormbalao
Thu, 29 Nov 2018 13:36:23 -0300
changeset 53257 5170dc2bcf64
parent 53256 bd8df96decba
child 53258 e348b0160d61
6913047: Long term memory leak when using PKCS11 and JCE exceeds 32 bit process address space Summary: Extract cryptographic keys within NSS PKCS11 software tokens for memory management purposes. Reviewed-by: valeriep
src/java.base/share/lib/security/default.policy
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DHKeyFactory.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DSAKeyFactory.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Digest.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSACipher.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Signature.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsMasterSecretGenerator.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsPrfGenerator.java
src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java
src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c
src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h
src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h
--- a/src/java.base/share/lib/security/default.policy	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/java.base/share/lib/security/default.policy	Thu Nov 29 13:36:23 2018 -0300
@@ -127,6 +127,7 @@
     permission java.lang.RuntimePermission "accessClassInPackage.sun.nio.ch";
     permission java.lang.RuntimePermission "loadLibrary.j2pkcs11";
     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";
     permission java.util.PropertyPermission "os.arch", "read";
     permission java.util.PropertyPermission "jdk.crypto.KeyAgreement.legacyKDF", "read";
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -400,19 +400,35 @@
         }
     }
 
-    private void cancelOperation() {
-        if (initialized == false) {
+    // reset the states to the pre-initialized values
+    // need to be called after doFinal or prior to re-init
+    private void reset(boolean doCancel) {
+        if (!initialized) {
             return;
         }
-
-        if ((session == null) || (token.explicitCancel == false)) {
-            return;
-        }
+        initialized = false;
         try {
-            if (session.hasObjects() == false) {
-                session = token.killSession(session);
+            if (session == null) {
                 return;
-            } else {
+            }
+            if (doCancel && token.explicitCancel) {
+                cancelOperation();
+            }
+        } finally {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
+            bytesBuffered = 0;
+            padBufferLen = 0;
+        }
+    }
+
+    private void cancelOperation() {
+        token.ensureValid();
+        if (session.hasObjects() == false) {
+            session = token.killSession(session);
+            return;
+        } else {
+            try {
                 // cancel operation by finishing it
                 int bufLen = doFinalLength(0);
                 byte[] buffer = new byte[bufLen];
@@ -421,40 +437,46 @@
                 } else {
                     token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
                 }
+            } catch (PKCS11Exception e) {
+                throw new ProviderException("Cancel failed", e);
             }
-        } catch (PKCS11Exception e) {
-            throw new ProviderException("Cancel failed", e);
         }
     }
 
     private void ensureInitialized() throws PKCS11Exception {
-        if (initialized == false) {
+        if (!initialized) {
             initialize();
         }
     }
 
     private void initialize() throws PKCS11Exception {
-        if (session == null) {
-            session = token.getOpSession();
+        if (p11Key == null) {
+            throw new ProviderException(
+                    "Operation cannot be performed without"
+                    + " calling engineInit first");
         }
-        CK_MECHANISM mechParams = (blockMode == MODE_CTR?
-            new CK_MECHANISM(mechanism, new CK_AES_CTR_PARAMS(iv)) :
-            new CK_MECHANISM(mechanism, iv));
-
+        token.ensureValid();
+        long p11KeyID = p11Key.getKeyID();
         try {
+            if (session == null) {
+                session = token.getOpSession();
+            }
+            CK_MECHANISM mechParams = (blockMode == MODE_CTR?
+                    new CK_MECHANISM(mechanism, new CK_AES_CTR_PARAMS(iv)) :
+                    new CK_MECHANISM(mechanism, iv));
             if (encrypt) {
-                token.p11.C_EncryptInit(session.id(), mechParams, p11Key.keyID);
+                token.p11.C_EncryptInit(session.id(), mechParams, p11KeyID);
             } else {
-                token.p11.C_DecryptInit(session.id(), mechParams, p11Key.keyID);
+                token.p11.C_DecryptInit(session.id(), mechParams, p11KeyID);
             }
-        } catch (PKCS11Exception ex) {
-            // release session when initialization failed
+        } catch (PKCS11Exception e) {
+            p11Key.releaseKeyID();
             session = token.releaseSession(session);
-            throw ex;
+            throw e;
         }
+        initialized = true;
         bytesBuffered = 0;
         padBufferLen = 0;
-        initialized = true;
     }
 
     // if update(inLen) is called, how big does the output buffer have to be?
@@ -485,18 +507,6 @@
         return result;
     }
 
-    // reset the states to the pre-initialized values
-    private void reset(boolean doCancel) {
-        if (doCancel) cancelOperation();
-
-        initialized = false;
-        bytesBuffered = 0;
-        padBufferLen = 0;
-        if (session != null) {
-            session = token.releaseSession(session);
-        }
-    }
-
     // see JCE spec
     protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
         try {
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DHKeyFactory.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DHKeyFactory.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -221,7 +221,12 @@
                 new CK_ATTRIBUTE(CKA_PRIME),
                 new CK_ATTRIBUTE(CKA_BASE),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
             KeySpec spec = new DHPublicKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger(),
@@ -243,7 +248,12 @@
                 new CK_ATTRIBUTE(CKA_PRIME),
                 new CK_ATTRIBUTE(CKA_BASE),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
             KeySpec spec = new DHPrivateKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger(),
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DSAKeyFactory.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DSAKeyFactory.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -218,7 +218,12 @@
                 new CK_ATTRIBUTE(CKA_SUBPRIME),
                 new CK_ATTRIBUTE(CKA_BASE),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
             KeySpec spec = new DSAPublicKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger(),
@@ -242,7 +247,12 @@
                 new CK_ATTRIBUTE(CKA_SUBPRIME),
                 new CK_ATTRIBUTE(CKA_BASE),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
             KeySpec spec = new DSAPrivateKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger(),
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Digest.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Digest.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -143,7 +143,8 @@
         token.ensureValid();
 
         if (session != null) {
-            if (state == S_INIT && token.explicitCancel == true) {
+            if (state == S_INIT && token.explicitCancel == true
+                    && session.hasObjects() == false) {
                 session = token.killSession(session);
             } else {
                 session = token.releaseSession(session);
@@ -254,6 +255,7 @@
         }
 
         fetchSession();
+        long p11KeyID = p11Key.getKeyID();
         try {
             if (state == S_BUFFERED) {
                 token.p11.C_DigestInit(session.id(), mechanism);
@@ -264,10 +266,12 @@
                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
                 bufOfs = 0;
             }
-            token.p11.C_DigestKey(session.id(), p11Key.keyID);
+            token.p11.C_DigestKey(session.id(), p11KeyID);
         } catch (PKCS11Exception e) {
             engineReset();
             throw new ProviderException("update(SecretKey) failed", e);
+        } finally {
+            p11Key.releaseKeyID();
         }
     }
 
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -120,6 +120,7 @@
             throw new IllegalStateException("Not initialized correctly");
         }
         Session session = null;
+        long privKeyID = privateKey.getKeyID();
         try {
             session = token.getOpSession();
             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
@@ -131,8 +132,8 @@
             attributes = token.getAttributes
                 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
             long keyID = token.p11.C_DeriveKey(session.id(),
-                new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
-                attributes);
+                    new CK_MECHANISM(mechanism, ckParams), privKeyID,
+                    attributes);
             attributes = new CK_ATTRIBUTE[] {
                 new CK_ATTRIBUTE(CKA_VALUE)
             };
@@ -143,6 +144,7 @@
         } catch (PKCS11Exception e) {
             throw new ProviderException("Could not derive key", e);
         } finally {
+            privateKey.releaseKeyID();
             publicValue = null;
             token.releaseSession(session);
         }
@@ -182,6 +184,7 @@
         }
         long keyType = CKK_GENERIC_SECRET;
         Session session = null;
+        long privKeyID = privateKey.getKeyID();
         try {
             session = token.getObjSession();
             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
@@ -193,8 +196,8 @@
             attributes = token.getAttributes
                 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
             long keyID = token.p11.C_DeriveKey(session.id(),
-                new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
-                attributes);
+                    new CK_MECHANISM(mechanism, ckParams), privKeyID,
+                    attributes);
             CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
                 new CK_ATTRIBUTE(CKA_VALUE_LEN),
             };
@@ -206,6 +209,7 @@
         } catch (PKCS11Exception e) {
             throw new InvalidKeyException("Could not derive key", e);
         } finally {
+            privateKey.releaseKeyID();
             publicValue = null;
             token.releaseSession(session);
         }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -290,13 +290,16 @@
                 new CK_ATTRIBUTE(CKA_EC_POINT),
                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
             try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
                 ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
                 ECPoint point = decodePoint(attributes[0].getByteArray(), params.getCurve());
                 return keySpec.cast(new ECPublicKeySpec(point, params));
             } catch (IOException e) {
                 throw new InvalidKeySpecException("Could not parse key", e);
+            } finally {
+                key.releaseKeyID();
             }
         } else { // X.509 handled in superclass
             throw new InvalidKeySpecException("Only ECPublicKeySpec and "
@@ -312,13 +315,16 @@
                 new CK_ATTRIBUTE(CKA_VALUE),
                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
             try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
                 ECParameterSpec params = decodeParameters(attributes[1].getByteArray());
                 return keySpec.cast(
                     new ECPrivateKeySpec(attributes[0].getBigInteger(), params));
             } catch (IOException e) {
                 throw new InvalidKeySpecException("Could not parse key", e);
+            } finally {
+                key.releaseKeyID();
             }
         } else { // PKCS#8 handled in superclass
             throw new InvalidKeySpecException("Only ECPrivateKeySpec "
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java	Thu Nov 29 13:36:23 2018 -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,12 +44,15 @@
 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;
 import sun.security.util.ECUtil;
+import sun.security.jca.JCAUtil;
 
 /**
  * Key implementation classes.
@@ -83,23 +85,38 @@
     // algorithm name, returned by getAlgorithm(), etc.
     final String algorithm;
 
-    // key id
-    final 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;
+    private final NativeKeyHolder keyIDHolder;
+
+    private static final boolean DISABLE_NATIVE_KEYS_EXTRACTION;
+
+    /**
+     * {@systemProperty sun.security.pkcs11.disableKeyExtraction} property
+     * indicating whether or not cryptographic keys within tokens are
+     * extracted to a Java byte array for memory management purposes.
+     *
+     * Key extraction affects NSS PKCS11 library only.
+     *
+     */
+    static {
+        PrivilegedAction<String> getKeyExtractionProp =
+                () -> System.getProperty(
+                        "sun.security.pkcs11.disableKeyExtraction", "false");
+        String disableKeyExtraction =
+                AccessController.doPrivileged(getKeyExtractionProp);
+        DISABLE_NATIVE_KEYS_EXTRACTION =
+                "true".equalsIgnoreCase(disableKeyExtraction);
+    }
 
     P11Key(String type, Session session, long keyID, String algorithm,
             int keyLength, CK_ATTRIBUTE[] attributes) {
         this.type = type;
         this.token = session.token;
-        this.keyID = keyID;
         this.algorithm = algorithm;
         this.keyLength = keyLength;
         boolean tokenObject = false;
@@ -119,11 +136,21 @@
         this.tokenObject = tokenObject;
         this.sensitive = sensitive;
         this.extractable = extractable;
-        if (tokenObject == false) {
-            sessionKeyRef = new SessionKeyRef(this, keyID, session);
-        } else {
-            sessionKeyRef = null;
-        }
+        char[] tokenLabel = this.token.tokenInfo.label;
+        boolean isNSS = (tokenLabel[0] == 'N' && tokenLabel[1] == 'S'
+                && tokenLabel[2] == 'S');
+        boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&
+                extractable && !tokenObject);
+        this.keyIDHolder = new NativeKeyHolder(this, keyID, session, extractKeyInfo,
+            tokenObject);
+    }
+
+    public long getKeyID() {
+        return keyIDHolder.getKeyID();
+    }
+
+    public void releaseKeyID() {
+        keyIDHolder.releaseKeyID();
     }
 
     // see JCA spec
@@ -208,8 +235,7 @@
         token.ensureValid();
         String s1 = token.provider.getName() + " " + algorithm + " " + type
                 + " key, " + keyLength + " bits";
-        s1 += " (id " + keyID + ", "
-                + (tokenObject ? "token" : "session") + " object";
+        s1 += (tokenObject ? "token" : "session") + " object";
         if (isPublic()) {
             s1 += ")";
         } else {
@@ -241,12 +267,15 @@
 
     void fetchAttributes(CK_ATTRIBUTE[] attributes) {
         Session tempSession = null;
+        long keyID = this.getKeyID();
         try {
             tempSession = token.getOpSession();
-            token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes);
+            token.p11.C_GetAttributeValue(tempSession.id(), keyID,
+                        attributes);
         } catch (PKCS11Exception e) {
             throw new ProviderException(e);
         } finally {
+            this.releaseKeyID();
             token.releaseSession(tempSession);
         }
     }
@@ -293,7 +322,8 @@
             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);
     }
 
     static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
@@ -303,8 +333,9 @@
             new CK_ATTRIBUTE(CKA_SENSITIVE),
             new CK_ATTRIBUTE(CKA_EXTRACTABLE),
         });
-        return new P11TlsMasterSecretKey
-                (session, keyID, algorithm, keyLength, attributes, major, minor);
+        return new P11TlsMasterSecretKey(
+                session, keyID, algorithm, keyLength, attributes, major,
+                minor);
     }
 
     // we assume that all components of public keys are always accessible
@@ -312,17 +343,17 @@
             int keyLength, CK_ATTRIBUTE[] attributes) {
         switch (algorithm) {
             case "RSA":
-                return new P11RSAPublicKey
-                    (session, keyID, algorithm, keyLength, attributes);
+                return new P11RSAPublicKey(session, keyID, algorithm,
+                        keyLength, attributes);
             case "DSA":
-                return new P11DSAPublicKey
-                    (session, keyID, algorithm, keyLength, attributes);
+                return new P11DSAPublicKey(session, keyID, algorithm,
+                        keyLength, attributes);
             case "DH":
-                return new P11DHPublicKey
-                    (session, keyID, algorithm, keyLength, attributes);
+                return new P11DHPublicKey(session, keyID, algorithm,
+                        keyLength, attributes);
             case "EC":
-                return new P11ECPublicKey
-                    (session, keyID, algorithm, keyLength, attributes);
+                return new P11ECPublicKey(session, keyID, algorithm,
+                        keyLength, attributes);
             default:
                 throw new ProviderException
                     ("Unknown public key algorithm " + algorithm);
@@ -367,21 +398,21 @@
                         crtKey = false;
                     }
                     if (crtKey) {
-                        return new P11RSAPrivateKey
-                                (session, keyID, algorithm, keyLength, attributes, attrs2);
+                        return new P11RSAPrivateKey(session, keyID, algorithm,
+                                keyLength, attributes, attrs2);
                     } else {
-                        return new P11RSAPrivateNonCRTKey
-                                (session, keyID, algorithm, keyLength, attributes);
+                        return new P11RSAPrivateNonCRTKey(session, keyID,
+                                algorithm, keyLength, attributes);
                     }
                 case "DSA":
-                    return new P11DSAPrivateKey
-                            (session, keyID, algorithm, keyLength, attributes);
+                    return new P11DSAPrivateKey(session, keyID, algorithm,
+                            keyLength, attributes);
                 case "DH":
-                    return new P11DHPrivateKey
-                            (session, keyID, algorithm, keyLength, attributes);
+                    return new P11DHPrivateKey(session, keyID, algorithm,
+                            keyLength, attributes);
                 case "EC":
-                    return new P11ECPrivateKey
-                            (session, keyID, algorithm, keyLength, attributes);
+                    return new P11ECPrivateKey(session, keyID, algorithm,
+                            keyLength, attributes);
                 default:
                     throw new ProviderException
                             ("Unknown private key algorithm " + algorithm);
@@ -435,17 +466,19 @@
                     b = encoded;
                     if (b == null) {
                         Session tempSession = null;
+                        long keyID = this.getKeyID();
                         try {
                             tempSession = token.getOpSession();
                             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
                                 new CK_ATTRIBUTE(CKA_VALUE),
                             };
                             token.p11.C_GetAttributeValue
-                                (tempSession.id(), keyID, attributes);
+                                    (tempSession.id(), keyID, attributes);
                             b = attributes[0].getByteArray();
                         } catch (PKCS11Exception e) {
                             throw new ProviderException(e);
                         } finally {
+                            this.releaseKeyID();
                             token.releaseSession(tempSession);
                         }
                         encoded = b;
@@ -1100,6 +1133,153 @@
     }
 }
 
+final class NativeKeyHolder {
+
+    private static long nativeKeyWrapperKeyID = 0;
+    private static CK_MECHANISM nativeKeyWrapperMechanism = null;
+
+    private final P11Key p11Key;
+    private final byte[] nativeKeyInfo;
+
+    // destroyed and recreated when refCount toggles to 1
+    private long keyID;
+
+    private boolean isTokenObject;
+
+    // phantom reference notification clean up for session keys
+    private SessionKeyRef ref;
+
+    private int refCount;
+
+    NativeKeyHolder(P11Key p11Key, long keyID, Session keySession,
+            boolean extractKeyInfo, boolean isTokenObject) {
+        this.p11Key = p11Key;
+        this.keyID = keyID;
+        this.refCount = -1;
+        byte[] ki = null;
+        if (isTokenObject) {
+            this.ref = null;
+        } else {
+            this.ref = new SessionKeyRef(p11Key, keyID, keySession);
+
+            // Try extracting key info, if any error, disable it
+            Token token = p11Key.token;
+            if (extractKeyInfo) {
+                try {
+                    if (p11Key.sensitive && nativeKeyWrapperKeyID == 0) {
+                        synchronized(NativeKeyHolder.class) {
+                            // 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);
+                                byte[] iv = new byte[16];
+                                JCAUtil.getSecureRandom().nextBytes(iv);
+                                nativeKeyWrapperMechanism = new CK_MECHANISM
+                                    (CKM_AES_CBC_PAD, iv);
+                            } catch (PKCS11Exception e) {
+                                // best effort
+                            } finally {
+                                token.releaseSession(wrappingSession);
+                            }
+                        }
+                    }
+                    Session opSession = null;
+                    try {
+                        opSession = token.getOpSession();
+                        ki = p11Key.token.p11.getNativeKeyInfo(opSession.id(),
+                            keyID, nativeKeyWrapperKeyID, nativeKeyWrapperMechanism);
+                    } catch (PKCS11Exception e) {
+                        // best effort
+                    } finally {
+                        token.releaseSession(opSession);
+                    }
+                } catch (PKCS11Exception e) {
+                    // best effort
+                }
+            }
+        }
+        this.nativeKeyInfo = ((ki == null || ki.length == 0)? null : ki);
+    }
+
+    long getKeyID() throws ProviderException {
+        if (this.nativeKeyInfo != null) {
+            synchronized(this.nativeKeyInfo) {
+                if (this.refCount == -1) {
+                    this.refCount = 0;
+                }
+                int cnt = (this.refCount)++;
+                if (keyID == 0) {
+                    if (cnt != 0) {
+                        throw new RuntimeException(
+                                "Error: null keyID with non-zero refCount " + cnt);
+                    }
+                    if (this.ref != null)  {
+                        throw new RuntimeException(
+                                "Error: null keyID with non-null session ref");
+                    }
+                    Token token = p11Key.token;
+                    // Create keyID using nativeKeyInfo
+                    Session session = null;
+                    try {
+                        session = token.getObjSession();
+                        this.keyID = token.p11.createNativeKey(session.id(),
+                                nativeKeyInfo, nativeKeyWrapperKeyID, nativeKeyWrapperMechanism);
+                        this.ref = new SessionKeyRef(p11Key, this.keyID, session);
+                    } catch (PKCS11Exception e) {
+                        this.refCount--;
+                        throw new ProviderException("Error recreating native key", e);
+                    } finally {
+                        token.releaseSession(session);
+                    }
+                } else {
+                    if (cnt < 0) {
+                        throw new RuntimeException("ERROR: negative refCount");
+                    }
+                }
+            }
+        }
+        return keyID;
+    }
+
+    void releaseKeyID() {
+        if (this.nativeKeyInfo != null) {
+            synchronized(this.nativeKeyInfo) {
+                if (this.refCount == -1) {
+                    throw new RuntimeException("Error: miss match getKeyID call");
+                }
+                int cnt = --(this.refCount);
+                if (cnt == 0) {
+                    // destroy
+                    if (this.keyID == 0) {
+                        throw new RuntimeException("ERROR: null keyID can't be destroyed");
+                    }
+
+                    if (this.ref == null) {
+                        throw new RuntimeException("ERROR: null session ref can't be disposed");
+                    }
+                    // destroy
+                    this.keyID = 0;
+                    this.ref = this.ref.dispose();
+                } else {
+                    if (cnt < 0) {
+                        // should never happen as we start count at 1 and pair get/release calls
+                        throw new RuntimeException("wrong refCount value: " + cnt);
+                    }
+                }
+            }
+        }
+    }
+}
+
 /*
  * NOTE: Must use PhantomReference here and not WeakReference
  * otherwise the key maybe cleared before other objects which
@@ -1117,64 +1297,50 @@
     }
 
     private static void drainRefQueueBounded() {
-        Session sess = null;
-        Token tkn = null;
         while (true) {
             SessionKeyRef next = (SessionKeyRef) refQueue.poll();
             if (next == null) {
                 break;
             }
-
-            // If the token is still valid, try to remove the object
-            if (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
-                        if (tkn != null && sess != null) {
-                            tkn.releaseSession(sess);
-                            sess = null;
-                        }
-
-                        tkn = next.session.token;
-                        sess = tkn.getOpSession();
-                    }
-                    next.disposeNative(sess);
-                } catch (PKCS11Exception e) {
-                    // ignore
-                }
-            }
-            // Regardless of native results, dispose of java references
             next.dispose();
         }
-
-        if (tkn != null && sess != null) {
-            tkn.releaseSession(sess);
-        }
     }
 
-    // handle to the native key
-    private long keyID;
-    private Session session;
+    // handle to the native key and the session it is generated under
+    private final long keyID;
+    private final Session session;
 
-    SessionKeyRef(P11Key key , long keyID, Session session) {
-        super(key, refQueue);
+    SessionKeyRef(P11Key p11Key, long keyID, Session session) {
+        super(p11Key, refQueue);
+        if (session == null) {
+            throw new ProviderException("key must be associated with a session");
+        }
         this.keyID = keyID;
         this.session = session;
         this.session.addObject();
+
         refList.add(this);
         drainRefQueueBounded();
     }
 
-    private void disposeNative(Session s) throws PKCS11Exception {
-        session.token.p11.C_DestroyObject(s.id(), keyID);
-    }
-
-    private void dispose() {
+    SessionKeyRef dispose() {
+        Token token = session.token;
+        // If the token is still valid, try to remove the key object
+        if (token.isValid()) {
+            Session s = null;
+            try {
+                s = token.getOpSession();
+                token.p11.C_DestroyObject(s.id(), keyID);
+            } catch (PKCS11Exception e) {
+                // best effort
+            } finally {
+                token.releaseSession(s);
+            }
+        }
         refList.remove(this);
         this.clear();
         session.removeObject();
+        return null;
     }
 
     public int compareTo(SessionKeyRef other) {
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -201,6 +201,7 @@
             throw new IllegalStateException("Not initialized correctly");
         }
         Session session = null;
+        long privKeyID = privateKey.getKeyID();
         try {
             session = token.getOpSession();
             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
@@ -210,8 +211,9 @@
             attributes = token.getAttributes
                 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
             long keyID = token.p11.C_DeriveKey(session.id(),
-                new CK_MECHANISM(mechanism, publicValue), privateKey.keyID,
-                attributes);
+                    new CK_MECHANISM(mechanism, publicValue), privKeyID,
+                    attributes);
+
             attributes = new CK_ATTRIBUTE[] {
                 new CK_ATTRIBUTE(CKA_VALUE)
             };
@@ -237,6 +239,7 @@
         } catch (PKCS11Exception e) {
             throw new ProviderException("Could not derive key", e);
         } finally {
+            privateKey.releaseKeyID();
             publicValue = null;
             token.releaseSession(session);
         }
@@ -325,6 +328,7 @@
         }
         long keyType = CKK_GENERIC_SECRET;
         Session session = null;
+        long privKeyID = privateKey.getKeyID();
         try {
             session = token.getObjSession();
             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
@@ -334,8 +338,8 @@
             attributes = token.getAttributes
                 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
             long keyID = token.p11.C_DeriveKey(session.id(),
-                new CK_MECHANISM(mechanism, publicValue), privateKey.keyID,
-                attributes);
+                    new CK_MECHANISM(mechanism, publicValue), privKeyID,
+                    attributes);
             CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
                 new CK_ATTRIBUTE(CKA_VALUE_LEN),
             };
@@ -359,6 +363,7 @@
         } catch (PKCS11Exception e) {
             throw new InvalidKeyException("Could not derive key", e);
         } finally {
+            privateKey.releaseKeyID();
             publicValue = null;
             token.releaseSession(session);
         }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1485,6 +1485,7 @@
         }
     }
 
+    // retrieves the native key handle and either update it directly or make a copy
     private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
                 throws PKCS11Exception {
 
@@ -1492,23 +1493,22 @@
         // if session key, convert to token key.
 
         Session session = null;
+        long keyID = key.getKeyID();
         try {
             session = token.getOpSession();
             if (key.tokenObject == true) {
-
                 // token key - set new CKA_ID
 
                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
                                 new CK_ATTRIBUTE(CKA_ID, alias) };
                 token.p11.C_SetAttributeValue
-                                (session.id(), key.keyID, attrs);
+                                (session.id(), keyID, attrs);
                 if (debug != null) {
                     debug.println("updateP11Pkey set new alias [" +
                                 alias +
                                 "] for key entry");
                 }
             } else {
-
                 // session key - convert to token key and set CKA_ID
 
                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
@@ -1518,7 +1518,8 @@
                 if (attribute != null) {
                     attrs = addAttribute(attrs, attribute);
                 }
-                token.p11.C_CopyObject(session.id(), key.keyID, attrs);
+                // creates a new token key with the desired CKA_ID
+                token.p11.C_CopyObject(session.id(), keyID, attrs);
                 if (debug != null) {
                     debug.println("updateP11Pkey copied private session key " +
                                 "for [" +
@@ -1528,6 +1529,7 @@
             }
         } finally {
             token.releaseSession(session);
+            key.releaseKeyID();
         }
     }
 
@@ -1894,10 +1896,12 @@
             return attrs;
         }
         String alg = privateKey.getAlgorithm();
-        if (id && alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
+        if (alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
+            if (id) {
+                BigInteger n = ((RSAPublicKey)publicKey).getModulus();
+                attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
+            }
             // CKA_NETSCAPE_DB not needed for RSA public keys
-            BigInteger n = ((RSAPublicKey)publicKey).getModulus();
-            attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
         } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {
             BigInteger y = ((DSAPublicKey)publicKey).getY();
             if (id) {
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
 
 package sun.security.pkcs11;
 
-import java.util.*;
 import java.nio.ByteBuffer;
 
 import java.security.*;
@@ -54,27 +53,12 @@
  */
 final class P11Mac extends MacSpi {
 
-    /* unitialized, all fields except session have arbitrary values */
-    private final static int S_UNINIT   = 1;
-
-    /* session initialized, no data processed yet */
-    private final static int S_RESET    = 2;
-
-    /* session initialized, data processed */
-    private final static int S_UPDATE   = 3;
-
-    /* transitional state after doFinal() before we go to S_UNINIT */
-    private final static int S_DOFINAL  = 4;
-
     // token instance
     private final Token token;
 
     // algorithm name
     private final String algorithm;
 
-    // mechanism id
-    private final long mechanism;
-
     // mechanism object
     private final CK_MECHANISM ckMechanism;
 
@@ -87,8 +71,8 @@
     // associated session, if any
     private Session session;
 
-    // state, one of S_* above
-    private int state;
+    // initialization status
+    private boolean initialized;
 
     // one byte buffer for the update(byte) method, initialized on demand
     private byte[] oneByte;
@@ -98,7 +82,6 @@
         super();
         this.token = token;
         this.algorithm = algorithm;
-        this.mechanism = mechanism;
         Long params = null;
         switch ((int)mechanism) {
         case (int)CKM_MD5_HMAC:
@@ -131,47 +114,65 @@
             throw new ProviderException("Unknown mechanism: " + mechanism);
         }
         ckMechanism = new CK_MECHANISM(mechanism, params);
-        state = S_UNINIT;
-        initialize();
     }
 
-    private void ensureInitialized() throws PKCS11Exception {
-        token.ensureValid();
-        if (state == S_UNINIT) {
-            initialize();
+    // reset the states to the pre-initialized values
+    private void reset(boolean doCancel) {
+        if (!initialized) {
+            return;
+        }
+        initialized = false;
+        try {
+            if (session == null) {
+                return;
+            }
+            if (doCancel && token.explicitCancel) {
+                cancelOperation();
+            }
+        } finally {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
         }
     }
 
     private void cancelOperation() {
         token.ensureValid();
-        if (state == S_UNINIT) {
-            return;
-        }
-        state = S_UNINIT;
-        if ((session == null) || (token.explicitCancel == false)) {
+        if (session.hasObjects() == false) {
+            session = token.killSession(session);
             return;
+        } else {
+            try {
+                token.p11.C_SignFinal(session.id(), 0);
+            } catch (PKCS11Exception e) {
+                throw new ProviderException("Cancel failed", e);
+            }
         }
-        try {
-            token.p11.C_SignFinal(session.id(), 0);
-        } catch (PKCS11Exception e) {
-            throw new ProviderException("Cancel failed", e);
+    }
+
+    private void ensureInitialized() throws PKCS11Exception {
+        if (!initialized) {
+            initialize();
         }
     }
 
     private void initialize() throws PKCS11Exception {
-        if (state == S_RESET) {
-            return;
-        }
-        if (session == null) {
-            session = token.getOpSession();
+        if (p11Key == null) {
+            throw new ProviderException(
+                    "Operation cannot be performed without calling engineInit first");
         }
-        if (p11Key != null) {
-            token.p11.C_SignInit
-                (session.id(), ckMechanism, p11Key.keyID);
-            state = S_RESET;
-        } else {
-            state = S_UNINIT;
+        token.ensureValid();
+        long p11KeyID = p11Key.getKeyID();
+        try {
+            if (session == null) {
+                session = token.getOpSession();
+            }
+            token.p11.C_SignInit(session.id(), ckMechanism, p11KeyID);
+        } catch (PKCS11Exception e) {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
+            throw e;
         }
+        initialized = true;
     }
 
     // see JCE spec
@@ -181,18 +182,7 @@
 
     // see JCE spec
     protected void engineReset() {
-        // the framework insists on calling reset() after doFinal(),
-        // but we prefer to take care of reinitialization ourselves
-        if (state == S_DOFINAL) {
-            state = S_UNINIT;
-            return;
-        }
-        cancelOperation();
-        try {
-            initialize();
-        } catch (PKCS11Exception e) {
-            throw new ProviderException("reset() failed, ", e);
-        }
+        reset(true);
     }
 
     // see JCE spec
@@ -202,7 +192,7 @@
             throw new InvalidAlgorithmParameterException
                 ("Parameters not supported");
         }
-        cancelOperation();
+        reset(true);
         p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm);
         try {
             initialize();
@@ -215,13 +205,12 @@
     protected byte[] engineDoFinal() {
         try {
             ensureInitialized();
-            byte[] mac = token.p11.C_SignFinal(session.id(), 0);
-            state = S_DOFINAL;
-            return mac;
+            return token.p11.C_SignFinal(session.id(), 0);
         } catch (PKCS11Exception e) {
+            reset(true);
             throw new ProviderException("doFinal() failed", e);
         } finally {
-            session = token.releaseSession(session);
+            reset(false);
         }
     }
 
@@ -239,7 +228,6 @@
         try {
             ensureInitialized();
             token.p11.C_SignUpdate(session.id(), 0, b, ofs, len);
-            state = S_UPDATE;
         } catch (PKCS11Exception e) {
             throw new ProviderException("update() failed", e);
         }
@@ -261,7 +249,6 @@
             int ofs = byteBuffer.position();
             token.p11.C_SignUpdate(session.id(), addr + ofs, null, 0, len);
             byteBuffer.position(ofs + len);
-            state = S_UPDATE;
         } catch (PKCS11Exception e) {
             throw new ProviderException("update() failed", e);
         }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSACipher.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSACipher.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -196,7 +196,7 @@
     }
 
     private void implInit(int opmode, Key key) throws InvalidKeyException {
-        cancelOperation();
+        reset(true);
         p11Key = P11KeyFactory.convertKey(token, key, algorithm);
         boolean encrypt;
         if (opmode == Cipher.ENCRYPT_MODE) {
@@ -241,80 +241,105 @@
         }
     }
 
-    private void cancelOperation() {
-        token.ensureValid();
-        if (initialized == false) {
+    // reset the states to the pre-initialized values
+    private void reset(boolean doCancel) {
+        if (!initialized) {
             return;
         }
         initialized = false;
-        if ((session == null) || (token.explicitCancel == false)) {
-            return;
+        try {
+            if (session == null) {
+                return;
+            }
+            if (doCancel && token.explicitCancel) {
+                cancelOperation();
+            }
+        } finally {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
         }
+    }
+
+    // should only called by reset as this method does not update other
+    // state variables such as "initialized"
+    private void cancelOperation() {
+        token.ensureValid();
         if (session.hasObjects() == false) {
             session = token.killSession(session);
             return;
-        }
-        try {
-            PKCS11 p11 = token.p11;
-            int inLen = maxInputSize;
-            int outLen = buffer.length;
-            switch (mode) {
-            case MODE_ENCRYPT:
-                p11.C_Encrypt
-                        (session.id(), buffer, 0, inLen, buffer, 0, outLen);
-                break;
-            case MODE_DECRYPT:
-                p11.C_Decrypt
-                        (session.id(), buffer, 0, inLen, buffer, 0, outLen);
-                break;
-            case MODE_SIGN:
-                byte[] tmpBuffer = new byte[maxInputSize];
-                p11.C_Sign
-                        (session.id(), tmpBuffer);
-                break;
-            case MODE_VERIFY:
-                p11.C_VerifyRecover
-                        (session.id(), buffer, 0, inLen, buffer, 0, outLen);
-                break;
-            default:
-                throw new ProviderException("internal error");
+        } else {
+            try {
+                PKCS11 p11 = token.p11;
+                int inLen = maxInputSize;
+                int outLen = buffer.length;
+                long sessId = session.id();
+                switch (mode) {
+                case MODE_ENCRYPT:
+                    p11.C_Encrypt(sessId, buffer, 0, inLen, buffer, 0, outLen);
+                    break;
+                case MODE_DECRYPT:
+                    p11.C_Decrypt(sessId, buffer, 0, inLen, buffer, 0, outLen);
+                    break;
+                case MODE_SIGN:
+                    byte[] tmpBuffer = new byte[maxInputSize];
+                    p11.C_Sign(sessId, tmpBuffer);
+                    break;
+                case MODE_VERIFY:
+                    p11.C_VerifyRecover(sessId, buffer, 0, inLen, buffer,
+                            0, outLen);
+                    break;
+                default:
+                    throw new ProviderException("internal error");
+                }
+            } catch (PKCS11Exception e) {
+                // XXX ensure this always works, ignore error
             }
-        } catch (PKCS11Exception e) {
-            // XXX ensure this always works, ignore error
         }
     }
 
     private void ensureInitialized() throws PKCS11Exception {
         token.ensureValid();
-        if (initialized == false) {
+        if (!initialized) {
             initialize();
         }
     }
 
     private void initialize() throws PKCS11Exception {
-        if (session == null) {
-            session = token.getOpSession();
+        if (p11Key == null) {
+            throw new ProviderException(
+                    "Operation cannot be performed without " +
+                    "calling engineInit first");
         }
-        PKCS11 p11 = token.p11;
-        CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism);
-        switch (mode) {
-        case MODE_ENCRYPT:
-            p11.C_EncryptInit(session.id(), ckMechanism, p11Key.keyID);
-            break;
-        case MODE_DECRYPT:
-            p11.C_DecryptInit(session.id(), ckMechanism, p11Key.keyID);
-            break;
-        case MODE_SIGN:
-            p11.C_SignInit(session.id(), ckMechanism, p11Key.keyID);
-            break;
-        case MODE_VERIFY:
-            p11.C_VerifyRecoverInit(session.id(), ckMechanism, p11Key.keyID);
-            break;
-        default:
-            throw new AssertionError("internal error");
+        long keyID = p11Key.getKeyID();
+        try {
+            if (session == null) {
+                session = token.getOpSession();
+            }
+            PKCS11 p11 = token.p11;
+            CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism);
+            switch (mode) {
+            case MODE_ENCRYPT:
+                p11.C_EncryptInit(session.id(), ckMechanism, keyID);
+                break;
+            case MODE_DECRYPT:
+                p11.C_DecryptInit(session.id(), ckMechanism, keyID);
+                break;
+            case MODE_SIGN:
+                p11.C_SignInit(session.id(), ckMechanism, keyID);
+                break;
+            case MODE_VERIFY:
+                p11.C_VerifyRecoverInit(session.id(), ckMechanism, keyID);
+                break;
+            default:
+                throw new AssertionError("internal error");
+            }
+            bufOfs = 0;
+            initialized = true;
+        } catch (PKCS11Exception e) {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
+            throw e;
         }
-        bufOfs = 0;
-        initialized = true;
     }
 
     private void implUpdate(byte[] in, int inOfs, int inLen) {
@@ -377,8 +402,7 @@
             throw (BadPaddingException)new BadPaddingException
                 ("doFinal() failed").initCause(e);
         } finally {
-            initialized = false;
-            session = token.releaseSession(session);
+            reset(false);
         }
     }
 
@@ -452,13 +476,17 @@
             }
         }
         Session s = null;
+        long p11KeyID = p11Key.getKeyID();
+        long sKeyID = sKey.getKeyID();
         try {
             s = token.getOpSession();
             return token.p11.C_WrapKey(s.id(), new CK_MECHANISM(mechanism),
-                p11Key.keyID, sKey.keyID);
+                    p11KeyID, sKeyID);
         } catch (PKCS11Exception e) {
             throw new InvalidKeyException("wrap() failed", e);
         } finally {
+            p11Key.releaseKeyID();
+            sKey.releaseKeyID();
             token.releaseSession(s);
         }
     }
@@ -518,6 +546,7 @@
         } else {
             Session s = null;
             SecretKey secretKey = null;
+            long p11KeyID = p11Key.getKeyID();
             try {
                 try {
                     s = token.getObjSession();
@@ -528,9 +557,10 @@
                         };
                     attributes = token.getAttributes(
                             O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
+
                     long keyID = token.p11.C_UnwrapKey(s.id(),
-                            new CK_MECHANISM(mechanism), p11Key.keyID,
-                            wrappedKey, attributes);
+                                    new CK_MECHANISM(mechanism), p11KeyID,
+                                    wrappedKey, attributes);
                     secretKey = P11Key.secretKey(s, keyID,
                             algorithm, 48 << 3, attributes);
                 } catch (PKCS11Exception e) {
@@ -554,6 +584,7 @@
 
                 return secretKey;
             } finally {
+                p11Key.releaseKeyID();
                 token.releaseSession(s);
             }
         }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java	Thu Nov 29 13:36:23 2018 -0300
@@ -263,7 +263,12 @@
                 new CK_ATTRIBUTE(CKA_MODULUS),
                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
             KeySpec spec = new RSAPublicKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger()
@@ -289,7 +294,13 @@
                 new CK_ATTRIBUTE(CKA_EXPONENT_2),
                 new CK_ATTRIBUTE(CKA_COEFFICIENT),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
+
             KeySpec spec = new RSAPrivateCrtKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger(),
@@ -307,7 +318,13 @@
                 new CK_ATTRIBUTE(CKA_MODULUS),
                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
             };
-            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            long keyID = key.getKeyID();
+            try {
+                token.p11.C_GetAttributeValue(session[0].id(), keyID, attributes);
+            } finally {
+                key.releaseKeyID();
+            }
+
             KeySpec spec = new RSAPrivateKeySpec(
                 attributes[0].getBigInteger(),
                 attributes[1].getBigInteger()
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -146,20 +146,24 @@
             P11Key p11Key = (P11Key)key;
             if (p11Key.token == token) {
                 if (extraAttrs != null) {
+                    P11Key newP11Key = null;
                     Session session = null;
+                    long p11KeyID = p11Key.getKeyID();
                     try {
                         session = token.getObjSession();
                         long newKeyID = token.p11.C_CopyObject(session.id(),
-                                p11Key.keyID, extraAttrs);
-                        p11Key = (P11Key) (P11Key.secretKey(session,
+                            p11KeyID, extraAttrs);
+                        newP11Key = (P11Key) (P11Key.secretKey(session,
                                 newKeyID, p11Key.algorithm, p11Key.keyLength,
                                 extraAttrs));
                     } catch (PKCS11Exception p11e) {
                         throw new InvalidKeyException
                                 ("Cannot duplicate the PKCS11 key", p11e);
                     } finally {
+                        p11Key.releaseKeyID();
                         token.releaseSession(session);
                     }
+                    p11Key = newP11Key;
                 }
                 return p11Key;
             }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Signature.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Signature.java	Thu Nov 29 13:36:23 2018 -0300
@@ -263,27 +263,33 @@
         }
     }
 
-    private void ensureInitialized() {
-        token.ensureValid();
-        if (initialized == false) {
-            initialize();
+    // reset the states to the pre-initialized values
+    private void reset(boolean doCancel) {
+
+        if (!initialized) {
+            return;
+        }
+        initialized = false;
+        try {
+            if (session == null) {
+                return;
+            }
+            if (doCancel && token.explicitCancel) {
+                cancelOperation();
+            }
+        } finally {
+            p11Key.releaseKeyID();
+            session = token.releaseSession(session);
         }
     }
 
     private void cancelOperation() {
+
         token.ensureValid();
-        if (initialized == false) {
-            return;
-        }
-        initialized = false;
-        if ((session == null) || (token.explicitCancel == false)) {
-            return;
-        }
         if (session.hasObjects() == false) {
             session = token.killSession(session);
             return;
-        }
-        try {
+        } else {
             // "cancel" operation by finishing it
             // XXX make sure all this always works correctly
             if (mode == M_SIGN) {
@@ -303,8 +309,8 @@
                     throw new ProviderException("cancel failed", e);
                 }
             } else { // M_VERIFY
+                byte[] signature;
                 try {
-                    byte[] signature;
                     if (keyAlgorithm.equals("DSA")) {
                         signature = new byte[40];
                     } else {
@@ -322,31 +328,48 @@
                         token.p11.C_Verify(session.id(), digest, signature);
                     }
                 } catch (PKCS11Exception e) {
-                    // will fail since the signature is incorrect
-                    // XXX check error code
+                    long errorCode = e.getErrorCode();
+                    if ((errorCode == CKR_SIGNATURE_INVALID) ||
+                        (errorCode == CKR_SIGNATURE_LEN_RANGE)) {
+                        // expected since signature is incorrect
+                        return;
+                    }
+                    throw new ProviderException("cancel failed", e);
                 }
             }
-        } finally {
-            session = token.releaseSession(session);
+        }
+    }
+
+    private void ensureInitialized() {
+
+        if (!initialized) {
+            initialize();
         }
     }
 
     // assumes current state is initialized == false
     private void initialize() {
+
+        if (p11Key == null) {
+            throw new ProviderException(
+                    "Operation cannot be performed without " +
+                    "calling engineInit first");
+        }
+        long keyID = p11Key.getKeyID();
         try {
+            token.ensureValid();
             if (session == null) {
                 session = token.getOpSession();
             }
             if (mode == M_SIGN) {
                 token.p11.C_SignInit(session.id(),
-                        new CK_MECHANISM(mechanism), p11Key.keyID);
+                        new CK_MECHANISM(mechanism), keyID);
             } else {
                 token.p11.C_VerifyInit(session.id(),
-                        new CK_MECHANISM(mechanism), p11Key.keyID);
+                        new CK_MECHANISM(mechanism), keyID);
             }
-            initialized = true;
         } catch (PKCS11Exception e) {
-            // release session when initialization failed
+            p11Key.releaseKeyID();
             session = token.releaseSession(session);
             throw new ProviderException("Initialization failed", e);
         }
@@ -356,6 +379,7 @@
                 md.reset();
             }
         }
+        initialized = true;
     }
 
     private void checkKeySize(String keyAlgo, Key key)
@@ -444,6 +468,7 @@
     @Override
     protected void engineInitVerify(PublicKey publicKey)
             throws InvalidKeyException {
+
         if (publicKey == null) {
             throw new InvalidKeyException("Key must not be null");
         }
@@ -451,7 +476,7 @@
         if (publicKey != p11Key) {
             checkKeySize(keyAlgorithm, publicKey);
         }
-        cancelOperation();
+        reset(true);
         mode = M_VERIFY;
         p11Key = P11KeyFactory.convertKey(token, publicKey, keyAlgorithm);
         initialize();
@@ -461,6 +486,7 @@
     @Override
     protected void engineInitSign(PrivateKey privateKey)
             throws InvalidKeyException {
+
         if (privateKey == null) {
             throw new InvalidKeyException("Key must not be null");
         }
@@ -468,7 +494,7 @@
         if (privateKey != p11Key) {
             checkKeySize(keyAlgorithm, privateKey);
         }
-        cancelOperation();
+        reset(true);
         mode = M_SIGN;
         p11Key = P11KeyFactory.convertKey(token, privateKey, keyAlgorithm);
         initialize();
@@ -503,6 +529,7 @@
     @Override
     protected void engineUpdate(byte[] b, int ofs, int len)
             throws SignatureException {
+
         ensureInitialized();
         if (len == 0) {
             return;
@@ -521,8 +548,7 @@
                 }
                 bytesProcessed += len;
             } catch (PKCS11Exception e) {
-                initialized = false;
-                session = token.releaseSession(session);
+                reset(false);
                 throw new ProviderException(e);
             }
             break;
@@ -546,6 +572,7 @@
     // see JCA spec
     @Override
     protected void engineUpdate(ByteBuffer byteBuffer) {
+
         ensureInitialized();
         int len = byteBuffer.remaining();
         if (len <= 0) {
@@ -571,8 +598,7 @@
                 bytesProcessed += len;
                 byteBuffer.position(ofs + len);
             } catch (PKCS11Exception e) {
-                initialized = false;
-                session = token.releaseSession(session);
+                reset(false);
                 throw new ProviderException("Update failed", e);
             }
             break;
@@ -589,6 +615,7 @@
             bytesProcessed += len;
             break;
         default:
+            reset(false);
             throw new ProviderException("Internal error");
         }
     }
@@ -596,7 +623,9 @@
     // see JCA spec
     @Override
     protected byte[] engineSign() throws SignatureException {
+
         ensureInitialized();
+        boolean doCancel = true;
         try {
             byte[] signature;
             if (type == T_UPDATE) {
@@ -633,6 +662,8 @@
                     signature = token.p11.C_Sign(session.id(), data);
                 }
             }
+            doCancel = false;
+
             if (keyAlgorithm.equals("RSA")) {
                 return signature;
             } else {
@@ -643,20 +674,21 @@
                 }
             }
         } catch (PKCS11Exception pe) {
+            doCancel = false;
             throw new ProviderException(pe);
         } catch (SignatureException | ProviderException e) {
-            cancelOperation();
             throw e;
         } finally {
-            initialized = false;
-            session = token.releaseSession(session);
+            reset(doCancel);
         }
     }
 
     // see JCA spec
     @Override
     protected boolean engineVerify(byte[] signature) throws SignatureException {
+
         ensureInitialized();
+        boolean doCancel = true;
         try {
             if (!p1363Format) {
                 if (keyAlgorithm.equals("DSA")) {
@@ -698,8 +730,10 @@
                     token.p11.C_Verify(session.id(), data, signature);
                 }
             }
+            doCancel = false;
             return true;
         } catch (PKCS11Exception pe) {
+            doCancel = false;
             long errorCode = pe.getErrorCode();
             if (errorCode == CKR_SIGNATURE_INVALID) {
                 return false;
@@ -714,11 +748,9 @@
             }
             throw new ProviderException(pe);
         }  catch (SignatureException | ProviderException e) {
-            cancelOperation();
             throw e;
         } finally {
-            initialized = false;
-            session = token.releaseSession(session);
+            reset(doCancel);
         }
     }
 
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java	Thu Nov 29 13:36:23 2018 -0300
@@ -189,8 +189,13 @@
             attributes = token.getAttributes
                 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
             // the returned keyID is a dummy, ignore
-            token.p11.C_DeriveKey(session.id(),
-                ckMechanism, p11Key.keyID, attributes);
+            long p11KeyID = p11Key.getKeyID();
+            try {
+                token.p11.C_DeriveKey(session.id(),
+                        ckMechanism, p11KeyID, attributes);
+            } finally {
+                p11Key.releaseKeyID();
+            }
 
             CK_SSL3_KEY_MAT_OUT out = null;
             if (params instanceof CK_SSL3_KEY_MAT_PARAMS) {
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsMasterSecretGenerator.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsMasterSecretGenerator.java	Thu Nov 29 13:36:23 2018 -0300
@@ -160,12 +160,13 @@
             ckMechanism = new CK_MECHANISM(mechanism, params);
         }
         Session session = null;
+        long p11KeyID = p11Key.getKeyID();
         try {
             session = token.getObjSession();
             CK_ATTRIBUTE[] attributes = token.getAttributes(O_GENERATE,
                 CKO_SECRET_KEY, CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
             long keyID = token.p11.C_DeriveKey(session.id(),
-                    ckMechanism, p11Key.keyID, attributes);
+                    ckMechanism, p11KeyID, attributes);
             int major, minor;
             if (ckVersion == null) {
                 major = -1;
@@ -174,12 +175,12 @@
                 major = ckVersion.major;
                 minor = ckVersion.minor;
             }
-            SecretKey key = P11Key.masterSecretKey(session, keyID,
+            return P11Key.masterSecretKey(session, keyID,
                 "TlsMasterSecret", 48 << 3, attributes, major, minor);
-            return key;
         } catch (Exception e) {
             throw new ProviderException("Could not generate key", e);
         } finally {
+            p11Key.releaseKeyID();
             token.releaseSession(session);
         }
     }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsPrfGenerator.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11TlsPrfGenerator.java	Thu Nov 29 13:36:23 2018 -0300
@@ -146,34 +146,36 @@
                         Functions.getHashMechId(spec.getPRFHashAlg()),
                         spec.getOutputLength(), ulServerOrClient);
                 Session session = null;
+                long keyID = p11Key.getKeyID();
                 try {
                     session = token.getOpSession();
                     token.p11.C_SignInit(session.id(),
-                            new CK_MECHANISM(mechanism, params), p11Key.keyID);
+                            new CK_MECHANISM(mechanism, params), keyID);
                     token.p11.C_SignUpdate(session.id(), 0, seed, 0, seed.length);
                     byte[] out = token.p11.C_SignFinal
                                         (session.id(), spec.getOutputLength());
-                    k = new SecretKeySpec(out, "TlsPrf");
+                    return new SecretKeySpec(out, "TlsPrf");
                 } catch (PKCS11Exception e) {
                     throw new ProviderException("Could not calculate PRF", e);
                 } finally {
+                    p11Key.releaseKeyID();
                     token.releaseSession(session);
                 }
             } else {
                 throw new ProviderException("Only Finished message authentication code"+
                                             " generation supported for TLS 1.2.");
             }
-            return k;
         }
 
         byte[] label = P11Util.getBytesUTF8(spec.getLabel());
 
         if (mechanism == CKM_NSS_TLS_PRF_GENERAL) {
             Session session = null;
+            long keyID = p11Key.getKeyID();
             try {
                 session = token.getOpSession();
                 token.p11.C_SignInit
-                    (session.id(), new CK_MECHANISM(mechanism), p11Key.keyID);
+                    (session.id(), new CK_MECHANISM(mechanism), keyID);
                 token.p11.C_SignUpdate(session.id(), 0, label, 0, label.length);
                 token.p11.C_SignUpdate(session.id(), 0, seed, 0, seed.length);
                 byte[] out = token.p11.C_SignFinal
@@ -182,6 +184,7 @@
             } catch (PKCS11Exception e) {
                 throw new ProviderException("Could not calculate PRF", e);
             } finally {
+                p11Key.releaseKeyID();
                 token.releaseSession(session);
             }
         }
@@ -192,15 +195,16 @@
         CK_TLS_PRF_PARAMS params = new CK_TLS_PRF_PARAMS(seed, label, out);
 
         Session session = null;
+        long keyID = p11Key.getKeyID();
         try {
             session = token.getOpSession();
-            long keyID = token.p11.C_DeriveKey(session.id(),
-                new CK_MECHANISM(mechanism, params), p11Key.keyID, null);
-            // ignore keyID, returned PRF bytes are in 'out'
+            token.p11.C_DeriveKey(session.id(),
+                new CK_MECHANISM(mechanism, params), keyID, null);
             return new SecretKeySpec(out, "TlsPrf");
         } catch (PKCS11Exception e) {
             throw new ProviderException("Could not calculate PRF", e);
         } finally {
+            p11Key.releaseKeyID();
             token.releaseSession(session);
         }
     }
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java	Thu Nov 29 13:36:23 2018 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  */
 
 /* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
@@ -1291,6 +1291,46 @@
  ******************************************************************************/
 
     /**
+     * getNativeKeyInfo gets the key object attributes and values as an opaque
+     * byte array to be used in createNativeKey method.
+     * (Key management)
+     *
+     * @param hSession the session's handle
+     * @param hKey key's handle
+     * @param hWrappingKey key handle for wrapping the extracted sensitive keys.
+     *        -1 if not used.
+     * @param pWrappingMech mechanism for wrapping the extracted sensitive keys
+     * @return an opaque byte array containing the key object attributes
+     *         and values
+     * @exception PKCS11Exception If an internal PKCS#11 function returns other
+     *            value than CKR_OK.
+     * @preconditions
+     * @postconditions
+     */
+    public native byte[] getNativeKeyInfo(long hSession, long hKey,
+            long hWrappingKey, CK_MECHANISM pWrappingMech) throws PKCS11Exception;
+
+    /**
+     * createNativeKey creates a key object with attributes and values
+     * specified by parameter as an opaque byte array.
+     * (Key management)
+     *
+     * @param hSession the session's handle
+     * @param keyInfo opaque byte array containing key object attributes
+     *        and values
+     * @param hWrappingKey key handle for unwrapping the extracted sensitive keys.
+     *        -1 if not used.
+     * @param pWrappingMech mechanism for unwrapping the extracted sensitive keys
+     * @return key object handle
+     * @exception PKCS11Exception If an internal PKCS#11 function returns other
+     *            value than CKR_OK.
+     * @preconditions
+     * @postconditions
+     */
+    public native long createNativeKey(long hSession, byte[] keyInfo,
+            long hWrappingKey, CK_MECHANISM pWrappingMech) throws PKCS11Exception;
+
+    /**
      * C_GenerateKey generates a secret key, creating a new key
      * object.
      * (Key management)
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c	Thu Nov 29 13:36:23 2018 -0300
@@ -54,6 +54,444 @@
 
 #include "sun_security_pkcs11_wrapper_PKCS11.h"
 
+#ifdef P11_ENABLE_GETNATIVEKEYINFO
+
+#define CK_ATTRIBUTES_TEMPLATE_LENGTH (CK_ULONG)61U
+
+static CK_ATTRIBUTE ckpAttributesTemplate[CK_ATTRIBUTES_TEMPLATE_LENGTH] = {
+        {CKA_CLASS, 0, 0},
+        {CKA_TOKEN, 0, 0},
+        {CKA_PRIVATE, 0, 0},
+        {CKA_LABEL, 0, 0},
+        {CKA_APPLICATION, 0, 0},
+        {CKA_VALUE, 0, 0},
+        {CKA_OBJECT_ID, 0, 0},
+        {CKA_CERTIFICATE_TYPE, 0, 0},
+        {CKA_ISSUER, 0, 0},
+        {CKA_SERIAL_NUMBER, 0, 0},
+        {CKA_AC_ISSUER, 0, 0},
+        {CKA_OWNER, 0, 0},
+        {CKA_ATTR_TYPES, 0, 0},
+        {CKA_TRUSTED, 0, 0},
+        {CKA_KEY_TYPE, 0, 0},
+        {CKA_SUBJECT, 0, 0},
+        {CKA_ID, 0, 0},
+        {CKA_SENSITIVE, 0, 0},
+        {CKA_ENCRYPT, 0, 0},
+        {CKA_DECRYPT, 0, 0},
+        {CKA_WRAP, 0, 0},
+        {CKA_UNWRAP, 0, 0},
+        {CKA_SIGN, 0, 0},
+        {CKA_SIGN_RECOVER, 0, 0},
+        {CKA_VERIFY, 0, 0},
+        {CKA_VERIFY_RECOVER, 0, 0},
+        {CKA_DERIVE, 0, 0},
+        {CKA_START_DATE, 0, 0},
+        {CKA_END_DATE, 0, 0},
+        {CKA_MODULUS, 0, 0},
+        {CKA_MODULUS_BITS, 0, 0},
+        {CKA_PUBLIC_EXPONENT, 0, 0},
+        {CKA_PRIVATE_EXPONENT, 0, 0},
+        {CKA_PRIME_1, 0, 0},
+        {CKA_PRIME_2, 0, 0},
+        {CKA_EXPONENT_1, 0, 0},
+        {CKA_EXPONENT_2, 0, 0},
+        {CKA_COEFFICIENT, 0, 0},
+        {CKA_PRIME, 0, 0},
+        {CKA_SUBPRIME, 0, 0},
+        {CKA_BASE, 0, 0},
+        {CKA_PRIME_BITS, 0, 0},
+        {CKA_SUB_PRIME_BITS, 0, 0},
+        {CKA_VALUE_BITS, 0, 0},
+        {CKA_VALUE_LEN, 0, 0},
+        {CKA_EXTRACTABLE, 0, 0},
+        {CKA_LOCAL, 0, 0},
+        {CKA_NEVER_EXTRACTABLE, 0, 0},
+        {CKA_ALWAYS_SENSITIVE, 0, 0},
+        {CKA_KEY_GEN_MECHANISM, 0, 0},
+        {CKA_MODIFIABLE, 0, 0},
+        {CKA_ECDSA_PARAMS, 0, 0},
+        {CKA_EC_PARAMS, 0, 0},
+        {CKA_EC_POINT, 0, 0},
+        {CKA_SECONDARY_AUTH, 0, 0},
+        {CKA_AUTH_PIN_FLAGS, 0, 0},
+        {CKA_HW_FEATURE_TYPE, 0, 0},
+        {CKA_RESET_ON_INIT, 0, 0},
+        {CKA_HAS_RESET, 0, 0},
+        {CKA_VENDOR_DEFINED, 0, 0},
+        {CKA_NETSCAPE_DB, 0, 0},
+};
+
+/*
+ * Class:     sun_security_pkcs11_wrapper_PKCS11
+ * Method:    getNativeKeyInfo
+ * Signature: (JJJLsun/security/pkcs11/wrapper/CK_MECHANISM;)[B
+ * Parametermapping:                         *PKCS11*
+ * @param   jlong         jSessionHandle     CK_SESSION_HANDLE hSession
+ * @param   jlong         jKeyHandle         CK_OBJECT_HANDLE hObject
+ * @param   jlong         jWrappingKeyHandle CK_OBJECT_HANDLE hObject
+ * @param   jobject       jWrappingMech      CK_MECHANISM_PTR pMechanism
+ * @return  jbyteArray    jNativeKeyInfo     -
+ */
+JNIEXPORT jbyteArray JNICALL
+Java_sun_security_pkcs11_wrapper_PKCS11_getNativeKeyInfo
+    (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jKeyHandle,
+    jlong jWrappingKeyHandle, jobject jWrappingMech)
+{
+    jbyteArray returnValue = NULL;
+    CK_SESSION_HANDLE ckSessionHandle = jLongToCKULong(jSessionHandle);
+    CK_OBJECT_HANDLE ckObjectHandle = jLongToCKULong(jKeyHandle);
+    CK_ATTRIBUTE_PTR ckpAttributes = NULL;
+    CK_RV rv;
+    jbyteArray nativeKeyInfoArray = NULL;
+    jbyteArray nativeKeyInfoWrappedKeyArray = NULL;
+    jbyte* nativeKeyInfoArrayRaw = NULL;
+    jbyte* nativeKeyInfoWrappedKeyArrayRaw = NULL;
+    unsigned int sensitiveAttributePosition = (unsigned int)-1;
+    unsigned int i = 0U;
+    unsigned long totalDataSize = 0UL, attributesCount = 0UL;
+    unsigned long totalCkAttributesSize = 0UL, totalNativeKeyInfoArraySize = 0UL;
+    unsigned long* wrappedKeySizePtr = NULL;
+    jbyte* nativeKeyInfoArrayRawCkAttributes = NULL;
+    jbyte* nativeKeyInfoArrayRawCkAttributesPtr = NULL;
+    jbyte* nativeKeyInfoArrayRawDataPtr = NULL;
+    CK_MECHANISM ckMechanism;
+    char iv[16] = {0x0};
+    CK_ULONG ckWrappedKeyLength = 0U;
+    unsigned long* wrappedKeySizeWrappedKeyArrayPtr = NULL;
+    CK_BYTE_PTR wrappedKeyBufferPtr = NULL;
+    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
+    CK_OBJECT_CLASS class;
+    CK_KEY_TYPE keyType;
+    CK_BBOOL sensitive;
+    CK_BBOOL netscapeAttributeValueNeeded = CK_FALSE;
+    CK_ATTRIBUTE ckNetscapeAttributesTemplate[4];
+    ckNetscapeAttributesTemplate[0].type = CKA_CLASS;
+    ckNetscapeAttributesTemplate[1].type = CKA_KEY_TYPE;
+    ckNetscapeAttributesTemplate[2].type = CKA_SENSITIVE;
+    ckNetscapeAttributesTemplate[3].type = CKA_NETSCAPE_DB;
+    ckNetscapeAttributesTemplate[0].pValue = &class;
+    ckNetscapeAttributesTemplate[1].pValue = &keyType;
+    ckNetscapeAttributesTemplate[2].pValue = &sensitive;
+    ckNetscapeAttributesTemplate[3].pValue = 0;
+    ckNetscapeAttributesTemplate[0].ulValueLen = sizeof(class);
+    ckNetscapeAttributesTemplate[1].ulValueLen = sizeof(keyType);
+    ckNetscapeAttributesTemplate[2].ulValueLen = sizeof(sensitive);
+    ckNetscapeAttributesTemplate[3].ulValueLen = 0;
+
+    if (ckpFunctions == NULL) { goto cleanup; }
+
+    // If key is private and of DSA or EC type, NSS may require CKA_NETSCAPE_DB
+    // attribute to unwrap it.
+    rv = (*ckpFunctions->C_GetAttributeValue)(ckSessionHandle, ckObjectHandle,
+            ckNetscapeAttributesTemplate,
+            sizeof(ckNetscapeAttributesTemplate)/sizeof(CK_ATTRIBUTE));
+
+    if (rv == CKR_OK && class == CKO_PRIVATE_KEY &&
+            (keyType == CKK_EC || keyType == CKK_DSA) &&
+            sensitive == CK_TRUE &&
+            ckNetscapeAttributesTemplate[3].ulValueLen == CK_UNAVAILABLE_INFORMATION) {
+        // We cannot set the attribute through C_SetAttributeValue here
+        // because it might be read-only. However, we can add it to
+        // the extracted buffer.
+        netscapeAttributeValueNeeded = CK_TRUE;
+        TRACE0("DEBUG: override CKA_NETSCAPE_DB attr value to TRUE\n");
+    }
+
+    ckpAttributes = (CK_ATTRIBUTE_PTR)malloc(
+            CK_ATTRIBUTES_TEMPLATE_LENGTH * sizeof(CK_ATTRIBUTE));
+    if (ckpAttributes == NULL) {
+        throwOutOfMemoryError(env, 0);
+        goto cleanup;
+    }
+    memcpy(ckpAttributes, ckpAttributesTemplate,
+            CK_ATTRIBUTES_TEMPLATE_LENGTH * sizeof(CK_ATTRIBUTE));
+
+    // Get sizes for value buffers
+    // NOTE: may return an error code but length values are filled anyways
+    (*ckpFunctions->C_GetAttributeValue)(ckSessionHandle, ckObjectHandle,
+            ckpAttributes, CK_ATTRIBUTES_TEMPLATE_LENGTH);
+
+    for (i = 0; i < CK_ATTRIBUTES_TEMPLATE_LENGTH; i++) {
+        if ((ckpAttributes+i)->ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+            totalDataSize += (ckpAttributes+i)->ulValueLen;
+            if ((ckpAttributes+i)->type == CKA_SENSITIVE) {
+                 sensitiveAttributePosition = attributesCount;
+                 TRACE0("DEBUG: GetNativeKeyInfo key is sensitive");
+            }
+            attributesCount++;
+        }
+    }
+
+    if (netscapeAttributeValueNeeded) {
+        attributesCount++;
+    }
+
+    // Allocate a single buffer to hold valid attributes and attribute's values
+    // Buffer structure: [ attributes-size, [ ... attributes ... ],
+    //                   values-size, [ ... values ... ], wrapped-key-size,
+    //                   [ ... wrapped-key ... ] ]
+    //     * sizes are expressed in bytes and data type is unsigned long
+    totalCkAttributesSize = attributesCount * sizeof(CK_ATTRIBUTE);
+    TRACE1("DEBUG: GetNativeKeyInfo attributesCount = %lu\n", attributesCount);
+    TRACE1("DEBUG: GetNativeKeyInfo sizeof CK_ATTRIBUTE = %lu\n", sizeof(CK_ATTRIBUTE));
+    TRACE1("DEBUG: GetNativeKeyInfo totalCkAttributesSize = %lu\n", totalCkAttributesSize);
+    TRACE1("DEBUG: GetNativeKeyInfo totalDataSize = %lu\n", totalDataSize);
+
+    totalNativeKeyInfoArraySize =
+            totalCkAttributesSize + sizeof(unsigned long) * 3 + totalDataSize;
+
+    TRACE1("DEBUG: GetNativeKeyInfo totalNativeKeyInfoArraySize = %lu\n", totalNativeKeyInfoArraySize);
+
+    nativeKeyInfoArray = (*env)->NewByteArray(env, totalNativeKeyInfoArraySize);
+    if (nativeKeyInfoArray == NULL) {
+        goto cleanup;
+    }
+
+    nativeKeyInfoArrayRaw = (*env)->GetByteArrayElements(env, nativeKeyInfoArray,
+            NULL);
+    if (nativeKeyInfoArrayRaw == NULL) {
+        goto cleanup;
+    }
+
+    wrappedKeySizePtr = (unsigned long*)(nativeKeyInfoArrayRaw +
+            sizeof(unsigned long)*2 + totalCkAttributesSize + totalDataSize);
+    memcpy(nativeKeyInfoArrayRaw, &totalCkAttributesSize, sizeof(unsigned long));
+
+    memcpy(nativeKeyInfoArrayRaw + sizeof(unsigned long) + totalCkAttributesSize,
+        &totalDataSize, sizeof(unsigned long));
+
+    memset(wrappedKeySizePtr, 0, sizeof(unsigned long));
+
+    nativeKeyInfoArrayRawCkAttributes = nativeKeyInfoArrayRaw +
+            sizeof(unsigned long);
+    nativeKeyInfoArrayRawCkAttributesPtr = nativeKeyInfoArrayRawCkAttributes;
+    nativeKeyInfoArrayRawDataPtr = nativeKeyInfoArrayRaw +
+            totalCkAttributesSize + sizeof(unsigned long) * 2;
+
+    for (i = 0; i < CK_ATTRIBUTES_TEMPLATE_LENGTH; i++) {
+        if ((ckpAttributes+i)->ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+            (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).type =
+                    (ckpAttributes+i)->type;
+            (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).ulValueLen =
+                    (ckpAttributes+i)->ulValueLen;
+            if ((ckpAttributes+i)->ulValueLen != 0) {
+                (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).pValue =
+                        nativeKeyInfoArrayRawDataPtr;
+            } else {
+                (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).pValue = 0;
+            }
+            nativeKeyInfoArrayRawDataPtr +=
+                    (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).ulValueLen;
+            nativeKeyInfoArrayRawCkAttributesPtr += sizeof(CK_ATTRIBUTE);
+        }
+    }
+
+    TRACE0("DEBUG: GetNativeKeyInfo finished prepping nativeKeyInfoArray\n");
+
+    // Get attribute's values
+    rv = (*ckpFunctions->C_GetAttributeValue)(ckSessionHandle, ckObjectHandle,
+            (CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributes,
+            attributesCount);
+    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) {
+        goto cleanup;
+    }
+
+    TRACE0("DEBUG: GetNativeKeyInfo 1st C_GetAttributeValue call passed\n");
+
+    if (netscapeAttributeValueNeeded) {
+        (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).type = CKA_NETSCAPE_DB;
+        // Value is not needed, public key is not used
+    }
+
+    if ((sensitiveAttributePosition != (unsigned int)-1) &&
+        *(CK_BBOOL*)(((CK_ATTRIBUTE_PTR)(((CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributes)
+                +sensitiveAttributePosition))->pValue) == CK_TRUE) {
+        // Key is sensitive. Need to extract it wrapped.
+        if (jWrappingKeyHandle != -1) {
+
+            jMechanismToCKMechanism(env, jWrappingMech, &ckMechanism);
+            rv = (*ckpFunctions->C_WrapKey)(ckSessionHandle, &ckMechanism,
+                    jLongToCKULong(jWrappingKeyHandle), ckObjectHandle,
+                    NULL_PTR, &ckWrappedKeyLength);
+            if (ckWrappedKeyLength != 0) {
+                // Allocate space for getting the wrapped key
+                nativeKeyInfoWrappedKeyArray = (*env)->NewByteArray(env,
+                        totalNativeKeyInfoArraySize + ckWrappedKeyLength);
+                if (nativeKeyInfoWrappedKeyArray == NULL) {
+                    goto cleanup;
+                }
+                nativeKeyInfoWrappedKeyArrayRaw =
+                        (*env)->GetByteArrayElements(env,
+                                nativeKeyInfoWrappedKeyArray, NULL);
+                if (nativeKeyInfoWrappedKeyArrayRaw == NULL) {
+                    goto cleanup;
+                }
+                memcpy(nativeKeyInfoWrappedKeyArrayRaw, nativeKeyInfoArrayRaw,
+                        totalNativeKeyInfoArraySize);
+                wrappedKeySizeWrappedKeyArrayPtr =
+                        (unsigned long*)(nativeKeyInfoWrappedKeyArrayRaw +
+                        sizeof(unsigned long)*2 + totalCkAttributesSize +
+                        totalDataSize);
+                memcpy(wrappedKeySizeWrappedKeyArrayPtr, &ckWrappedKeyLength, sizeof(unsigned long));
+                TRACE1("DEBUG: GetNativeKeyInfo 1st C_WrapKey wrappedKeyLength = %lu\n", ckWrappedKeyLength);
+
+                wrappedKeyBufferPtr =
+                        (unsigned char*)wrappedKeySizeWrappedKeyArrayPtr +
+                        sizeof(unsigned long);
+                rv = (*ckpFunctions->C_WrapKey)(ckSessionHandle, &ckMechanism,
+                        jLongToCKULong(jWrappingKeyHandle),ckObjectHandle,
+                        wrappedKeyBufferPtr, &ckWrappedKeyLength);
+                if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) {
+                    goto cleanup;
+                }
+                memcpy(wrappedKeySizeWrappedKeyArrayPtr, &ckWrappedKeyLength, sizeof(unsigned long));
+                TRACE1("DEBUG: GetNativeKeyInfo 2nd C_WrapKey wrappedKeyLength = %lu\n", ckWrappedKeyLength);
+            } else {
+                goto cleanup;
+            }
+        } else {
+            goto cleanup;
+        }
+        returnValue = nativeKeyInfoWrappedKeyArray;
+    } else {
+        returnValue = nativeKeyInfoArray;
+    }
+
+cleanup:
+    if (ckpAttributes != NULL) {
+        free(ckpAttributes);
+    }
+
+    if (nativeKeyInfoArrayRaw != NULL) {
+        (*env)->ReleaseByteArrayElements(env, nativeKeyInfoArray,
+                nativeKeyInfoArrayRaw, 0);
+    }
+
+    if (nativeKeyInfoWrappedKeyArrayRaw != NULL) {
+        (*env)->ReleaseByteArrayElements(env, nativeKeyInfoWrappedKeyArray,
+                nativeKeyInfoWrappedKeyArrayRaw, 0);
+    }
+
+    if (nativeKeyInfoArray != NULL && returnValue != nativeKeyInfoArray) {
+        (*env)->DeleteLocalRef(env, nativeKeyInfoArray);
+    }
+
+    if (nativeKeyInfoWrappedKeyArray != NULL
+            && returnValue != nativeKeyInfoWrappedKeyArray) {
+        (*env)->DeleteLocalRef(env, nativeKeyInfoWrappedKeyArray);
+    }
+
+    return returnValue;
+}
+#endif
+
+#ifdef P11_ENABLE_CREATENATIVEKEY
+/*
+ * Class:     sun_security_pkcs11_wrapper_PKCS11
+ * Method:    createNativeKey
+ * Signature: (J[BJLsun/security/pkcs11/wrapper/CK_MECHANISM;)J
+ * Parametermapping:                          *PKCS11*
+ * @param   jlong         jSessionHandle      CK_SESSION_HANDLE hSession
+ * @param   jbyteArray    jNativeKeyInfo      -
+ * @param   jlong         jWrappingKeyHandle  CK_OBJECT_HANDLE hObject
+ * @param   jobject       jWrappingMech       CK_MECHANISM_PTR pMechanism
+ * @return  jlong         jKeyHandle          CK_OBJECT_HANDLE hObject
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_security_pkcs11_wrapper_PKCS11_createNativeKey
+    (JNIEnv *env, jobject obj, jlong jSessionHandle, jbyteArray jNativeKeyInfo,
+    jlong jWrappingKeyHandle, jobject jWrappingMech)
+{
+    CK_OBJECT_HANDLE ckObjectHandle;
+    CK_RV rv;
+    CK_SESSION_HANDLE ckSessionHandle = jLongToCKULong(jSessionHandle);
+    jbyte* nativeKeyInfoArrayRaw = NULL;
+    jlong jObjectHandle = 0L;
+    unsigned long totalCkAttributesSize = 0UL;
+    unsigned long nativeKeyInfoCkAttributesCount = 0UL;
+    jbyte* nativeKeyInfoArrayRawCkAttributes = NULL;
+    jbyte* nativeKeyInfoArrayRawCkAttributesPtr = NULL;
+    jbyte* nativeKeyInfoArrayRawDataPtr = NULL;
+    unsigned long totalDataSize = 0UL;
+    unsigned long* wrappedKeySizePtr = NULL;
+    unsigned int i = 0U;
+    CK_MECHANISM ckMechanism;
+    char iv[16] = {0x0};
+    CK_ULONG ckWrappedKeyLength = 0UL;
+    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
+
+    if (ckpFunctions == NULL) { goto cleanup; }
+
+    nativeKeyInfoArrayRaw =
+            (*env)->GetByteArrayElements(env, jNativeKeyInfo, NULL);
+    if (nativeKeyInfoArrayRaw == NULL) {
+        goto cleanup;
+    }
+
+    memcpy(&totalCkAttributesSize, nativeKeyInfoArrayRaw, sizeof(unsigned long));
+    TRACE1("DEBUG: createNativeKey totalCkAttributesSize = %lu\n", totalCkAttributesSize);
+    nativeKeyInfoCkAttributesCount = totalCkAttributesSize/sizeof(CK_ATTRIBUTE);
+    TRACE1("DEBUG: createNativeKey nativeKeyInfoCkAttributesCount = %lu\n", nativeKeyInfoCkAttributesCount);
+
+    nativeKeyInfoArrayRawCkAttributes = nativeKeyInfoArrayRaw +
+            sizeof(unsigned long);
+    nativeKeyInfoArrayRawCkAttributesPtr = nativeKeyInfoArrayRawCkAttributes;
+    nativeKeyInfoArrayRawDataPtr = nativeKeyInfoArrayRaw +
+            totalCkAttributesSize + sizeof(unsigned long) * 2;
+    memcpy(&totalDataSize, (nativeKeyInfoArrayRaw + totalCkAttributesSize + sizeof(unsigned long)),
+            sizeof(unsigned long));
+    TRACE1("DEBUG: createNativeKey totalDataSize = %lu\n", totalDataSize);
+
+    wrappedKeySizePtr = (unsigned long*)(nativeKeyInfoArrayRaw +
+            sizeof(unsigned long)*2 + totalCkAttributesSize + totalDataSize);
+
+    memcpy(&ckWrappedKeyLength, wrappedKeySizePtr, sizeof(unsigned long));
+    TRACE1("DEBUG: createNativeKey wrappedKeyLength = %lu\n", ckWrappedKeyLength);
+
+    for (i = 0; i < nativeKeyInfoCkAttributesCount; i++) {
+        if ((*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).ulValueLen
+                > 0) {
+            (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).pValue =
+                    nativeKeyInfoArrayRawDataPtr;
+        }
+        nativeKeyInfoArrayRawDataPtr +=
+                (*(CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributesPtr).ulValueLen;
+        nativeKeyInfoArrayRawCkAttributesPtr += sizeof(CK_ATTRIBUTE);
+    }
+
+    if (ckWrappedKeyLength == 0) {
+        // Not a wrapped key
+        rv = (*ckpFunctions->C_CreateObject)(ckSessionHandle,
+                (CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributes,
+                jLongToCKULong(nativeKeyInfoCkAttributesCount), &ckObjectHandle);
+    } else {
+        // Wrapped key
+        jMechanismToCKMechanism(env, jWrappingMech, &ckMechanism);
+        rv = (*ckpFunctions->C_UnwrapKey)(ckSessionHandle, &ckMechanism,
+                jLongToCKULong(jWrappingKeyHandle),
+                (CK_BYTE_PTR)(wrappedKeySizePtr + 1), ckWrappedKeyLength,
+                (CK_ATTRIBUTE_PTR)nativeKeyInfoArrayRawCkAttributes,
+                jLongToCKULong(nativeKeyInfoCkAttributesCount),
+                &ckObjectHandle);
+    }
+    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) {
+        goto cleanup;
+    }
+
+    jObjectHandle = ckULongToJLong(ckObjectHandle);
+
+cleanup:
+
+    if (nativeKeyInfoArrayRaw != NULL) {
+        (*env)->ReleaseByteArrayElements(env, jNativeKeyInfo,
+                nativeKeyInfoArrayRaw, JNI_ABORT);
+    }
+
+    return jObjectHandle;
+}
+#endif
+
 #ifdef P11_ENABLE_C_GENERATEKEY
 /*
  * Class:     sun_security_pkcs11_wrapper_PKCS11
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11t.h	Thu Nov 29 13:36:23 2018 -0300
@@ -548,6 +548,7 @@
 #define CKA_ALLOWED_MECHANISMS          (CKF_ARRAY_ATTRIBUTE|0x00000600)
 
 #define CKA_VENDOR_DEFINED     0x80000000
+#define CKA_NETSCAPE_DB        0xD5A0DB00
 
 
 /* CK_ATTRIBUTE is a structure that includes the type, length
--- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h	Fri Jan 11 14:48:19 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h	Thu Nov 29 13:36:23 2018 -0300
@@ -151,6 +151,8 @@
 #undef  P11_ENABLE_C_GETFUNCTIONSTATUS
 #undef  P11_ENABLE_C_CANCELFUNCTION
 #undef  P11_ENABLE_C_WAITFORSLOTEVENT
+#define P11_ENABLE_GETNATIVEKEYINFO
+#define P11_ENABLE_CREATENATIVEKEY
 
 /* include the platform dependent part of the header */
 #include "p11_md.h"
@@ -204,6 +206,8 @@
 #define ckULongToJSize(x)       ((jsize) x)
 #define unsignedIntToCKULong(x) ((CK_ULONG) x)
 
+//#define P11_DEBUG
+
 #ifdef P11_DEBUG
 #define TRACE0(s) { printf(s); fflush(stdout); }
 #define TRACE1(s, p1) { printf(s, p1); fflush(stdout); }