changeset 16614:2ec14b8cdc25

Merge
author prr
date Thu, 26 Jan 2017 09:19:33 -0800
parents b873867cc8b8 1a13fbf70807
children 89cafca458bf
files make/copy/Copy-jdk.crypto.token.gmk make/lib/Lib-jdk.crypto.token.gmk src/jdk.crypto.token/share/classes/module-info.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/Config.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/KeyCache.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Cipher.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11DHKeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11DSAKeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Digest.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11ECKeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Key.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyAgreement.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyStore.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Mac.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11RSACipher.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11RSAKeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11SecureRandom.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Signature.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11TlsMasterSecretGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11TlsPrfGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11Util.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/Secmod.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/Session.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/SessionManager.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/SunPKCS11.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/TemplateManager.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/Token.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_ATTRIBUTE.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_CREATEMUTEX.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_C_INITIALIZE_ARGS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_DATE.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_DESTROYMUTEX.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_ECDH2_DERIVE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_INFO.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_LOCKMUTEX.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM_INFO.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_NOTIFY.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_RSA_PKCS_OAEP_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_RSA_PKCS_PSS_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SESSION_INFO.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SLOT_INFO.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_OUT.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SSL3_MASTER_KEY_DERIVE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_SSL3_RANDOM_DATA.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_TLS_PRF_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_TOKEN_INFO.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_UNLOCKMUTEX.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_VERSION.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH2_DERIVE_PARAMS.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/Constants.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/Functions.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/PKCS11.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/PKCS11Exception.java src/jdk.crypto.token/share/classes/sun/security/pkcs11/wrapper/PKCS11RuntimeException.java src/jdk.crypto.token/share/legal/pkcs11cryptotoken.md src/jdk.crypto.token/share/legal/pkcs11wrapper.md src/jdk.crypto.token/share/native/libj2pkcs11/j2secmod.c src/jdk.crypto.token/share/native/libj2pkcs11/j2secmod.h src/jdk.crypto.token/share/native/libj2pkcs11/p11_convert.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_crypt.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_digest.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_dual.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_general.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_keymgmt.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_mutex.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_objmgmt.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_sessmgmt.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_sign.c src/jdk.crypto.token/share/native/libj2pkcs11/p11_util.c src/jdk.crypto.token/share/native/libj2pkcs11/pkcs-11v2-20a3.h src/jdk.crypto.token/share/native/libj2pkcs11/pkcs11.h src/jdk.crypto.token/share/native/libj2pkcs11/pkcs11f.h src/jdk.crypto.token/share/native/libj2pkcs11/pkcs11t.h src/jdk.crypto.token/share/native/libj2pkcs11/pkcs11wrapper.h src/jdk.crypto.token/solaris/conf/security/sunpkcs11-solaris.cfg src/jdk.crypto.token/unix/native/libj2pkcs11/j2secmod_md.c src/jdk.crypto.token/unix/native/libj2pkcs11/j2secmod_md.h src/jdk.crypto.token/unix/native/libj2pkcs11/p11_md.c src/jdk.crypto.token/unix/native/libj2pkcs11/p11_md.h src/jdk.crypto.token/windows/native/libj2pkcs11/j2secmod_md.c src/jdk.crypto.token/windows/native/libj2pkcs11/j2secmod_md.h src/jdk.crypto.token/windows/native/libj2pkcs11/p11_md.c src/jdk.crypto.token/windows/native/libj2pkcs11/p11_md.h
diffstat 304 files changed, 38641 insertions(+), 37083 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/copy/Copy-jdk.crypto.cryptoki.gmk	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2014, 2017, 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
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include CopyCommon.gmk
+
+################################################################################
+
+ifeq ($(OPENJDK_TARGET_OS), solaris)
+
+  SUNPKCS11_CFG_SRC := \
+      $(JDK_TOPDIR)/src/jdk.crypto.cryptoki/solaris/conf/security/sunpkcs11-solaris.cfg
+  SUNPKCS11_CFG_DST := $(CONF_DST_DIR)/security/sunpkcs11-solaris.cfg
+
+  $(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC)
+	$(call install-file)
+
+  SECURITY_PKCS11_CONF_FILES += $(SUNPKCS11_CFG_DST)
+
+  TARGETS := $(SUNPKCS11_CFG_DST)
+
+endif
+
+################################################################################
--- a/make/copy/Copy-jdk.crypto.token.gmk	Tue Jan 24 18:44:13 2017 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-#
-# Copyright (c) 2014, 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
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.  Oracle designates this
-# particular file as subject to the "Classpath" exception as provided
-# by Oracle in the LICENSE file that accompanied this code.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-include CopyCommon.gmk
-
-################################################################################
-
-ifeq ($(OPENJDK_TARGET_OS), solaris)
-
-  SUNPKCS11_CFG_SRC := \
-      $(JDK_TOPDIR)/src/jdk.crypto.token/solaris/conf/security/sunpkcs11-solaris.cfg
-  SUNPKCS11_CFG_DST := $(CONF_DST_DIR)/security/sunpkcs11-solaris.cfg
-
-  $(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC)
-	$(call install-file)
-
-  SECURITY_PKCS11_CONF_FILES += $(SUNPKCS11_CFG_DST)
-
-  TARGETS := $(SUNPKCS11_CFG_DST)
-
-endif
-
-################################################################################
--- a/make/gensrc/GensrcLocaleData.gmk	Tue Jan 24 18:44:13 2017 +0300
+++ b/make/gensrc/GensrcLocaleData.gmk	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2017, 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
@@ -89,8 +89,8 @@
   $1_NON_BASE_LOCALES := $$(subst zh-MO,zh-MO$$(SPACE)zh-Hant-MO, $$($1_NON_BASE_LOCALES))
   $1_NON_BASE_LOCALES := $$(subst zh-TW,zh-TW$$(SPACE)zh-Hant-TW, $$($1_NON_BASE_LOCALES))
 
-# Adding implict locales nn-NO and nb-NO
-  $1_NON_BASE_LOCALES += nn-NO  nb-NO
+# Adding implict locales nb nn-NO and nb-NO
+  $1_NON_BASE_LOCALES += nb  nn-NO  nb-NO
   $1_NON_BASE_LOCALES := $$(sort $$($1_NON_BASE_LOCALES))
 
   ALL_BASE_LOCALES += $$($1_BASE_LOCALES)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/lib/Lib-jdk.crypto.cryptoki.gmk	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2011, 2017, 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
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include LibCommon.gmk
+
+################################################################################
+
+LIBJ2PKCS11_SRC := $(JDK_TOPDIR)/src/jdk.crypto.cryptoki/share/native/libj2pkcs11 \
+    $(JDK_TOPDIR)/src/jdk.crypto.cryptoki/$(OPENJDK_TARGET_OS_TYPE)/native/libj2pkcs11
+
+$(eval $(call SetupNativeCompilation,BUILD_LIBJ2PKCS11, \
+    LIBRARY := j2pkcs11, \
+    OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
+    SRC := $(LIBJ2PKCS11_SRC), \
+    OPTIMIZATION := LOW, \
+    CFLAGS := $(CFLAGS_JDKLIB) $(addprefix -I, $(LIBJ2PKCS11_SRC)) \
+        $(LIBJAVA_HEADER_FLAGS) \
+        -I$(SUPPORT_OUTPUTDIR)/headers/jdk.crypto.cryptoki, \
+    MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libj2pkcs11/mapfile-vers, \
+    LDFLAGS := $(LDFLAGS_JDKLIB) \
+        $(call SET_SHARED_LIBRARY_ORIGIN), \
+    LIBS_unix := $(LIBDL), \
+    LIBS_solaris := -lc, \
+    VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \
+    RC_FLAGS := $(RC_FLAGS) \
+        -D "JDK_FNAME=j2pkcs11.dll" \
+        -D "JDK_INTERNAL_NAME=j2pkcs11" \
+        -D "JDK_FTYPE=0x2L", \
+    OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libj2pkcs11, \
+))
+
+TARGETS += $(BUILD_LIBJ2PKCS11)
+
+################################################################################
--- a/make/lib/Lib-jdk.crypto.token.gmk	Tue Jan 24 18:44:13 2017 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-#
-# Copyright (c) 2011, 2016, 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
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.  Oracle designates this
-# particular file as subject to the "Classpath" exception as provided
-# by Oracle in the LICENSE file that accompanied this code.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-include LibCommon.gmk
-
-################################################################################
-
-LIBJ2PKCS11_SRC := $(JDK_TOPDIR)/src/jdk.crypto.token/share/native/libj2pkcs11 \
-    $(JDK_TOPDIR)/src/jdk.crypto.token/$(OPENJDK_TARGET_OS_TYPE)/native/libj2pkcs11
-
-$(eval $(call SetupNativeCompilation,BUILD_LIBJ2PKCS11, \
-    LIBRARY := j2pkcs11, \
-    OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
-    SRC := $(LIBJ2PKCS11_SRC), \
-    OPTIMIZATION := LOW, \
-    CFLAGS := $(CFLAGS_JDKLIB) $(addprefix -I, $(LIBJ2PKCS11_SRC)) \
-        $(LIBJAVA_HEADER_FLAGS) \
-        -I$(SUPPORT_OUTPUTDIR)/headers/jdk.crypto.token, \
-    MAPFILE := $(JDK_TOPDIR)/make/mapfiles/libj2pkcs11/mapfile-vers, \
-    LDFLAGS := $(LDFLAGS_JDKLIB) \
-        $(call SET_SHARED_LIBRARY_ORIGIN), \
-    LIBS_unix := $(LIBDL), \
-    LIBS_solaris := -lc, \
-    VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \
-    RC_FLAGS := $(RC_FLAGS) \
-        -D "JDK_FNAME=j2pkcs11.dll" \
-        -D "JDK_INTERNAL_NAME=j2pkcs11" \
-        -D "JDK_FTYPE=0x2L", \
-    OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libj2pkcs11, \
-))
-
-TARGETS += $(BUILD_LIBJ2PKCS11)
-
-################################################################################
--- a/make/src/classes/build/tools/cldrconverter/Bundle.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/make/src/classes/build/tools/cldrconverter/Bundle.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -389,6 +389,18 @@
                 }
             }
         }
+        // replace empty era names with parentMap era names
+        for (String key : ERA_KEYS) {
+            Object value = myMap.get(key);
+            if (value != null && value instanceof String[]) {
+                String[] eraStrings = (String[]) value;
+                for (String eraString : eraStrings) {
+                    if (eraString == null || eraString.isEmpty()) {
+                        fillInElements(parentsMap, key, value);
+                    }
+                }
+            }
+        }
 
         // Remove all duplicates
         if (Objects.nonNull(parentsMap)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/src/classes/build/tools/taglet/Incubating.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.taglet;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import com.sun.source.doctree.DocTree;
+import jdk.javadoc.doclet.taglet.Taglet;
+import static jdk.javadoc.doclet.taglet.Taglet.Location.*;
+
+/**
+ * An inline tag to conveniently insert a standard Incubating warning.  For
+ * use by members in Incubator Modules.
+ */
+public class Incubating implements Taglet {
+
+    /** Returns the set of locations in which a taglet may be used. */
+    @Override
+    public Set<Location> getAllowedLocations() {
+        return EnumSet.of(OVERVIEW, MODULE, PACKAGE, TYPE);
+    }
+
+    @Override
+    public boolean isInlineTag() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "Incubating";
+    }
+
+    private static final String MESSAGE =
+        "<BR><b><a href=\"http://openjdk.java.net/jeps/11\">Incubating Feature.</a>"
+                + " Will be removed in a future release.</b>";
+
+    @Override
+    public String toString(DocTree tag) {
+        return MESSAGE;
+    }
+
+    @Override
+    public String toString(List<? extends DocTree> tags) {
+        return MESSAGE;
+    }
+}
+
--- a/src/java.base/share/classes/java/net/URLClassLoader.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/java/net/URLClassLoader.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -66,16 +66,16 @@
  * refer to a directory. Otherwise, the URL is assumed to refer to a JAR file
  * which will be opened as needed.
  * <p>
+ * This class loader supports the loading of classes and resources from the
+ * contents of a <a href="../util/jar/JarFile.html#multirelease">multi-release</a>
+ * JAR file that is referred to by a given URL.
+ * <p>
  * The AccessControlContext of the thread that created the instance of
  * URLClassLoader will be used when subsequently loading classes and
  * resources.
  * <p>
  * The classes that are loaded are by default granted permission only to
  * access the URLs specified when the URLClassLoader was created.
- * <p>
- * This class loader supports the loading of classes from the contents of a
- * <a href="../util/jar/JarFile.html#multirelease">multi-release</a> JAR file
- * that is referred to by a given URL.
  *
  * @author  David Connelly
  * @since   1.2
--- a/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -71,7 +71,7 @@
  * following standard {@code AlgorithmParameterGenerator} algorithms and
  * keysizes in parentheses:
  * <ul>
- * <li>{@code DiffieHellman} (1024, 2048, 4096)</li>
+ * <li>{@code DiffieHellman} (1024, 2048)</li>
  * <li>{@code DSA} (1024, 2048)</li>
  * </ul>
  * These algorithms are described in the <a href=
--- a/src/java.base/share/classes/module-info.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/module-info.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -219,7 +219,7 @@
         java.security.jgss;
     exports sun.nio.ch to
         java.management,
-        jdk.crypto.token,
+        jdk.crypto.cryptoki,
         jdk.sctp,
         jdk.unsupported;
     exports sun.nio.cs to
@@ -244,13 +244,13 @@
         java.desktop,
         java.security.jgss;
     exports sun.security.internal.interfaces to
-        jdk.crypto.token;
+        jdk.crypto.cryptoki;
     exports sun.security.internal.spec to
-        jdk.crypto.token;
+        jdk.crypto.cryptoki;
     exports sun.security.jca to
         java.smartcardio,
         jdk.crypto.ec,
-        jdk.crypto.token,
+        jdk.crypto.cryptoki,
         jdk.naming.dns;
     exports sun.security.pkcs to
         jdk.crypto.ec,
@@ -258,13 +258,13 @@
     exports sun.security.provider to
         java.rmi,
         java.security.jgss,
-        jdk.crypto.token,
+        jdk.crypto.cryptoki,
         jdk.policytool,
         jdk.security.auth;
     exports sun.security.provider.certpath to
         java.naming;
     exports sun.security.rsa to
-        jdk.crypto.token;
+        jdk.crypto.cryptoki;
     exports sun.security.ssl to
         java.security.jgss;
     exports sun.security.timestamp to
@@ -280,14 +280,14 @@
         java.smartcardio,
         java.xml.crypto,
         jdk.crypto.ec,
-        jdk.crypto.token,
+        jdk.crypto.cryptoki,
         jdk.jartool,
         jdk.policytool,
         jdk.security.auth,
         jdk.security.jgss;
     exports sun.security.x509 to
         jdk.crypto.ec,
-        jdk.crypto.token,
+        jdk.crypto.cryptoki,
         jdk.jartool,
         jdk.security.auth;
     exports sun.security.validator to
--- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -32,7 +32,6 @@
 import java.net.URI;
 import java.nio.file.Paths;
 import java.util.*;
-import java.text.MessageFormat;
 import java.security.*;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
@@ -579,10 +578,9 @@
                 k.add(policy);
                 return k;
             });
-            MessageFormat form = new MessageFormat(ResourcesMgr.getString
-                (POLICY + ".error.parsing.policy.message"));
             Object[] source = {policy, pe.getLocalizedMessage()};
-            System.err.println(form.format(source));
+            System.err.println(LocalizedMessage.getMessage
+                (POLICY + ".error.parsing.policy.message", source));
             if (debug != null) {
                 pe.printStackTrace();
             }
@@ -805,32 +803,30 @@
                         }
                     }
                 } catch (java.lang.reflect.InvocationTargetException ite) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                         (POLICY +
-                          ".error.adding.Permission.perm.message"));
                     Object[] source = {pe.permission,
                                        ite.getTargetException().toString()};
-                    System.err.println(form.format(source));
+                    System.err.println(
+                        LocalizedMessage.getMessage(
+                            POLICY + ".error.adding.Permission.perm.message",
+                            source));
                 } catch (Exception e) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                         (POLICY +
-                          ".error.adding.Permission.perm.message"));
                     Object[] source = {pe.permission,
                                        e.toString()};
-                    System.err.println(form.format(source));
+                    System.err.println(
+                        LocalizedMessage.getMessage(
+                            POLICY + ".error.adding.Permission.perm.message",
+                            source));
                 }
             }
 
             // No need to sync because noone has access to newInfo yet
             newInfo.policyEntries.add(entry);
         } catch (Exception e) {
-            MessageFormat form = new MessageFormat(ResourcesMgr.getString
-                                         (POLICY
-                                         + ".error.adding.Entry.message"));
             Object[] source = {e.toString()};
-            System.err.println(form.format(source));
+            System.err.println(
+                LocalizedMessage.getMessage(
+                    POLICY + ".error.adding.Entry.message",
+                    source));
         }
         if (debug != null)
             debug.println();
@@ -1803,29 +1799,29 @@
             } else if (prefix.equalsIgnoreCase("alias")) {
                 // get the suffix and perform keystore alias replacement
                 if (colonIndex == -1) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("alias.name.not.provided.pe.name."));
                     Object[] source = {pe.name};
-                    throw new Exception(form.format(source));
+                    throw new Exception(
+                        LocalizedMessage.getMessage(
+                            "alias.name.not.provided.pe.name.",
+                            source));
                 }
                 suffix = value.substring(colonIndex+1);
                 if ((suffix = getDN(suffix, keystore)) == null) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("unable.to.perform.substitution.on.alias.suffix"));
                     Object[] source = {value.substring(colonIndex+1)};
-                    throw new Exception(form.format(source));
+                    throw new Exception(
+                        LocalizedMessage.getMessage(
+                            "unable.to.perform.substitution.on.alias.suffix",
+                            source));
                 }
 
                 sb.append(X500PRINCIPAL + " \"" + suffix + "\"");
                 startIndex = e+2;
             } else {
-                MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("substitution.value.prefix.unsupported"));
                 Object[] source = {prefix};
-                throw new Exception(form.format(source));
+                throw new Exception(
+                    LocalizedMessage.getMessage(
+                        "substitution.value.prefix.unsupported",
+                        source));
             }
         }
 
@@ -2039,7 +2035,7 @@
             super(type);
             if (type == null) {
                 throw new NullPointerException
-                    (ResourcesMgr.getString("type.can.t.be.null"));
+                    (LocalizedMessage.getMessage("type.can.t.be.null"));
             }
             this.type = type;
             this.name = name;
--- a/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -26,18 +26,14 @@
 package sun.security.provider;
 
 import java.io.*;
-import java.lang.RuntimePermission;
-import java.net.SocketPermission;
-import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.Principal;
-import java.text.MessageFormat;
 import java.util.*;
 import javax.security.auth.x500.X500Principal;
 
 import sun.security.util.Debug;
 import sun.security.util.PropertyExpander;
-import sun.security.util.ResourcesMgr;
+import sun.security.util.LocalizedMessage;
 
 /**
  * The policy for a Java runtime (specifying
@@ -209,13 +205,12 @@
                     if (!domainEntries.containsKey(domainName)) {
                         domainEntries.put(domainName, de);
                     } else {
-                        MessageFormat form =
-                            new MessageFormat(ResourcesMgr.getString(
-                                "duplicate.keystore.domain.name"));
+                        LocalizedMessage localizedMsg =
+                            new LocalizedMessage("duplicate.keystore.domain.name");
                         Object[] source = {domainName};
                         String msg = "duplicate keystore domain name: " +
                                      domainName;
-                        throw new ParsingException(msg, form, source);
+                        throw new ParsingException(msg, localizedMsg, source);
                     }
                 }
             } else {
@@ -225,7 +220,7 @@
         }
 
         if (keyStoreUrlString == null && storePassURL != null) {
-            throw new ParsingException(ResourcesMgr.getString
+            throw new ParsingException(LocalizedMessage.getMessage
                 ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
         }
     }
@@ -367,7 +362,7 @@
             keyStoreType = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                        ResourcesMgr.getString("expected.keystore.type"));
+                LocalizedMessage.getMessage("expected.keystore.type"));
         }
 
         // parse keystore provider
@@ -380,7 +375,7 @@
             keyStoreProvider = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                        ResourcesMgr.getString("expected.keystore.provider"));
+                LocalizedMessage.getMessage("expected.keystore.provider"));
         }
     }
 
@@ -430,7 +425,7 @@
                 if (e.codeBase != null)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString
+                            LocalizedMessage.getMessage
                                 ("multiple.Codebase.expressions"));
                 e.codeBase = match("quoted string");
                 peekAndMatch(",");
@@ -438,8 +433,8 @@
                 if (e.signedBy != null)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString(
-                                "multiple.SignedBy.expressions"));
+                            LocalizedMessage.getMessage
+                                ("multiple.SignedBy.expressions"));
                 e.signedBy = match("quoted string");
 
                 // verify syntax of the aliases
@@ -457,8 +452,8 @@
                 if (actr <= cctr)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString(
-                                "SignedBy.has.empty.alias"));
+                            LocalizedMessage.getMessage
+                                ("SignedBy.has.empty.alias"));
 
                 peekAndMatch(",");
             } else if (peekAndMatch("Principal")) {
@@ -500,7 +495,7 @@
                         }
                         throw new ParsingException
                                 (st.lineno(),
-                                 ResourcesMgr.getString
+                                LocalizedMessage.getMessage
                                     ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
                     }
                 }
@@ -537,8 +532,8 @@
 
             } else {
                 throw new ParsingException(st.lineno(),
-                                  ResourcesMgr.getString(
-                                      "expected.codeBase.or.SignedBy.or.Principal"));
+                    LocalizedMessage.getMessage
+                        ("expected.codeBase.or.SignedBy.or.Principal"));
             }
         }
 
@@ -561,8 +556,8 @@
             } else {
                 throw new
                     ParsingException(st.lineno(),
-                                     ResourcesMgr.getString(
-                                        "expected.permission.entry"));
+                        LocalizedMessage.getMessage
+                            ("expected.permission.entry"));
             }
         }
         match("}");
@@ -738,15 +733,14 @@
         switch (lookahead) {
         case StreamTokenizer.TT_NUMBER:
             throw new ParsingException(st.lineno(), expect,
-                                       ResourcesMgr.getString("number.") +
-                                       String.valueOf(st.nval));
+                LocalizedMessage.getMessage("number.") +
+                    String.valueOf(st.nval));
         case StreamTokenizer.TT_EOF:
-            MessageFormat form = new MessageFormat(
-                    ResourcesMgr.getString
-                            ("expected.expect.read.end.of.file."));
+            LocalizedMessage localizedMsg = new LocalizedMessage
+                ("expected.expect.read.end.of.file.");
             Object[] source = {expect};
             String msg = "expected [" + expect + "], read [end of file]";
-            throw new ParsingException(msg, form, source);
+            throw new ParsingException(msg, localizedMsg, source);
         case StreamTokenizer.TT_WORD:
             if (expect.equalsIgnoreCase(st.sval)) {
                 lookahead = st.nextToken();
@@ -832,10 +826,10 @@
             switch (lookahead) {
             case StreamTokenizer.TT_NUMBER:
                 throw new ParsingException(st.lineno(), ";",
-                                          ResourcesMgr.getString("number.") +
-                                          String.valueOf(st.nval));
+                        LocalizedMessage.getMessage("number.") +
+                            String.valueOf(st.nval));
             case StreamTokenizer.TT_EOF:
-                throw new ParsingException(ResourcesMgr.getString
+                throw new ParsingException(LocalizedMessage.getMessage
                         ("expected.read.end.of.file."));
             default:
                 lookahead = st.nextToken();
@@ -993,8 +987,8 @@
          */
         public PrincipalEntry(String principalClass, String principalName) {
             if (principalClass == null || principalName == null)
-                throw new NullPointerException(ResourcesMgr.getString(
-                                  "null.principalClass.or.principalName"));
+                throw new NullPointerException(LocalizedMessage.getMessage
+                    ("null.principalClass.or.principalName"));
             this.principalClass = principalClass;
             this.principalName = principalName;
         }
@@ -1244,11 +1238,11 @@
             if (!entries.containsKey(keystoreName)) {
                 entries.put(keystoreName, entry);
             } else {
-                MessageFormat form = new MessageFormat(ResourcesMgr.getString(
-                    "duplicate.keystore.name"));
+                LocalizedMessage localizedMsg = new LocalizedMessage
+                    ("duplicate.keystore.name");
                 Object[] source = {keystoreName};
                 String msg = "duplicate keystore name: " + keystoreName;
-                throw new ParsingException(msg, form, source);
+                throw new ParsingException(msg, localizedMsg, source);
             }
         }
 
@@ -1320,7 +1314,7 @@
         private static final long serialVersionUID = -4330692689482574072L;
 
         private String i18nMessage;
-        private MessageFormat form;
+        private LocalizedMessage localizedMsg;
         private Object[] source;
 
         /**
@@ -1336,10 +1330,10 @@
             i18nMessage = msg;
         }
 
-        public ParsingException(String msg, MessageFormat form,
+        public ParsingException(String msg, LocalizedMessage localizedMsg,
                                 Object[] source) {
             super(msg);
-            this.form = form;
+            this.localizedMsg = localizedMsg;
             this.source = source;
         }
 
@@ -1347,7 +1341,7 @@
             super("line " + line + ": " + msg);
             // don't call form.format unless getLocalizedMessage is called
             // to avoid unnecessary permission checks
-            form = new MessageFormat(ResourcesMgr.getString("line.number.msg"));
+            localizedMsg = new LocalizedMessage("line.number.msg");
             source = new Object[] {line, msg};
         }
 
@@ -1356,14 +1350,14 @@
                 "], found [" + actual + "]");
             // don't call form.format unless getLocalizedMessage is called
             // to avoid unnecessary permission checks
-            form = new MessageFormat(ResourcesMgr.getString
-                ("line.number.expected.expect.found.actual."));
+            localizedMsg = new LocalizedMessage
+                ("line.number.expected.expect.found.actual.");
             source = new Object[] {line, expect, actual};
         }
 
         @Override
         public String getLocalizedMessage() {
-            return i18nMessage != null ? i18nMessage : form.format(source);
+            return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+/**
+ * This class produces formatted and localized messages describing security
+ * issues. Some messages may be required when the VM is not fully booted. In
+ * this case, localization resources and classes used for message formatting
+ * may not be available. When the VM is not booted, the message will not be
+ * localized, and it will be formatted using simplified message formatting
+ * code that is contained in this class.
+ */
+
+/*
+ * Some of this code is executed before the VM is fully booted. Some import
+ * statements have been omitted to help prevent accidental use of classes that
+ * may not be available during boot.
+ */
+
+public class LocalizedMessage {
+
+    private static final Resources resources = new Resources();
+
+    private final String key;
+
+    /**
+     * A LocalizedMessage can be instantiated with a key and formatted with
+     * arguments later in the style of MessageFormat. This organization
+     * allows the actual formatting (and associated permission checks) to be
+     * avoided unless the resulting string is needed.
+     * @param key
+     */
+    public LocalizedMessage(String key) {
+        this.key = key;
+    }
+
+    /**
+     * Return a localized string corresponding to the key stored in this
+     * object, formatted with the provided arguments. When the VM is booted,
+     * this method will obtain the correct localized message and format it
+     * using java.text.MessageFormat. Otherwise, a non-localized string is
+     * returned, and the formatting is performed by simplified formatting code.
+     *
+     * @param arguments The arguments that should be placed in the message
+     * @return A formatted message string
+     */
+    public String format(Object... arguments) {
+        return getMessage(key, arguments);
+    }
+
+    /**
+     * Return a non-localized string corresponding to the provided key, and
+     * formatted with the provided arguments. All strings are obtained from
+     * sun.security.util.Resources, and the formatting only supports
+     * simple positional argument replacement (e.g. {1}).
+     *
+     * @param key The key of the desired string in Resources
+     * @param arguments The arguments that should be placed in the message
+     * @return A formatted message string
+     */
+    public static String getMessageUnbooted(String key,
+                                            Object... arguments) {
+
+        String value = resources.getString(key);
+        if (arguments == null || arguments.length == 0) {
+            return value;
+        }
+        // Classes like StringTokenizer may not be loaded, so parsing
+        //   is performed with String methods
+        StringBuilder sb = new StringBuilder();
+        int nextBraceIndex;
+        while ((nextBraceIndex = value.indexOf('{')) >= 0) {
+
+            String firstPart = value.substring(0, nextBraceIndex);
+            sb.append(firstPart);
+            value = value.substring(nextBraceIndex + 1);
+
+            // look for closing brace and argument index
+            nextBraceIndex = value.indexOf('}');
+            if (nextBraceIndex < 0) {
+                // no closing brace
+                // MessageFormat would throw IllegalArgumentException, but
+                //   that exception class may not be loaded yet
+                throw new RuntimeException("Unmatched braces");
+            }
+            String indexStr = value.substring(0, nextBraceIndex);
+            try {
+                int index = Integer.parseInt(indexStr);
+                sb.append(arguments[index]);
+            }
+            catch(NumberFormatException e) {
+                // argument index is not an integer
+                throw new RuntimeException("not an integer: " + indexStr);
+            }
+            value = value.substring(nextBraceIndex + 1);
+        }
+        sb.append(value);
+        return sb.toString();
+    }
+
+    /**
+     * Return a localized string corresponding to the provided key, and
+     * formatted with the provided arguments. When the VM is booted, this
+     * method will obtain the correct localized message and format it using
+     * java.text.MessageFormat. Otherwise, a non-localized string is returned,
+     * and the formatting is performed by simplified formatting code.
+     *
+     * @param key The key of the desired string in the security resource bundle
+     * @param arguments The arguments that should be placed in the message
+     * @return A formatted message string
+     */
+    public static String getMessage(String key,
+                                    Object... arguments) {
+
+        if (jdk.internal.misc.VM.isBooted()) {
+            // Localization and formatting resources are available
+            String value = ResourcesMgr.getString(key);
+            if (arguments == null) {
+                return value;
+            }
+            java.text.MessageFormat form = new java.text.MessageFormat(value);
+            return form.format(arguments);
+        } else {
+            return getMessageUnbooted(key, arguments);
+        }
+    }
+
+}
--- a/src/java.base/share/classes/sun/security/util/ResourcesMgr.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/sun/security/util/ResourcesMgr.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -34,37 +34,24 @@
  */
 public class ResourcesMgr {
     // intended for java.security, javax.security and sun.security resources
-    private final static String RESOURCES = "sun.security.util.Resources";
-    private final static String AUTH_RESOURCES = "sun.security.util.AuthResources";
-
     private final static Map<String, ResourceBundle> bundles = new ConcurrentHashMap<>();
 
     public static String getString(String s) {
-        ResourceBundle bundle = bundles.get(RESOURCES);
-        if (bundle == null) {
-
-            // only load if/when needed
-            bundle = java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedAction<java.util.ResourceBundle>() {
-                public java.util.ResourceBundle run() {
-                    return (java.util.ResourceBundle.getBundle
-                                ("sun.security.util.Resources"));
-                }
-            });
-        }
-
-        return bundle.getString(s);
+        return getBundle("sun.security.util.Resources").getString(s);
     }
 
     public static String getAuthResourceString(String s) {
-        if (VM.initLevel() == 3) {
-            // cannot trigger loading of any resource bundle as
-            // it depends on the system class loader fully initialized.
-            throw new InternalError("system class loader is being initialized");
+        return getBundle("sun.security.util.AuthResources").getString(s);
+    }
+
+    private static ResourceBundle getBundle(String bundleName) {
+        if (!VM.isBooted()) {
+            // don't expect this be called before the system is fully initialized.
+            // This triggers loading of any resource bundle that should be
+            // be done during initialization of system class loader.
+            throw new InternalError("Expected to use ResourceBundle only after booted");
         }
-
-        return bundles.computeIfAbsent(AUTH_RESOURCES, ResourceBundle::getBundle)
-                      .getString(s);
+        return bundles.computeIfAbsent(bundleName, ResourceBundle::getBundle);
     }
 
 }
--- a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Thu Jan 26 09:19:33 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -195,9 +195,26 @@
         return parent;
     }
 
+    /**
+     * This method returns equivalent CLDR supported locale for zh-HK,
+     * no, no-NO locales so that COMPAT locales do not precede
+     * those locales during ResourceBundle search path.
+     */
+    private static Locale getEquivalentLoc(Locale locale) {
+        switch (locale.toString()) {
+            case "zh_HK":
+                return Locale.forLanguageTag("zh-Hant-HK");
+            case "no":
+            case "no_NO":
+                return Locale.forLanguageTag("nb");
+        }
+        return locale;
+    }
+
     @Override
     public boolean isSupportedProviderLocale(Locale locale, Set<String> langtags) {
-        return Locale.ROOT.equals(locale) ||
-            langtags.contains(locale.stripExtensions().toLanguageTag());
+        return Locale.ROOT.equals(locale)
+                || langtags.contains(locale.stripExtensions().toLanguageTag())
+                || langtags.contains(getEquivalentLoc(locale).toLanguageTag());
     }
 }
--- a/src/java.base/share/lib/security/default.policy	Tue Jan 24 18:44:13 2017 +0300
+++ b/src/java.base/share/lib/security/default.policy	Thu Jan 26 09:19:33 2017 -0800
@@ -137,7 +137,7 @@
     permission java.security.SecurityPermission "removeProviderProperty.SunEC";
 };
 
-grant codeBase "jrt:/jdk.crypto.token" {
+grant codeBase "jrt:/jdk.crypto.cryptoki" {
     permission java.lang.RuntimePermission
                    "accessClassInPackage.sun.security.*";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.nio.ch";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/module-info.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2017, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module jdk.crypto.cryptoki {
+    // Depends on SunEC provider for EC related functionality
+    requires jdk.crypto.ec;
+    provides java.security.Provider with sun.security.pkcs11.SunPKCS11;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Config.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 2003, 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.io.*;
+import static java.io.StreamTokenizer.*;
+import java.math.BigInteger;
+import java.util.*;
+
+import java.security.*;
+
+import sun.security.util.PropertyExpander;
+
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+
+/**
+ * Configuration container and file parsing.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class Config {
+
+    static final int ERR_HALT       = 1;
+    static final int ERR_IGNORE_ALL = 2;
+    static final int ERR_IGNORE_LIB = 3;
+
+    // same as allowSingleThreadedModules but controlled via a system property
+    // and applied to all providers. if set to false, no SunPKCS11 instances
+    // will accept single threaded modules regardless of the setting in their
+    // config files.
+    private static final boolean staticAllowSingleThreadedModules;
+    private static final String osName;
+    private static final String osArch;
+
+    static {
+        List<String> props = AccessController.doPrivileged(
+            new PrivilegedAction<>() {
+                @Override
+                public List<String> run() {
+                    return List.of(
+                        System.getProperty(
+                            "sun.security.pkcs11.allowSingleThreadedModules",
+                            "true"),
+                        System.getProperty("os.name"),
+                        System.getProperty("os.arch"));
+                }
+            }
+        );
+        if ("false".equalsIgnoreCase(props.get(0))) {
+            staticAllowSingleThreadedModules = false;
+        } else {
+            staticAllowSingleThreadedModules = true;
+        }
+        osName = props.get(1);
+        osArch = props.get(2);
+    }
+
+    private final static boolean DEBUG = false;
+
+    private static void debug(Object o) {
+        if (DEBUG) {
+            System.out.println(o);
+        }
+    }
+
+    // file name containing this configuration
+    private String filename;
+
+    // Reader and StringTokenizer used during parsing
+    private Reader reader;
+
+    private StreamTokenizer st;
+
+    private Set<String> parsedKeywords;
+
+    // name suffix of the provider
+    private String name;
+
+    // name of the PKCS#11 library
+    private String library;
+
+    // description to pass to the provider class
+    private String description;
+
+    // slotID of the slot to use
+    private int slotID = -1;
+
+    // slot to use, specified as index in the slotlist
+    private int slotListIndex = -1;
+
+    // set of enabled mechanisms (or null to use default)
+    private Set<Long> enabledMechanisms;
+
+    // set of disabled mechanisms
+    private Set<Long> disabledMechanisms;
+
+    // whether to print debug info during startup
+    private boolean showInfo = false;
+
+    // template manager, initialized from parsed attributes
+    private TemplateManager templateManager;
+
+    // how to handle error during startup, one of ERR_
+    private int handleStartupErrors = ERR_HALT;
+
+    // flag indicating whether the P11KeyStore should
+    // be more tolerant of input parameters
+    private boolean keyStoreCompatibilityMode = true;
+
+    // flag indicating whether we need to explicitly cancel operations
+    // see Token
+    private boolean explicitCancel = true;
+
+    // how often to test for token insertion, if no token is present
+    private int insertionCheckInterval = 2000;
+
+    // flag inidicating whether to omit the call to C_Initialize()
+    // should be used only if we are running within a process that
+    // has already called it (e.g. Plugin inside of Mozilla/NSS)
+    private boolean omitInitialize = false;
+
+    // whether to allow modules that only support single threaded access.
+    // they cannot be used safely from multiple PKCS#11 consumers in the
+    // same process, for example NSS and SunPKCS11
+    private boolean allowSingleThreadedModules = true;
+
+    // name of the C function that returns the PKCS#11 functionlist
+    // This option primarily exists for the deprecated
+    // Secmod.Module.getProvider() method.
+    private String functionList = "C_GetFunctionList";
+
+    // whether to use NSS secmod mode. Implicitly set if nssLibraryDirectory,
+    // nssSecmodDirectory, or nssModule is specified.
+    private boolean nssUseSecmod;
+
+    // location of the NSS library files (libnss3.so, etc.)
+    private String nssLibraryDirectory;
+
+    // location of secmod.db
+    private String nssSecmodDirectory;
+
+    // which NSS module to use
+    private String nssModule;
+
+    private Secmod.DbMode nssDbMode = Secmod.DbMode.READ_WRITE;
+
+    // Whether the P11KeyStore should specify the CKA_NETSCAPE_DB attribute
+    // when creating private keys. Only valid if nssUseSecmod is true.
+    private boolean nssNetscapeDbWorkaround = true;
+
+    // Special init argument string for the NSS softtoken.
+    // This is used when using the NSS softtoken directly without secmod mode.
+    private String nssArgs;
+
+    // whether to use NSS trust attributes for the KeyStore of this provider
+    // this option is for internal use by the SunPKCS11 code only and
+    // works only for NSS providers created via the Secmod API
+    private boolean nssUseSecmodTrust = false;
+
+    // Flag to indicate whether the X9.63 encoding for EC points shall be used
+    // (true) or whether that encoding shall be wrapped in an ASN.1 OctetString
+    // (false).
+    private boolean useEcX963Encoding = false;
+
+    // Flag to indicate whether NSS should favour performance (false) or
+    // memory footprint (true).
+    private boolean nssOptimizeSpace = false;
+
+    Config(String fn) throws IOException {
+        this.filename = fn;
+        if (filename.startsWith("--")) {
+            // inline config
+            String config = filename.substring(2).replace("\\n", "\n");
+            reader = new StringReader(config);
+        } else {
+            reader = new BufferedReader(new InputStreamReader
+                (new FileInputStream(expand(filename))));
+        }
+        parsedKeywords = new HashSet<String>();
+        st = new StreamTokenizer(reader);
+        setupTokenizer();
+        parse();
+    }
+
+    String getFileName() {
+        return filename;
+    }
+
+    String getName() {
+        return name;
+    }
+
+    String getLibrary() {
+        return library;
+    }
+
+    String getDescription() {
+        if (description != null) {
+            return description;
+        }
+        return "SunPKCS11-" + name + " using library " + library;
+    }
+
+    int getSlotID() {
+        return slotID;
+    }
+
+    int getSlotListIndex() {
+        if ((slotID == -1) && (slotListIndex == -1)) {
+            // if neither is set, default to first slot
+            return 0;
+        } else {
+            return slotListIndex;
+        }
+    }
+
+    boolean getShowInfo() {
+        return (SunPKCS11.debug != null) || showInfo;
+    }
+
+    TemplateManager getTemplateManager() {
+        if (templateManager == null) {
+            templateManager = new TemplateManager();
+        }
+        return templateManager;
+    }
+
+    boolean isEnabled(long m) {
+        if (enabledMechanisms != null) {
+            return enabledMechanisms.contains(Long.valueOf(m));
+        }
+        if (disabledMechanisms != null) {
+            return !disabledMechanisms.contains(Long.valueOf(m));
+        }
+        return true;
+    }
+
+    int getHandleStartupErrors() {
+        return handleStartupErrors;
+    }
+
+    boolean getKeyStoreCompatibilityMode() {
+        return keyStoreCompatibilityMode;
+    }
+
+    boolean getExplicitCancel() {
+        return explicitCancel;
+    }
+
+    int getInsertionCheckInterval() {
+        return insertionCheckInterval;
+    }
+
+    boolean getOmitInitialize() {
+        return omitInitialize;
+    }
+
+    boolean getAllowSingleThreadedModules() {
+        return staticAllowSingleThreadedModules && allowSingleThreadedModules;
+    }
+
+    String getFunctionList() {
+        return functionList;
+    }
+
+    boolean getNssUseSecmod() {
+        return nssUseSecmod;
+    }
+
+    String getNssLibraryDirectory() {
+        return nssLibraryDirectory;
+    }
+
+    String getNssSecmodDirectory() {
+        return nssSecmodDirectory;
+    }
+
+    String getNssModule() {
+        return nssModule;
+    }
+
+    Secmod.DbMode getNssDbMode() {
+        return nssDbMode;
+    }
+
+    public boolean getNssNetscapeDbWorkaround() {
+        return nssUseSecmod && nssNetscapeDbWorkaround;
+    }
+
+    String getNssArgs() {
+        return nssArgs;
+    }
+
+    boolean getNssUseSecmodTrust() {
+        return nssUseSecmodTrust;
+    }
+
+    boolean getUseEcX963Encoding() {
+        return useEcX963Encoding;
+    }
+
+    boolean getNssOptimizeSpace() {
+        return nssOptimizeSpace;
+    }
+
+    private static String expand(final String s) throws IOException {
+        try {
+            return PropertyExpander.expand(s);
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    private void setupTokenizer() {
+        st.resetSyntax();
+        st.wordChars('a', 'z');
+        st.wordChars('A', 'Z');
+        st.wordChars('0', '9');
+        st.wordChars(':', ':');
+        st.wordChars('.', '.');
+        st.wordChars('_', '_');
+        st.wordChars('-', '-');
+        st.wordChars('/', '/');
+        st.wordChars('\\', '\\');
+        st.wordChars('$', '$');
+        st.wordChars('{', '{'); // need {} for property subst
+        st.wordChars('}', '}');
+        st.wordChars('*', '*');
+        st.wordChars('+', '+');
+        st.wordChars('~', '~');
+        // XXX check ASCII table and add all other characters except special
+
+        // special: #="(),
+        st.whitespaceChars(0, ' ');
+        st.commentChar('#');
+        st.eolIsSignificant(true);
+        st.quoteChar('\"');
+    }
+
+    private ConfigurationException excToken(String msg) {
+        return new ConfigurationException(msg + " " + st);
+    }
+
+    private ConfigurationException excLine(String msg) {
+        return new ConfigurationException(msg + ", line " + st.lineno());
+    }
+
+    private void parse() throws IOException {
+        while (true) {
+            int token = nextToken();
+            if (token == TT_EOF) {
+                break;
+            }
+            if (token == TT_EOL) {
+                continue;
+            }
+            if (token != TT_WORD) {
+                throw excToken("Unexpected token:");
+            }
+            String word = st.sval;
+            if (word.equals("name")) {
+                name = parseStringEntry(word);
+            } else if (word.equals("library")) {
+                library = parseLibrary(word);
+            } else if (word.equals("description")) {
+                parseDescription(word);
+            } else if (word.equals("slot")) {
+                parseSlotID(word);
+            } else if (word.equals("slotListIndex")) {
+                parseSlotListIndex(word);
+            } else if (word.equals("enabledMechanisms")) {
+                parseEnabledMechanisms(word);
+            } else if (word.equals("disabledMechanisms")) {
+                parseDisabledMechanisms(word);
+            } else if (word.equals("attributes")) {
+                parseAttributes(word);
+            } else if (word.equals("handleStartupErrors")) {
+                parseHandleStartupErrors(word);
+            } else if (word.endsWith("insertionCheckInterval")) {
+                insertionCheckInterval = parseIntegerEntry(word);
+                if (insertionCheckInterval < 100) {
+                    throw excLine(word + " must be at least 100 ms");
+                }
+            } else if (word.equals("showInfo")) {
+                showInfo = parseBooleanEntry(word);
+            } else if (word.equals("keyStoreCompatibilityMode")) {
+                keyStoreCompatibilityMode = parseBooleanEntry(word);
+            } else if (word.equals("explicitCancel")) {
+                explicitCancel = parseBooleanEntry(word);
+            } else if (word.equals("omitInitialize")) {
+                omitInitialize = parseBooleanEntry(word);
+            } else if (word.equals("allowSingleThreadedModules")) {
+                allowSingleThreadedModules = parseBooleanEntry(word);
+            } else if (word.equals("functionList")) {
+                functionList = parseStringEntry(word);
+            } else if (word.equals("nssUseSecmod")) {
+                nssUseSecmod = parseBooleanEntry(word);
+            } else if (word.equals("nssLibraryDirectory")) {
+                nssLibraryDirectory = parseLibrary(word);
+                nssUseSecmod = true;
+            } else if (word.equals("nssSecmodDirectory")) {
+                nssSecmodDirectory = expand(parseStringEntry(word));
+                nssUseSecmod = true;
+            } else if (word.equals("nssModule")) {
+                nssModule = parseStringEntry(word);
+                nssUseSecmod = true;
+            } else if (word.equals("nssDbMode")) {
+                String mode = parseStringEntry(word);
+                if (mode.equals("readWrite")) {
+                    nssDbMode = Secmod.DbMode.READ_WRITE;
+                } else if (mode.equals("readOnly")) {
+                    nssDbMode = Secmod.DbMode.READ_ONLY;
+                } else if (mode.equals("noDb")) {
+                    nssDbMode = Secmod.DbMode.NO_DB;
+                } else {
+                    throw excToken("nssDbMode must be one of readWrite, readOnly, and noDb:");
+                }
+                nssUseSecmod = true;
+            } else if (word.equals("nssNetscapeDbWorkaround")) {
+                nssNetscapeDbWorkaround = parseBooleanEntry(word);
+                nssUseSecmod = true;
+            } else if (word.equals("nssArgs")) {
+                parseNSSArgs(word);
+            } else if (word.equals("nssUseSecmodTrust")) {
+                nssUseSecmodTrust = parseBooleanEntry(word);
+            } else if (word.equals("useEcX963Encoding")) {
+                useEcX963Encoding = parseBooleanEntry(word);
+            } else if (word.equals("nssOptimizeSpace")) {
+                nssOptimizeSpace = parseBooleanEntry(word);
+            } else {
+                throw new ConfigurationException
+                        ("Unknown keyword '" + word + "', line " + st.lineno());
+            }
+            parsedKeywords.add(word);
+        }
+        reader.close();
+        reader = null;
+        st = null;
+        parsedKeywords = null;
+        if (name == null) {
+            throw new ConfigurationException("name must be specified");
+        }
+        if (nssUseSecmod == false) {
+            if (library == null) {
+                throw new ConfigurationException("library must be specified");
+            }
+        } else {
+            if (library != null) {
+                throw new ConfigurationException
+                    ("library must not be specified in NSS mode");
+            }
+            if ((slotID != -1) || (slotListIndex != -1)) {
+                throw new ConfigurationException
+                    ("slot and slotListIndex must not be specified in NSS mode");
+            }
+            if (nssArgs != null) {
+                throw new ConfigurationException
+                    ("nssArgs must not be specified in NSS mode");
+            }
+            if (nssUseSecmodTrust != false) {
+                throw new ConfigurationException("nssUseSecmodTrust is an "
+                    + "internal option and must not be specified in NSS mode");
+            }
+        }
+    }
+
+    //
+    // Parsing helper methods
+    //
+
+    private int nextToken() throws IOException {
+        int token = st.nextToken();
+        debug(st);
+        return token;
+    }
+
+    private void parseEquals() throws IOException {
+        int token = nextToken();
+        if (token != '=') {
+            throw excToken("Expected '=', read");
+        }
+    }
+
+    private void parseOpenBraces() throws IOException {
+        while (true) {
+            int token = nextToken();
+            if (token == TT_EOL) {
+                continue;
+            }
+            if ((token == TT_WORD) && st.sval.equals("{")) {
+                return;
+            }
+            throw excToken("Expected '{', read");
+        }
+    }
+
+    private boolean isCloseBraces(int token) {
+        return (token == TT_WORD) && st.sval.equals("}");
+    }
+
+    private String parseWord() throws IOException {
+        int token = nextToken();
+        if (token != TT_WORD) {
+            throw excToken("Unexpected value:");
+        }
+        return st.sval;
+    }
+
+    private String parseStringEntry(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+
+        int token = nextToken();
+        if (token != TT_WORD && token != '\"') {
+            // not a word token nor a string enclosed by double quotes
+            throw excToken("Unexpected value:");
+        }
+        String value = st.sval;
+
+        debug(keyword + ": " + value);
+        return value;
+    }
+
+    private boolean parseBooleanEntry(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        boolean value = parseBoolean();
+        debug(keyword + ": " + value);
+        return value;
+    }
+
+    private int parseIntegerEntry(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        int value = decodeNumber(parseWord());
+        debug(keyword + ": " + value);
+        return value;
+    }
+
+    private boolean parseBoolean() throws IOException {
+        String val = parseWord();
+        switch (val) {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            default:
+                throw excToken("Expected boolean value, read:");
+        }
+    }
+
+    private String parseLine() throws IOException {
+        // allow quoted string as part of line
+        String s = null;
+        while (true) {
+            int token = nextToken();
+            if ((token == TT_EOL) || (token == TT_EOF)) {
+                break;
+            }
+            if (token != TT_WORD && token != '\"') {
+                throw excToken("Unexpected value");
+            }
+            if (s == null) {
+                s = st.sval;
+            } else {
+                s = s + " " + st.sval;
+            }
+        }
+        if (s == null) {
+            throw excToken("Unexpected empty line");
+        }
+        return s;
+    }
+
+    private int decodeNumber(String str) throws IOException {
+        try {
+            if (str.startsWith("0x") || str.startsWith("0X")) {
+                return Integer.parseInt(str.substring(2), 16);
+            } else {
+                return Integer.parseInt(str);
+            }
+        } catch (NumberFormatException e) {
+            throw excToken("Expected number, read");
+        }
+    }
+
+    private static boolean isNumber(String s) {
+        if (s.length() == 0) {
+            return false;
+        }
+        char ch = s.charAt(0);
+        return ((ch >= '0') && (ch <= '9'));
+    }
+
+    private void parseComma() throws IOException {
+        int token = nextToken();
+        if (token != ',') {
+            throw excToken("Expected ',', read");
+        }
+    }
+
+    private static boolean isByteArray(String val) {
+        return val.startsWith("0h");
+    }
+
+    private byte[] decodeByteArray(String str) throws IOException {
+        if (str.startsWith("0h") == false) {
+            throw excToken("Expected byte array value, read");
+        }
+        str = str.substring(2);
+        // XXX proper hex parsing
+        try {
+            return new BigInteger(str, 16).toByteArray();
+        } catch (NumberFormatException e) {
+            throw excToken("Expected byte array value, read");
+        }
+    }
+
+    private void checkDup(String keyword) throws IOException {
+        if (parsedKeywords.contains(keyword)) {
+            throw excLine(keyword + " must only be specified once");
+        }
+    }
+
+    //
+    // individual entry parsing methods
+    //
+
+    private String parseLibrary(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        String lib = parseLine();
+        lib = expand(lib);
+        int i = lib.indexOf("/$ISA/");
+        if (i != -1) {
+            // replace "/$ISA/" with "/sparcv9/" on 64-bit Solaris SPARC
+            // and with "/amd64/" on Solaris AMD64.
+            // On all other platforms, just turn it into a "/"
+            String prefix = lib.substring(0, i);
+            String suffix = lib.substring(i + 5);
+            if (osName.equals("SunOS") && osArch.equals("sparcv9")) {
+                lib = prefix + "/sparcv9" + suffix;
+            } else if (osName.equals("SunOS") && osArch.equals("amd64")) {
+                lib = prefix + "/amd64" + suffix;
+            } else {
+                lib = prefix + suffix;
+            }
+        }
+        debug(keyword + ": " + lib);
+
+        // Check to see if full path is specified to prevent the DLL
+        // preloading attack
+        if (!(new File(lib)).isAbsolute()) {
+            throw new ConfigurationException(
+                "Absolute path required for library value: " + lib);
+        }
+        return lib;
+    }
+
+    private void parseDescription(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        description = parseLine();
+        debug("description: " + description);
+    }
+
+    private void parseSlotID(String keyword) throws IOException {
+        if (slotID >= 0) {
+            throw excLine("Duplicate slot definition");
+        }
+        if (slotListIndex >= 0) {
+            throw excLine
+                ("Only one of slot and slotListIndex must be specified");
+        }
+        parseEquals();
+        String slotString = parseWord();
+        slotID = decodeNumber(slotString);
+        debug("slot: " + slotID);
+    }
+
+    private void parseSlotListIndex(String keyword) throws IOException {
+        if (slotListIndex >= 0) {
+            throw excLine("Duplicate slotListIndex definition");
+        }
+        if (slotID >= 0) {
+            throw excLine
+                ("Only one of slot and slotListIndex must be specified");
+        }
+        parseEquals();
+        String slotString = parseWord();
+        slotListIndex = decodeNumber(slotString);
+        debug("slotListIndex: " + slotListIndex);
+    }
+
+    private void parseEnabledMechanisms(String keyword) throws IOException {
+        enabledMechanisms = parseMechanisms(keyword);
+    }
+
+    private void parseDisabledMechanisms(String keyword) throws IOException {
+        disabledMechanisms = parseMechanisms(keyword);
+    }
+
+    private Set<Long> parseMechanisms(String keyword) throws IOException {
+        checkDup(keyword);
+        Set<Long> mechs = new HashSet<Long>();
+        parseEquals();
+        parseOpenBraces();
+        while (true) {
+            int token = nextToken();
+            if (isCloseBraces(token)) {
+                break;
+            }
+            if (token == TT_EOL) {
+                continue;
+            }
+            if (token != TT_WORD) {
+                throw excToken("Expected mechanism, read");
+            }
+            long mech = parseMechanism(st.sval);
+            mechs.add(Long.valueOf(mech));
+        }
+        if (DEBUG) {
+            System.out.print("mechanisms: [");
+            for (Long mech : mechs) {
+                System.out.print(Functions.getMechanismName(mech));
+                System.out.print(", ");
+            }
+            System.out.println("]");
+        }
+        return mechs;
+    }
+
+    private long parseMechanism(String mech) throws IOException {
+        if (isNumber(mech)) {
+            return decodeNumber(mech);
+        } else {
+            try {
+                return Functions.getMechanismId(mech);
+            } catch (IllegalArgumentException e) {
+                throw excLine("Unknown mechanism: " + mech);
+            }
+        }
+    }
+
+    private void parseAttributes(String keyword) throws IOException {
+        if (templateManager == null) {
+            templateManager = new TemplateManager();
+        }
+        int token = nextToken();
+        if (token == '=') {
+            String s = parseWord();
+            if (s.equals("compatibility") == false) {
+                throw excLine("Expected 'compatibility', read " + s);
+            }
+            setCompatibilityAttributes();
+            return;
+        }
+        if (token != '(') {
+            throw excToken("Expected '(' or '=', read");
+        }
+        String op = parseOperation();
+        parseComma();
+        long objectClass = parseObjectClass();
+        parseComma();
+        long keyAlg = parseKeyAlgorithm();
+        token = nextToken();
+        if (token != ')') {
+            throw excToken("Expected ')', read");
+        }
+        parseEquals();
+        parseOpenBraces();
+        List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
+        while (true) {
+            token = nextToken();
+            if (isCloseBraces(token)) {
+                break;
+            }
+            if (token == TT_EOL) {
+                continue;
+            }
+            if (token != TT_WORD) {
+                throw excToken("Expected mechanism, read");
+            }
+            String attributeName = st.sval;
+            long attributeId = decodeAttributeName(attributeName);
+            parseEquals();
+            String attributeValue = parseWord();
+            attributes.add(decodeAttributeValue(attributeId, attributeValue));
+        }
+        templateManager.addTemplate
+                (op, objectClass, keyAlg, attributes.toArray(CK_A0));
+    }
+
+    private void setCompatibilityAttributes() {
+        // all secret keys
+        templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
+        new CK_ATTRIBUTE[] {
+            TOKEN_FALSE,
+            SENSITIVE_FALSE,
+            EXTRACTABLE_TRUE,
+            ENCRYPT_TRUE,
+            DECRYPT_TRUE,
+            WRAP_TRUE,
+            UNWRAP_TRUE,
+        });
+
+        // generic secret keys are special
+        // They are used as MAC keys plus for the SSL/TLS (pre)master secrets
+        templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
+        new CK_ATTRIBUTE[] {
+            SIGN_TRUE,
+            VERIFY_TRUE,
+            ENCRYPT_NULL,
+            DECRYPT_NULL,
+            WRAP_NULL,
+            UNWRAP_NULL,
+            DERIVE_TRUE,
+        });
+
+        // all private and public keys
+        templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
+        new CK_ATTRIBUTE[] {
+            TOKEN_FALSE,
+            SENSITIVE_FALSE,
+            EXTRACTABLE_TRUE,
+        });
+        templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
+        new CK_ATTRIBUTE[] {
+            TOKEN_FALSE,
+        });
+
+        // additional attributes for RSA private keys
+        templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
+        new CK_ATTRIBUTE[] {
+            DECRYPT_TRUE,
+            SIGN_TRUE,
+            SIGN_RECOVER_TRUE,
+            UNWRAP_TRUE,
+        });
+        // additional attributes for RSA public keys
+        templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
+        new CK_ATTRIBUTE[] {
+            ENCRYPT_TRUE,
+            VERIFY_TRUE,
+            VERIFY_RECOVER_TRUE,
+            WRAP_TRUE,
+        });
+
+        // additional attributes for DSA private keys
+        templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
+        new CK_ATTRIBUTE[] {
+            SIGN_TRUE,
+        });
+        // additional attributes for DSA public keys
+        templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
+        new CK_ATTRIBUTE[] {
+            VERIFY_TRUE,
+        });
+
+        // additional attributes for DH private keys
+        templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
+        new CK_ATTRIBUTE[] {
+            DERIVE_TRUE,
+        });
+
+        // additional attributes for EC private keys
+        templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
+        new CK_ATTRIBUTE[] {
+            SIGN_TRUE,
+            DERIVE_TRUE,
+        });
+        // additional attributes for EC public keys
+        templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
+        new CK_ATTRIBUTE[] {
+            VERIFY_TRUE,
+        });
+    }
+
+    private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
+
+    private String parseOperation() throws IOException {
+        String op = parseWord();
+        switch (op) {
+            case "*":
+                return TemplateManager.O_ANY;
+            case "generate":
+                return TemplateManager.O_GENERATE;
+            case "import":
+                return TemplateManager.O_IMPORT;
+            default:
+                throw excLine("Unknown operation " + op);
+        }
+    }
+
+    private long parseObjectClass() throws IOException {
+        String name = parseWord();
+        try {
+            return Functions.getObjectClassId(name);
+        } catch (IllegalArgumentException e) {
+            throw excLine("Unknown object class " + name);
+        }
+    }
+
+    private long parseKeyAlgorithm() throws IOException {
+        String name = parseWord();
+        if (isNumber(name)) {
+            return decodeNumber(name);
+        } else {
+            try {
+                return Functions.getKeyId(name);
+            } catch (IllegalArgumentException e) {
+                throw excLine("Unknown key algorithm " + name);
+            }
+        }
+    }
+
+    private long decodeAttributeName(String name) throws IOException {
+        if (isNumber(name)) {
+            return decodeNumber(name);
+        } else {
+            try {
+                return Functions.getAttributeId(name);
+            } catch (IllegalArgumentException e) {
+                throw excLine("Unknown attribute name " + name);
+            }
+        }
+    }
+
+    private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
+            throws IOException {
+        if (value.equals("null")) {
+            return new CK_ATTRIBUTE(id);
+        } else if (value.equals("true")) {
+            return new CK_ATTRIBUTE(id, true);
+        } else if (value.equals("false")) {
+            return new CK_ATTRIBUTE(id, false);
+        } else if (isByteArray(value)) {
+            return new CK_ATTRIBUTE(id, decodeByteArray(value));
+        } else if (isNumber(value)) {
+            return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
+        } else {
+            throw excLine("Unknown attribute value " + value);
+        }
+    }
+
+    private void parseNSSArgs(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        int token = nextToken();
+        if (token != '"') {
+            throw excToken("Expected quoted string");
+        }
+        nssArgs = expand(st.sval);
+        debug("nssArgs: " + nssArgs);
+    }
+
+    private void parseHandleStartupErrors(String keyword) throws IOException {
+        checkDup(keyword);
+        parseEquals();
+        String val = parseWord();
+        if (val.equals("ignoreAll")) {
+            handleStartupErrors = ERR_IGNORE_ALL;
+        } else if (val.equals("ignoreMissingLibrary")) {
+            handleStartupErrors = ERR_IGNORE_LIB;
+        } else if (val.equals("halt")) {
+            handleStartupErrors = ERR_HALT;
+        } else {
+            throw excToken("Invalid value for handleStartupErrors:");
+        }
+        debug("handleStartupErrors: " + handleStartupErrors);
+    }
+
+}
+
+class ConfigurationException extends IOException {
+    private static final long serialVersionUID = 254492758807673194L;
+    ConfigurationException(String msg) {
+        super(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/KeyCache.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2003, 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.util.*;
+import java.lang.ref.*;
+
+import java.security.Key;
+
+import sun.security.util.Cache;
+
+/**
+ * Key to P11Key translation cache. The PKCS#11 token can only perform
+ * operations on keys stored on the token (permanently or temporarily). That
+ * means that in order to allow the PKCS#11 provider to use keys from other
+ * providers, we need to transparently convert them to P11Keys. The engines
+ * do that using (Secret)KeyFactories, which in turn use this class as a
+ * cache.
+ *
+ * There are two KeyCache instances per provider, one for secret keys and
+ * one for public and private keys.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class KeyCache {
+
+    private final Cache<IdentityWrapper, P11Key> strongCache;
+
+    private WeakReference<Map<Key,P11Key>> cacheReference;
+
+    KeyCache() {
+        strongCache = Cache.newHardMemoryCache(16);
+    }
+
+    private static final class IdentityWrapper {
+        final Object obj;
+        IdentityWrapper(Object obj) {
+            this.obj = obj;
+        }
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o instanceof IdentityWrapper == false) {
+                return false;
+            }
+            IdentityWrapper other = (IdentityWrapper)o;
+            return this.obj == other.obj;
+        }
+        public int hashCode() {
+            return System.identityHashCode(obj);
+        }
+    }
+
+    synchronized P11Key get(Key key) {
+        P11Key p11Key = strongCache.get(new IdentityWrapper(key));
+        if (p11Key != null) {
+            return p11Key;
+        }
+        Map<Key,P11Key> map =
+                (cacheReference == null) ? null : cacheReference.get();
+        if (map == null) {
+            return null;
+        }
+        return map.get(key);
+    }
+
+    synchronized void put(Key key, P11Key p11Key) {
+        strongCache.put(new IdentityWrapper(key), p11Key);
+        Map<Key,P11Key> map =
+                (cacheReference == null) ? null : cacheReference.get();
+        if (map == null) {
+            map = new IdentityHashMap<>();
+            cacheReference = new WeakReference<>(map);
+        }
+        map.put(key, p11Key);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,929 @@
+/*
+ * Copyright (c) 2003, 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.pkcs11;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Locale;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+import sun.nio.ch.DirectBuffer;
+import sun.security.jca.JCAUtil;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * Cipher implementation class. This class currently supports
+ * DES, DESede, AES, ARCFOUR, and Blowfish.
+ *
+ * This class is designed to support ECB, CBC, CTR with NoPadding
+ * and ECB, CBC with PKCS5Padding. It will use its own padding impl
+ * if the native mechanism does not support padding.
+ *
+ * Note that PKCS#11 currently only supports ECB, CBC, and CTR.
+ * There are no provisions for other modes such as CFB, OFB, and PCBC.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11Cipher extends CipherSpi {
+
+    // mode constant for ECB mode
+    private final static int MODE_ECB = 3;
+    // mode constant for CBC mode
+    private final static int MODE_CBC = 4;
+    // mode constant for CTR mode
+    private final static int MODE_CTR = 5;
+
+    // padding constant for NoPadding
+    private final static int PAD_NONE = 5;
+    // padding constant for PKCS5Padding
+    private final static int PAD_PKCS5 = 6;
+
+    private static interface Padding {
+        // ENC: format the specified buffer with padding bytes and return the
+        // actual padding length
+        int setPaddingBytes(byte[] paddingBuffer, int padLen);
+
+        // DEC: return the length of trailing padding bytes given the specified
+        // padded data
+        int unpad(byte[] paddedData, int len)
+                throws BadPaddingException, IllegalBlockSizeException;
+    }
+
+    private static class PKCS5Padding implements Padding {
+
+        private final int blockSize;
+
+        PKCS5Padding(int blockSize)
+                throws NoSuchPaddingException {
+            if (blockSize == 0) {
+                throw new NoSuchPaddingException
+                        ("PKCS#5 padding not supported with stream ciphers");
+            }
+            this.blockSize = blockSize;
+        }
+
+        public int setPaddingBytes(byte[] paddingBuffer, int padLen) {
+            Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f));
+            return padLen;
+        }
+
+        public int unpad(byte[] paddedData, int len)
+                throws BadPaddingException, IllegalBlockSizeException {
+            if ((len < 1) || (len % blockSize != 0)) {
+                throw new IllegalBlockSizeException
+                    ("Input length must be multiples of " + blockSize);
+            }
+            byte padValue = paddedData[len - 1];
+            if (padValue < 1 || padValue > blockSize) {
+                throw new BadPaddingException("Invalid pad value!");
+            }
+            // sanity check padding bytes
+            int padStartIndex = len - padValue;
+            for (int i = padStartIndex; i < len; i++) {
+                if (paddedData[i] != padValue) {
+                    throw new BadPaddingException("Invalid pad bytes!");
+                }
+            }
+            return padValue;
+        }
+    }
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // name of the key algorithm, e.g. DES instead of algorithm DES/CBC/...
+    private final String keyAlgorithm;
+
+    // mechanism id
+    private final long mechanism;
+
+    // associated session, if any
+    private Session session;
+
+    // key, if init() was called
+    private P11Key p11Key;
+
+    // flag indicating whether an operation is initialized
+    private boolean initialized;
+
+    // falg indicating encrypt or decrypt mode
+    private boolean encrypt;
+
+    // mode, one of MODE_* above (MODE_ECB for stream ciphers)
+    private int blockMode;
+
+    // block size, 0 for stream ciphers
+    private final int blockSize;
+
+    // padding type, on of PAD_* above (PAD_NONE for stream ciphers)
+    private int paddingType;
+
+    // when the padding is requested but unsupported by the native mechanism,
+    // we use the following to do padding and necessary data buffering.
+    // padding object which generate padding and unpad the decrypted data
+    private Padding paddingObj;
+    // buffer for holding back the block which contains padding bytes
+    private byte[] padBuffer;
+    private int padBufferLen;
+
+    // original IV, if in MODE_CBC or MODE_CTR
+    private byte[] iv;
+
+    // number of bytes buffered internally by the native mechanism and padBuffer
+    // if we do the padding
+    private int bytesBuffered;
+
+    // length of key size in bytes; currently only used by AES given its oid
+    // specification mandates a fixed size of the key
+    private int fixedKeySize = -1;
+
+    P11Cipher(Token token, String algorithm, long mechanism)
+            throws PKCS11Exception, NoSuchAlgorithmException {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = mechanism;
+
+        String[] algoParts = algorithm.split("/");
+
+        if (algoParts[0].startsWith("AES")) {
+            blockSize = 16;
+            int index = algoParts[0].indexOf('_');
+            if (index != -1) {
+                // should be well-formed since we specify what we support
+                fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1))/8;
+            }
+            keyAlgorithm = "AES";
+        } else {
+            keyAlgorithm = algoParts[0];
+            if (keyAlgorithm.equals("RC4") ||
+                    keyAlgorithm.equals("ARCFOUR")) {
+                blockSize = 0;
+            } else { // DES, DESede, Blowfish
+                blockSize = 8;
+            }
+        }
+        this.blockMode =
+            (algoParts.length > 1 ? parseMode(algoParts[1]) : MODE_ECB);
+        String defPadding = (blockSize == 0 ? "NoPadding" : "PKCS5Padding");
+        String paddingStr =
+                (algoParts.length > 2 ? algoParts[2] : defPadding);
+        try {
+            engineSetPadding(paddingStr);
+        } catch (NoSuchPaddingException nspe) {
+            // should not happen
+            throw new ProviderException(nspe);
+        }
+    }
+
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        // Disallow change of mode for now since currently it's explicitly
+        // defined in transformation strings
+        throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+    }
+
+    private int parseMode(String mode) throws NoSuchAlgorithmException {
+        mode = mode.toUpperCase(Locale.ENGLISH);
+        int result;
+        if (mode.equals("ECB")) {
+            result = MODE_ECB;
+        } else if (mode.equals("CBC")) {
+            if (blockSize == 0) {
+                throw new NoSuchAlgorithmException
+                        ("CBC mode not supported with stream ciphers");
+            }
+            result = MODE_CBC;
+        } else if (mode.equals("CTR")) {
+            result = MODE_CTR;
+        } else {
+            throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+        }
+        return result;
+    }
+
+    // see JCE spec
+    protected void engineSetPadding(String padding)
+            throws NoSuchPaddingException {
+        paddingObj = null;
+        padBuffer = null;
+        padding = padding.toUpperCase(Locale.ENGLISH);
+        if (padding.equals("NOPADDING")) {
+            paddingType = PAD_NONE;
+        } else if (padding.equals("PKCS5PADDING")) {
+            if (this.blockMode == MODE_CTR) {
+                throw new NoSuchPaddingException
+                    ("PKCS#5 padding not supported with CTR mode");
+            }
+            paddingType = PAD_PKCS5;
+            if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD &&
+                    mechanism != CKM_AES_CBC_PAD) {
+                // no native padding support; use our own padding impl
+                paddingObj = new PKCS5Padding(blockSize);
+                padBuffer = new byte[blockSize];
+            }
+        } else {
+            throw new NoSuchPaddingException("Unsupported padding " + padding);
+        }
+    }
+
+    // see JCE spec
+    protected int engineGetBlockSize() {
+        return blockSize;
+    }
+
+    // see JCE spec
+    protected int engineGetOutputSize(int inputLen) {
+        return doFinalLength(inputLen);
+    }
+
+    // see JCE spec
+    protected byte[] engineGetIV() {
+        return (iv == null) ? null : iv.clone();
+    }
+
+    // see JCE spec
+    protected AlgorithmParameters engineGetParameters() {
+        if (iv == null) {
+            return null;
+        }
+        IvParameterSpec ivSpec = new IvParameterSpec(iv);
+        try {
+            AlgorithmParameters params =
+                    AlgorithmParameters.getInstance(keyAlgorithm,
+                    P11Util.getSunJceProvider());
+            params.init(ivSpec);
+            return params;
+        } catch (GeneralSecurityException e) {
+            // NoSuchAlgorithmException, NoSuchProviderException
+            // InvalidParameterSpecException
+            throw new ProviderException("Could not encode parameters", e);
+        }
+    }
+
+    // see JCE spec
+    protected void engineInit(int opmode, Key key, SecureRandom random)
+            throws InvalidKeyException {
+        try {
+            implInit(opmode, key, null, random);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidKeyException("init() failed", e);
+        }
+    }
+
+    // see JCE spec
+    protected void engineInit(int opmode, Key key,
+            AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        byte[] ivValue;
+        if (params != null) {
+            if (params instanceof IvParameterSpec == false) {
+                throw new InvalidAlgorithmParameterException
+                        ("Only IvParameterSpec supported");
+            }
+            IvParameterSpec ivSpec = (IvParameterSpec) params;
+            ivValue = ivSpec.getIV();
+        } else {
+            ivValue = null;
+        }
+        implInit(opmode, key, ivValue, random);
+    }
+
+    // see JCE spec
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+            SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        byte[] ivValue;
+        if (params != null) {
+            try {
+                IvParameterSpec ivSpec =
+                        params.getParameterSpec(IvParameterSpec.class);
+                ivValue = ivSpec.getIV();
+            } catch (InvalidParameterSpecException e) {
+                throw new InvalidAlgorithmParameterException
+                        ("Could not decode IV", e);
+            }
+        } else {
+            ivValue = null;
+        }
+        implInit(opmode, key, ivValue, random);
+    }
+
+    // actual init() implementation
+    private void implInit(int opmode, Key key, byte[] iv,
+            SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        reset(true);
+        if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) {
+            throw new InvalidKeyException("Key size is invalid");
+        }
+        switch (opmode) {
+            case Cipher.ENCRYPT_MODE:
+                encrypt = true;
+                break;
+            case Cipher.DECRYPT_MODE:
+                encrypt = false;
+                break;
+            default:
+                throw new InvalidAlgorithmParameterException
+                        ("Unsupported mode: " + opmode);
+        }
+        if (blockMode == MODE_ECB) { // ECB or stream cipher
+            if (iv != null) {
+                if (blockSize == 0) {
+                    throw new InvalidAlgorithmParameterException
+                            ("IV not used with stream ciphers");
+                } else {
+                    throw new InvalidAlgorithmParameterException
+                            ("IV not used in ECB mode");
+                }
+            }
+        } else { // MODE_CBC or MODE_CTR
+            if (iv == null) {
+                if (encrypt == false) {
+                    String exMsg =
+                        (blockMode == MODE_CBC ?
+                         "IV must be specified for decryption in CBC mode" :
+                         "IV must be specified for decryption in CTR mode");
+                    throw new InvalidAlgorithmParameterException(exMsg);
+                }
+                // generate random IV
+                if (random == null) {
+                    random = JCAUtil.getSecureRandom();
+                }
+                iv = new byte[blockSize];
+                random.nextBytes(iv);
+            } else {
+                if (iv.length != blockSize) {
+                    throw new InvalidAlgorithmParameterException
+                            ("IV length must match block size");
+                }
+            }
+        }
+        this.iv = iv;
+        p11Key = P11SecretKeyFactory.convertKey(token, key, keyAlgorithm);
+        try {
+            initialize();
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not initialize cipher", e);
+        }
+    }
+
+    private void cancelOperation() {
+        if (initialized == false) {
+            return;
+        }
+
+        if ((session == null) || (token.explicitCancel == false)) {
+            return;
+        }
+        try {
+            if (session.hasObjects() == false) {
+                session = token.killSession(session);
+                return;
+            } else {
+                // cancel operation by finishing it
+                int bufLen = doFinalLength(0);
+                byte[] buffer = new byte[bufLen];
+                if (encrypt) {
+                    token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen);
+                } else {
+                    token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
+                }
+            }
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("Cancel failed", e);
+        }
+    }
+
+    private void ensureInitialized() throws PKCS11Exception {
+        if (initialized == false) {
+            initialize();
+        }
+    }
+
+    private void initialize() throws PKCS11Exception {
+        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));
+
+        try {
+            if (encrypt) {
+                token.p11.C_EncryptInit(session.id(), mechParams, p11Key.keyID);
+            } else {
+                token.p11.C_DecryptInit(session.id(), mechParams, p11Key.keyID);
+            }
+        } catch (PKCS11Exception ex) {
+            // release session when initialization failed
+            session = token.releaseSession(session);
+            throw ex;
+        }
+        bytesBuffered = 0;
+        padBufferLen = 0;
+        initialized = true;
+    }
+
+    // if update(inLen) is called, how big does the output buffer have to be?
+    private int updateLength(int inLen) {
+        if (inLen <= 0) {
+            return 0;
+        }
+
+        int result = inLen + bytesBuffered;
+        if (blockSize != 0 && blockMode != MODE_CTR) {
+            // minus the number of bytes in the last incomplete block.
+            result -= (result & (blockSize - 1));
+        }
+        return result;
+    }
+
+    // if doFinal(inLen) is called, how big does the output buffer have to be?
+    private int doFinalLength(int inLen) {
+        if (inLen < 0) {
+            return 0;
+        }
+
+        int result = inLen + bytesBuffered;
+        if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
+            // add the number of bytes to make the last block complete.
+            result += (blockSize - (result & (blockSize - 1)));
+        }
+        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 {
+            byte[] out = new byte[updateLength(inLen)];
+            int n = engineUpdate(in, inOfs, inLen, out, 0);
+            return P11Util.convert(out, 0, n);
+        } catch (ShortBufferException e) {
+            // convert since the output length is calculated by updateLength()
+            throw new ProviderException(e);
+        }
+    }
+
+    // see JCE spec
+    protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
+            int outOfs) throws ShortBufferException {
+        int outLen = out.length - outOfs;
+        return implUpdate(in, inOfs, inLen, out, outOfs, outLen);
+    }
+
+    // see JCE spec
+    @Override
+    protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException {
+        return implUpdate(inBuffer, outBuffer);
+    }
+
+    // see JCE spec
+    protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        try {
+            byte[] out = new byte[doFinalLength(inLen)];
+            int n = engineDoFinal(in, inOfs, inLen, out, 0);
+            return P11Util.convert(out, 0, n);
+        } catch (ShortBufferException e) {
+            // convert since the output length is calculated by doFinalLength()
+            throw new ProviderException(e);
+        }
+    }
+
+    // see JCE spec
+    protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
+            int outOfs) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int n = 0;
+        if ((inLen != 0) && (in != null)) {
+            n = engineUpdate(in, inOfs, inLen, out, outOfs);
+            outOfs += n;
+        }
+        n += implDoFinal(out, outOfs, out.length - outOfs);
+        return n;
+    }
+
+    // see JCE spec
+    @Override
+    protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int n = engineUpdate(inBuffer, outBuffer);
+        n += implDoFinal(outBuffer);
+        return n;
+    }
+
+    private int implUpdate(byte[] in, int inOfs, int inLen,
+            byte[] out, int outOfs, int outLen) throws ShortBufferException {
+        if (outLen < updateLength(inLen)) {
+            throw new ShortBufferException();
+        }
+        try {
+            ensureInitialized();
+            int k = 0;
+            if (encrypt) {
+                k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
+                        0, out, outOfs, outLen);
+            } else {
+                int newPadBufferLen = 0;
+                if (paddingObj != null) {
+                    if (padBufferLen != 0) {
+                        // NSS throws up when called with data not in multiple
+                        // of blocks. Try to work around this by holding the
+                        // extra data in padBuffer.
+                        if (padBufferLen != padBuffer.length) {
+                            int bufCapacity = padBuffer.length - padBufferLen;
+                            if (inLen > bufCapacity) {
+                                bufferInputBytes(in, inOfs, bufCapacity);
+                                inOfs += bufCapacity;
+                                inLen -= bufCapacity;
+                            } else {
+                                bufferInputBytes(in, inOfs, inLen);
+                                return 0;
+                            }
+                        }
+                        k = token.p11.C_DecryptUpdate(session.id(),
+                                0, padBuffer, 0, padBufferLen,
+                                0, out, outOfs, outLen);
+                        padBufferLen = 0;
+                    }
+                    newPadBufferLen = inLen & (blockSize - 1);
+                    if (newPadBufferLen == 0) {
+                        newPadBufferLen = padBuffer.length;
+                    }
+                    inLen -= newPadBufferLen;
+                }
+                if (inLen > 0) {
+                    k += token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs,
+                            inLen, 0, out, (outOfs + k), (outLen - k));
+                }
+                // update 'padBuffer' if using our own padding impl.
+                if (paddingObj != null) {
+                    bufferInputBytes(in, inOfs + inLen, newPadBufferLen);
+                }
+            }
+            bytesBuffered += (inLen - k);
+            return k;
+        } catch (PKCS11Exception e) {
+            if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+                throw (ShortBufferException)
+                        (new ShortBufferException().initCause(e));
+            }
+            reset(false);
+            throw new ProviderException("update() failed", e);
+        }
+    }
+
+    private int implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
+            throws ShortBufferException {
+        int inLen = inBuffer.remaining();
+        if (inLen <= 0) {
+            return 0;
+        }
+
+        int outLen = outBuffer.remaining();
+        if (outLen < updateLength(inLen)) {
+            throw new ShortBufferException();
+        }
+        int origPos = inBuffer.position();
+        try {
+            ensureInitialized();
+
+            long inAddr = 0;
+            int inOfs = 0;
+            byte[] inArray = null;
+
+            if (inBuffer instanceof DirectBuffer) {
+                inAddr = ((DirectBuffer) inBuffer).address();
+                inOfs = origPos;
+            } else if (inBuffer.hasArray()) {
+                inArray = inBuffer.array();
+                inOfs = (origPos + inBuffer.arrayOffset());
+            }
+
+            long outAddr = 0;
+            int outOfs = 0;
+            byte[] outArray = null;
+            if (outBuffer instanceof DirectBuffer) {
+                outAddr = ((DirectBuffer) outBuffer).address();
+                outOfs = outBuffer.position();
+            } else {
+                if (outBuffer.hasArray()) {
+                    outArray = outBuffer.array();
+                    outOfs = (outBuffer.position() + outBuffer.arrayOffset());
+                } else {
+                    outArray = new byte[outLen];
+                }
+            }
+
+            int k = 0;
+            if (encrypt) {
+                if (inAddr == 0 && inArray == null) {
+                    inArray = new byte[inLen];
+                    inBuffer.get(inArray);
+                } else {
+                    inBuffer.position(origPos + inLen);
+                }
+                k = token.p11.C_EncryptUpdate(session.id(),
+                        inAddr, inArray, inOfs, inLen,
+                        outAddr, outArray, outOfs, outLen);
+            } else {
+                int newPadBufferLen = 0;
+                if (paddingObj != null) {
+                    if (padBufferLen != 0) {
+                        // NSS throws up when called with data not in multiple
+                        // of blocks. Try to work around this by holding the
+                        // extra data in padBuffer.
+                        if (padBufferLen != padBuffer.length) {
+                            int bufCapacity = padBuffer.length - padBufferLen;
+                            if (inLen > bufCapacity) {
+                                bufferInputBytes(inBuffer, bufCapacity);
+                                inOfs += bufCapacity;
+                                inLen -= bufCapacity;
+                            } else {
+                                bufferInputBytes(inBuffer, inLen);
+                                return 0;
+                            }
+                        }
+                        k = token.p11.C_DecryptUpdate(session.id(), 0,
+                                padBuffer, 0, padBufferLen, outAddr, outArray,
+                                outOfs, outLen);
+                        padBufferLen = 0;
+                    }
+                    newPadBufferLen = inLen & (blockSize - 1);
+                    if (newPadBufferLen == 0) {
+                        newPadBufferLen = padBuffer.length;
+                    }
+                    inLen -= newPadBufferLen;
+                }
+                if (inLen > 0) {
+                    if (inAddr == 0 && inArray == null) {
+                        inArray = new byte[inLen];
+                        inBuffer.get(inArray);
+                    } else {
+                        inBuffer.position(inBuffer.position() + inLen);
+                    }
+                    k += token.p11.C_DecryptUpdate(session.id(), inAddr,
+                            inArray, inOfs, inLen, outAddr, outArray,
+                            (outOfs + k), (outLen - k));
+                }
+                // update 'padBuffer' if using our own padding impl.
+                if (paddingObj != null && newPadBufferLen != 0) {
+                    bufferInputBytes(inBuffer, newPadBufferLen);
+                }
+            }
+            bytesBuffered += (inLen - k);
+            if (!(outBuffer instanceof DirectBuffer) &&
+                    !outBuffer.hasArray()) {
+                outBuffer.put(outArray, outOfs, k);
+            } else {
+                outBuffer.position(outBuffer.position() + k);
+            }
+            return k;
+        } catch (PKCS11Exception e) {
+            // Reset input buffer to its original position for
+            inBuffer.position(origPos);
+            if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+                throw (ShortBufferException)
+                        (new ShortBufferException().initCause(e));
+            }
+            reset(false);
+            throw new ProviderException("update() failed", e);
+        }
+    }
+
+    private int implDoFinal(byte[] out, int outOfs, int outLen)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int requiredOutLen = doFinalLength(0);
+        if (outLen < requiredOutLen) {
+            throw new ShortBufferException();
+        }
+        boolean doCancel = true;
+        try {
+            ensureInitialized();
+            int k = 0;
+            if (encrypt) {
+                if (paddingObj != null) {
+                    int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
+                            requiredOutLen - bytesBuffered);
+                    k = token.p11.C_EncryptUpdate(session.id(),
+                            0, padBuffer, 0, actualPadLen,
+                            0, out, outOfs, outLen);
+                }
+                k += token.p11.C_EncryptFinal(session.id(),
+                        0, out, (outOfs + k), (outLen - k));
+                doCancel = false;
+            } else {
+                // Special handling to match SunJCE provider behavior
+                if (bytesBuffered == 0 && padBufferLen == 0) {
+                    return 0;
+                }
+                if (paddingObj != null) {
+                    if (padBufferLen != 0) {
+                        k = token.p11.C_DecryptUpdate(session.id(), 0,
+                                padBuffer, 0, padBufferLen, 0, padBuffer, 0,
+                                padBuffer.length);
+                    }
+                    k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k,
+                            padBuffer.length - k);
+                    doCancel = false;
+
+                    int actualPadLen = paddingObj.unpad(padBuffer, k);
+                    k -= actualPadLen;
+                    System.arraycopy(padBuffer, 0, out, outOfs, k);
+                } else {
+                    k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs,
+                            outLen);
+                    doCancel = false;
+                }
+            }
+            return k;
+        } catch (PKCS11Exception e) {
+            doCancel = false;
+            handleException(e);
+            throw new ProviderException("doFinal() failed", e);
+        } finally {
+            reset(doCancel);
+        }
+    }
+
+    private int implDoFinal(ByteBuffer outBuffer)
+            throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        int outLen = outBuffer.remaining();
+        int requiredOutLen = doFinalLength(0);
+        if (outLen < requiredOutLen) {
+            throw new ShortBufferException();
+        }
+
+        boolean doCancel = true;
+        try {
+            ensureInitialized();
+
+            long outAddr = 0;
+            byte[] outArray = null;
+            int outOfs = 0;
+            if (outBuffer instanceof DirectBuffer) {
+                outAddr = ((DirectBuffer) outBuffer).address();
+                outOfs = outBuffer.position();
+            } else {
+                if (outBuffer.hasArray()) {
+                    outArray = outBuffer.array();
+                    outOfs = outBuffer.position() + outBuffer.arrayOffset();
+                } else {
+                    outArray = new byte[outLen];
+                }
+            }
+
+            int k = 0;
+
+            if (encrypt) {
+                if (paddingObj != null) {
+                    int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
+                            requiredOutLen - bytesBuffered);
+                    k = token.p11.C_EncryptUpdate(session.id(),
+                            0, padBuffer, 0, actualPadLen,
+                            outAddr, outArray, outOfs, outLen);
+                }
+                k += token.p11.C_EncryptFinal(session.id(),
+                        outAddr, outArray, (outOfs + k), (outLen - k));
+                doCancel = false;
+            } else {
+                // Special handling to match SunJCE provider behavior
+                if (bytesBuffered == 0 && padBufferLen == 0) {
+                    return 0;
+                }
+
+                if (paddingObj != null) {
+                    if (padBufferLen != 0) {
+                        k = token.p11.C_DecryptUpdate(session.id(),
+                                0, padBuffer, 0, padBufferLen,
+                                0, padBuffer, 0, padBuffer.length);
+                        padBufferLen = 0;
+                    }
+                    k += token.p11.C_DecryptFinal(session.id(),
+                            0, padBuffer, k, padBuffer.length - k);
+                    doCancel = false;
+
+                    int actualPadLen = paddingObj.unpad(padBuffer, k);
+                    k -= actualPadLen;
+                    outArray = padBuffer;
+                    outOfs = 0;
+                } else {
+                    k = token.p11.C_DecryptFinal(session.id(),
+                            outAddr, outArray, outOfs, outLen);
+                    doCancel = false;
+                }
+            }
+            if ((!encrypt && paddingObj != null) ||
+                    (!(outBuffer instanceof DirectBuffer) &&
+                    !outBuffer.hasArray())) {
+                outBuffer.put(outArray, outOfs, k);
+            } else {
+                outBuffer.position(outBuffer.position() + k);
+            }
+            return k;
+        } catch (PKCS11Exception e) {
+            doCancel = false;
+            handleException(e);
+            throw new ProviderException("doFinal() failed", e);
+        } finally {
+            reset(doCancel);
+        }
+    }
+
+    private void handleException(PKCS11Exception e)
+            throws ShortBufferException, IllegalBlockSizeException {
+        long errorCode = e.getErrorCode();
+        if (errorCode == CKR_BUFFER_TOO_SMALL) {
+            throw (ShortBufferException)
+                    (new ShortBufferException().initCause(e));
+        } else if (errorCode == CKR_DATA_LEN_RANGE ||
+                   errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
+            throw (IllegalBlockSizeException)
+                    (new IllegalBlockSizeException(e.toString()).initCause(e));
+        }
+    }
+
+    // see JCE spec
+    protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
+            InvalidKeyException {
+        // XXX key wrapping
+        throw new UnsupportedOperationException("engineWrap()");
+    }
+
+    // see JCE spec
+    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+            int wrappedKeyType)
+            throws InvalidKeyException, NoSuchAlgorithmException {
+        // XXX key unwrapping
+        throw new UnsupportedOperationException("engineUnwrap()");
+    }
+
+    // see JCE spec
+    @Override
+    protected int engineGetKeySize(Key key) throws InvalidKeyException {
+        int n = P11SecretKeyFactory.convertKey
+                (token, key, keyAlgorithm).length();
+        return n;
+    }
+
+    private final void bufferInputBytes(byte[] in, int inOfs, int len) {
+        System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
+        padBufferLen += len;
+        bytesBuffered += len;
+    }
+
+    private final void bufferInputBytes(ByteBuffer inBuffer, int len) {
+        inBuffer.get(padBuffer, padBufferLen, len);
+        padBufferLen += len;
+        bytesBuffered += len;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DHKeyFactory.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2003, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.math.BigInteger;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.interfaces.*;
+import javax.crypto.spec.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * DH KeyFactory implementation.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11DHKeyFactory extends P11KeyFactory {
+
+    P11DHKeyFactory(Token token, String algorithm) {
+        super(token, algorithm);
+    }
+
+    PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException {
+        try {
+            if (key instanceof DHPublicKey) {
+                DHPublicKey dhKey = (DHPublicKey)key;
+                DHParameterSpec params = dhKey.getParams();
+                return generatePublic(
+                    dhKey.getY(),
+                    params.getP(),
+                    params.getG()
+                );
+            } else if ("X.509".equals(key.getFormat())) {
+                // let SunJCE provider parse for us, then recurse
+                try {
+                    KeyFactory factory = implGetSoftwareFactory();
+                    key = (PublicKey)factory.translateKey(key);
+                    return implTranslatePublicKey(key);
+                } catch (GeneralSecurityException e) {
+                    throw new InvalidKeyException("Could not translate key", e);
+                }
+            } else {
+                throw new InvalidKeyException("PublicKey must be instance "
+                        + "of DHPublicKey or have X.509 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create DH public key", e);
+        }
+    }
+
+    PrivateKey implTranslatePrivateKey(PrivateKey key)
+            throws InvalidKeyException {
+        try {
+            if (key instanceof DHPrivateKey) {
+                DHPrivateKey dhKey = (DHPrivateKey)key;
+                DHParameterSpec params = dhKey.getParams();
+                return generatePrivate(
+                    dhKey.getX(),
+                    params.getP(),
+                    params.getG()
+                );
+            } else if ("PKCS#8".equals(key.getFormat())) {
+                // let SunJCE provider parse for us, then recurse
+                try {
+                    KeyFactory factory = implGetSoftwareFactory();
+                    key = (PrivateKey)factory.translateKey(key);
+                    return implTranslatePrivateKey(key);
+                } catch (GeneralSecurityException e) {
+                    throw new InvalidKeyException("Could not translate key", e);
+                }
+            } else {
+                throw new InvalidKeyException("PrivateKey must be instance "
+                        + "of DHPrivateKey or have PKCS#8 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create DH private key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PublicKey engineGeneratePublic(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof X509EncodedKeySpec) {
+            try {
+                KeyFactory factory = implGetSoftwareFactory();
+                PublicKey key = factory.generatePublic(keySpec);
+                return implTranslatePublicKey(key);
+            } catch (GeneralSecurityException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create DH public key", e);
+            }
+        }
+        if (keySpec instanceof DHPublicKeySpec == false) {
+            throw new InvalidKeySpecException("Only DHPublicKeySpec and "
+                + "X509EncodedKeySpec supported for DH public keys");
+        }
+        try {
+            DHPublicKeySpec ds = (DHPublicKeySpec)keySpec;
+            return generatePublic(
+                ds.getY(),
+                ds.getP(),
+                ds.getG()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create DH public key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof PKCS8EncodedKeySpec) {
+            try {
+                KeyFactory factory = implGetSoftwareFactory();
+                PrivateKey key = factory.generatePrivate(keySpec);
+                return implTranslatePrivateKey(key);
+            } catch (GeneralSecurityException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create DH private key", e);
+            }
+        }
+        if (keySpec instanceof DHPrivateKeySpec == false) {
+            throw new InvalidKeySpecException("Only DHPrivateKeySpec and "
+                + "PKCS8EncodedKeySpec supported for DH private keys");
+        }
+        try {
+            DHPrivateKeySpec ds = (DHPrivateKeySpec)keySpec;
+            return generatePrivate(
+                ds.getX(),
+                ds.getP(),
+                ds.getG()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create DH private key", e);
+        }
+    }
+
+    private PublicKey generatePublic(BigInteger y, BigInteger p, BigInteger g)
+            throws PKCS11Exception {
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
+            new CK_ATTRIBUTE(CKA_VALUE, y),
+            new CK_ATTRIBUTE(CKA_PRIME, p),
+            new CK_ATTRIBUTE(CKA_BASE, g),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PUBLIC_KEY, CKK_DH, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.publicKey
+                (session, keyID, "DH", p.bitLength(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private PrivateKey generatePrivate(BigInteger x, BigInteger p,
+            BigInteger g) throws PKCS11Exception {
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
+            new CK_ATTRIBUTE(CKA_VALUE, x),
+            new CK_ATTRIBUTE(CKA_PRIME, p),
+            new CK_ATTRIBUTE(CKA_BASE, g),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.privateKey
+                (session, keyID, "DH", p.bitLength(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (DHPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            KeySpec spec = new DHPublicKeySpec(
+                attributes[0].getBigInteger(),
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger()
+            );
+            return keySpec.cast(spec);
+        } else { // X.509 handled in superclass
+            throw new InvalidKeySpecException("Only DHPublicKeySpec and "
+                + "X509EncodedKeySpec supported for DH public keys");
+        }
+    }
+
+    <T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (DHPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            KeySpec spec = new DHPrivateKeySpec(
+                attributes[0].getBigInteger(),
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger()
+            );
+            return keySpec.cast(spec);
+        } else { // PKCS#8 handled in superclass
+            throw new InvalidKeySpecException("Only DHPrivateKeySpec "
+                + "and PKCS8EncodedKeySpec supported for DH private keys");
+        }
+    }
+
+    KeyFactory implGetSoftwareFactory() throws GeneralSecurityException {
+        return KeyFactory.getInstance("DH", P11Util.getSunJceProvider());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11DSAKeyFactory.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2003, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.math.BigInteger;
+
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * DSA KeyFactory implementation.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11DSAKeyFactory extends P11KeyFactory {
+
+    P11DSAKeyFactory(Token token, String algorithm) {
+        super(token, algorithm);
+    }
+
+    PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException {
+        try {
+            if (key instanceof DSAPublicKey) {
+                DSAPublicKey dsaKey = (DSAPublicKey)key;
+                DSAParams params = dsaKey.getParams();
+                return generatePublic(
+                    dsaKey.getY(),
+                    params.getP(),
+                    params.getQ(),
+                    params.getG()
+                );
+            } else if ("X.509".equals(key.getFormat())) {
+                // let Sun provider parse for us, then recurse
+                byte[] encoded = key.getEncoded();
+                key = new sun.security.provider.DSAPublicKey(encoded);
+                return implTranslatePublicKey(key);
+            } else {
+                throw new InvalidKeyException("PublicKey must be instance "
+                        + "of DSAPublicKey or have X.509 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create DSA public key", e);
+        }
+    }
+
+    PrivateKey implTranslatePrivateKey(PrivateKey key)
+            throws InvalidKeyException {
+        try {
+            if (key instanceof DSAPrivateKey) {
+                DSAPrivateKey dsaKey = (DSAPrivateKey)key;
+                DSAParams params = dsaKey.getParams();
+                return generatePrivate(
+                    dsaKey.getX(),
+                    params.getP(),
+                    params.getQ(),
+                    params.getG()
+                );
+            } else if ("PKCS#8".equals(key.getFormat())) {
+                // let Sun provider parse for us, then recurse
+                byte[] encoded = key.getEncoded();
+                key = new sun.security.provider.DSAPrivateKey(encoded);
+                return implTranslatePrivateKey(key);
+            } else {
+                throw new InvalidKeyException("PrivateKey must be instance "
+                        + "of DSAPrivateKey or have PKCS#8 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create DSA private key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PublicKey engineGeneratePublic(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof X509EncodedKeySpec) {
+            try {
+                byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded();
+                PublicKey key = new sun.security.provider.DSAPublicKey(encoded);
+                return implTranslatePublicKey(key);
+            } catch (InvalidKeyException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create DSA public key", e);
+            }
+        }
+        if (keySpec instanceof DSAPublicKeySpec == false) {
+            throw new InvalidKeySpecException("Only DSAPublicKeySpec and "
+                + "X509EncodedKeySpec supported for DSA public keys");
+        }
+        try {
+            DSAPublicKeySpec ds = (DSAPublicKeySpec)keySpec;
+            return generatePublic(
+                ds.getY(),
+                ds.getP(),
+                ds.getQ(),
+                ds.getG()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create DSA public key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof PKCS8EncodedKeySpec) {
+            try {
+                byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
+                PrivateKey key = new sun.security.provider.DSAPrivateKey(encoded);
+                return implTranslatePrivateKey(key);
+            } catch (GeneralSecurityException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create DSA private key", e);
+            }
+        }
+        if (keySpec instanceof DSAPrivateKeySpec == false) {
+            throw new InvalidKeySpecException("Only DSAPrivateKeySpec and "
+                + "PKCS8EncodedKeySpec supported for DSA private keys");
+        }
+        try {
+            DSAPrivateKeySpec ds = (DSAPrivateKeySpec)keySpec;
+            return generatePrivate(
+                ds.getX(),
+                ds.getP(),
+                ds.getQ(),
+                ds.getG()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create DSA private key", e);
+        }
+    }
+
+    private PublicKey generatePublic(BigInteger y, BigInteger p, BigInteger q,
+            BigInteger g) throws PKCS11Exception {
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
+            new CK_ATTRIBUTE(CKA_VALUE, y),
+            new CK_ATTRIBUTE(CKA_PRIME, p),
+            new CK_ATTRIBUTE(CKA_SUBPRIME, q),
+            new CK_ATTRIBUTE(CKA_BASE, g),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PUBLIC_KEY, CKK_DSA, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.publicKey
+                (session, keyID, "DSA", p.bitLength(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private PrivateKey generatePrivate(BigInteger x, BigInteger p,
+            BigInteger q, BigInteger g) throws PKCS11Exception {
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
+            new CK_ATTRIBUTE(CKA_VALUE, x),
+            new CK_ATTRIBUTE(CKA_PRIME, p),
+            new CK_ATTRIBUTE(CKA_SUBPRIME, q),
+            new CK_ATTRIBUTE(CKA_BASE, g),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.privateKey
+                (session, keyID, "DSA", p.bitLength(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (DSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_SUBPRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            KeySpec spec = new DSAPublicKeySpec(
+                attributes[0].getBigInteger(),
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger(),
+                attributes[3].getBigInteger()
+            );
+            return keySpec.cast(spec);
+        } else { // X.509 handled in superclass
+            throw new InvalidKeySpecException("Only DSAPublicKeySpec and "
+                + "X509EncodedKeySpec supported for DSA public keys");
+        }
+    }
+
+    <T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (DSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_SUBPRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            KeySpec spec = new DSAPrivateKeySpec(
+                attributes[0].getBigInteger(),
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger(),
+                attributes[3].getBigInteger()
+            );
+            return keySpec.cast(spec);
+        } else { // PKCS#8 handled in superclass
+            throw new InvalidKeySpecException("Only DSAPrivateKeySpec "
+                + "and PKCS8EncodedKeySpec supported for DSA private keys");
+        }
+    }
+
+    KeyFactory implGetSoftwareFactory() throws GeneralSecurityException {
+        return KeyFactory.getInstance("DSA", P11Util.getSunProvider());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Digest.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2003, 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.util.*;
+import java.nio.ByteBuffer;
+
+import java.security.*;
+
+import javax.crypto.SecretKey;
+
+import sun.nio.ch.DirectBuffer;
+
+import sun.security.util.MessageDigestSpi2;
+
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * MessageDigest implementation class. This class currently supports
+ * MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512.
+ *
+ * Note that many digest operations are on fairly small amounts of data
+ * (less than 100 bytes total). For example, the 2nd hashing in HMAC or
+ * the PRF in TLS. In order to speed those up, we use some buffering to
+ * minimize number of the Java->native transitions.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11Digest extends MessageDigestSpi implements Cloneable,
+    MessageDigestSpi2 {
+
+    /* fields initialized, no session acquired */
+    private final static int S_BLANK    = 1;
+
+    /* data in buffer, session acquired, but digest not initialized */
+    private final static int S_BUFFERED = 2;
+
+    /* session initialized for digesting */
+    private final static int S_INIT     = 3;
+
+    private final static int BUFFER_SIZE = 96;
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // mechanism id object
+    private final CK_MECHANISM mechanism;
+
+    // length of the digest in bytes
+    private final int digestLength;
+
+    // associated session, if any
+    private Session session;
+
+    // current state, one of S_* above
+    private int state;
+
+    // buffer to reduce number of JNI calls
+    private byte[] buffer;
+
+    // offset into the buffer
+    private int bufOfs;
+
+    P11Digest(Token token, String algorithm, long mechanism) {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = new CK_MECHANISM(mechanism);
+        switch ((int)mechanism) {
+        case (int)CKM_MD2:
+        case (int)CKM_MD5:
+            digestLength = 16;
+            break;
+        case (int)CKM_SHA_1:
+            digestLength = 20;
+            break;
+        case (int)CKM_SHA224:
+            digestLength = 28;
+            break;
+        case (int)CKM_SHA256:
+            digestLength = 32;
+            break;
+        case (int)CKM_SHA384:
+            digestLength = 48;
+            break;
+        case (int)CKM_SHA512:
+            digestLength = 64;
+            break;
+        default:
+            throw new ProviderException("Unknown mechanism: " + mechanism);
+        }
+        buffer = new byte[BUFFER_SIZE];
+        state = S_BLANK;
+    }
+
+    // see JCA spec
+    protected int engineGetDigestLength() {
+        return digestLength;
+    }
+
+    private void fetchSession() {
+        token.ensureValid();
+        if (state == S_BLANK) {
+            try {
+                session = token.getOpSession();
+                state = S_BUFFERED;
+            } catch (PKCS11Exception e) {
+                throw new ProviderException("No more session available", e);
+            }
+        }
+    }
+
+    // see JCA spec
+    protected void engineReset() {
+        token.ensureValid();
+
+        if (session != null) {
+            if (state == S_INIT && token.explicitCancel == true) {
+                session = token.killSession(session);
+            } else {
+                session = token.releaseSession(session);
+            }
+        }
+        state = S_BLANK;
+        bufOfs = 0;
+    }
+
+    // see JCA spec
+    protected byte[] engineDigest() {
+        try {
+            byte[] digest = new byte[digestLength];
+            int n = engineDigest(digest, 0, digestLength);
+            return digest;
+        } catch (DigestException e) {
+            throw new ProviderException("internal error", e);
+        }
+    }
+
+    // see JCA spec
+    protected int engineDigest(byte[] digest, int ofs, int len)
+            throws DigestException {
+        if (len < digestLength) {
+            throw new DigestException("Length must be at least " +
+                    digestLength);
+        }
+
+        fetchSession();
+        try {
+            int n;
+            if (state == S_BUFFERED) {
+                n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0,
+                        bufOfs, digest, ofs, len);
+                bufOfs = 0;
+            } else {
+                if (bufOfs != 0) {
+                    token.p11.C_DigestUpdate(session.id(), 0, buffer, 0,
+                            bufOfs);
+                    bufOfs = 0;
+                }
+                n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
+            }
+            if (n != digestLength) {
+                throw new ProviderException("internal digest length error");
+            }
+            return n;
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("digest() failed", e);
+        } finally {
+            engineReset();
+        }
+    }
+
+    // see JCA spec
+    protected void engineUpdate(byte in) {
+        byte[] temp = { in };
+        engineUpdate(temp, 0, 1);
+    }
+
+    // see JCA spec
+    protected void engineUpdate(byte[] in, int ofs, int len) {
+        if (len <= 0) {
+            return;
+        }
+
+        fetchSession();
+        try {
+            if (state == S_BUFFERED) {
+                token.p11.C_DigestInit(session.id(), mechanism);
+                state = S_INIT;
+            }
+            if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
+                // process the buffered data
+                token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
+                bufOfs = 0;
+            }
+            if (bufOfs + len > buffer.length) {
+                // process the new data
+                token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
+             } else {
+                // buffer the new data
+                System.arraycopy(in, ofs, buffer, bufOfs, len);
+                bufOfs += len;
+            }
+        } catch (PKCS11Exception e) {
+            engineReset();
+            throw new ProviderException("update() failed", e);
+        }
+    }
+
+    // Called by SunJSSE via reflection during the SSL 3.0 handshake if
+    // the master secret is sensitive.
+    // Note: Change to protected after this method is moved from
+    // sun.security.util.MessageSpi2 interface to
+    // java.security.MessageDigestSpi class
+    public void engineUpdate(SecretKey key) throws InvalidKeyException {
+        // SunJSSE calls this method only if the key does not have a RAW
+        // encoding, i.e. if it is sensitive. Therefore, no point in calling
+        // SecretKeyFactory to try to convert it. Just verify it ourselves.
+        if (key instanceof P11Key == false) {
+            throw new InvalidKeyException("Not a P11Key: " + key);
+        }
+        P11Key p11Key = (P11Key)key;
+        if (p11Key.token != token) {
+            throw new InvalidKeyException("Not a P11Key of this provider: " +
+                    key);
+        }
+
+        fetchSession();
+        try {
+            if (state == S_BUFFERED) {
+                token.p11.C_DigestInit(session.id(), mechanism);
+                state = S_INIT;
+            }
+
+            if (bufOfs != 0) {
+                token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
+                bufOfs = 0;
+            }
+            token.p11.C_DigestKey(session.id(), p11Key.keyID);
+        } catch (PKCS11Exception e) {
+            engineReset();
+            throw new ProviderException("update(SecretKey) failed", e);
+        }
+    }
+
+    // see JCA spec
+    protected void engineUpdate(ByteBuffer byteBuffer) {
+        int len = byteBuffer.remaining();
+        if (len <= 0) {
+            return;
+        }
+
+        if (byteBuffer instanceof DirectBuffer == false) {
+            super.engineUpdate(byteBuffer);
+            return;
+        }
+
+        fetchSession();
+        long addr = ((DirectBuffer)byteBuffer).address();
+        int ofs = byteBuffer.position();
+        try {
+            if (state == S_BUFFERED) {
+                token.p11.C_DigestInit(session.id(), mechanism);
+                state = S_INIT;
+            }
+            if (bufOfs != 0) {
+                token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
+                bufOfs = 0;
+            }
+            token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);
+            byteBuffer.position(ofs + len);
+        } catch (PKCS11Exception e) {
+            engineReset();
+            throw new ProviderException("update() failed", e);
+        }
+    }
+
+    public Object clone() throws CloneNotSupportedException {
+        P11Digest copy = (P11Digest) super.clone();
+        copy.buffer = buffer.clone();
+        try {
+            if (session != null) {
+                copy.session = copy.token.getOpSession();
+            }
+            if (state == S_INIT) {
+                byte[] stateValues =
+                    token.p11.C_GetOperationState(session.id());
+                token.p11.C_SetOperationState(copy.session.id(),
+                                              stateValues, 0, 0);
+            }
+        } catch (PKCS11Exception e) {
+            throw (CloneNotSupportedException)
+                (new CloneNotSupportedException(algorithm).initCause(e));
+        }
+        return copy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECDHKeyAgreement.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2006, 2007, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.security.*;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * KeyAgreement implementation for ECDH.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.6
+ */
+final class P11ECDHKeyAgreement extends KeyAgreementSpi {
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // mechanism id
+    private final long mechanism;
+
+    // private key, if initialized
+    private P11Key privateKey;
+
+    // encoded public point, non-null between doPhase() and generateSecret() only
+    private byte[] publicValue;
+
+    // length of the secret to be derived
+    private int secretLen;
+
+    P11ECDHKeyAgreement(Token token, String algorithm, long mechanism) {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = mechanism;
+    }
+
+    // see JCE spec
+    protected void engineInit(Key key, SecureRandom random)
+            throws InvalidKeyException {
+        if (key instanceof PrivateKey == false) {
+            throw new InvalidKeyException
+                        ("Key must be instance of PrivateKey");
+        }
+        privateKey = P11KeyFactory.convertKey(token, key, "EC");
+        publicValue = null;
+    }
+
+    // see JCE spec
+    protected void engineInit(Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException
+                        ("Parameters not supported");
+        }
+        engineInit(key, random);
+    }
+
+    // see JCE spec
+    protected Key engineDoPhase(Key key, boolean lastPhase)
+            throws InvalidKeyException, IllegalStateException {
+        if (privateKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        if (publicValue != null) {
+            throw new IllegalStateException("Phase already executed");
+        }
+        if (lastPhase == false) {
+            throw new IllegalStateException
+                ("Only two party agreement supported, lastPhase must be true");
+        }
+        if (key instanceof ECPublicKey == false) {
+            throw new InvalidKeyException
+                ("Key must be a PublicKey with algorithm EC");
+        }
+        ECPublicKey ecKey = (ECPublicKey)key;
+        int keyLenBits = ecKey.getParams().getCurve().getField().getFieldSize();
+        secretLen = (keyLenBits + 7) >> 3;
+        publicValue = P11ECKeyFactory.getEncodedPublicValue(ecKey);
+        return null;
+    }
+
+    // see JCE spec
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        if ((privateKey == null) || (publicValue == null)) {
+            throw new IllegalStateException("Not initialized correctly");
+        }
+        Session session = null;
+        try {
+            session = token.getOpSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
+            };
+            CK_ECDH1_DERIVE_PARAMS ckParams =
+                    new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
+            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);
+            attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE)
+            };
+            token.p11.C_GetAttributeValue(session.id(), keyID, attributes);
+            byte[] secret = attributes[0].getByteArray();
+            token.p11.C_DestroyObject(session.id(), keyID);
+            return secret;
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("Could not derive key", e);
+        } finally {
+            publicValue = null;
+            token.releaseSession(session);
+        }
+    }
+
+    // see JCE spec
+    protected int engineGenerateSecret(byte[] sharedSecret, int
+            offset) throws IllegalStateException, ShortBufferException {
+        if (offset + secretLen > sharedSecret.length) {
+            throw new ShortBufferException("Need " + secretLen
+                + " bytes, only " + (sharedSecret.length - offset) + " available");
+        }
+        byte[] secret = engineGenerateSecret();
+        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
+        return secret.length;
+    }
+
+    // see JCE spec
+    protected SecretKey engineGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException,
+            InvalidKeyException {
+        if (algorithm == null) {
+            throw new NoSuchAlgorithmException("Algorithm must not be null");
+        }
+        if (algorithm.equals("TlsPremasterSecret") == false) {
+            throw new NoSuchAlgorithmException
+                ("Only supported for algorithm TlsPremasterSecret");
+        }
+        return nativeGenerateSecret(algorithm);
+    }
+
+    private SecretKey nativeGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException,
+            InvalidKeyException {
+        if ((privateKey == null) || (publicValue == null)) {
+            throw new IllegalStateException("Not initialized correctly");
+        }
+        long keyType = CKK_GENERIC_SECRET;
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
+            };
+            CK_ECDH1_DERIVE_PARAMS ckParams =
+                    new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
+            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);
+            CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE_LEN),
+            };
+            token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes);
+            int keyLen = (int)lenAttributes[0].getLong();
+            SecretKey key = P11Key.secretKey
+                        (session, keyID, algorithm, keyLen << 3, attributes);
+            return key;
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not derive key", e);
+        } finally {
+            publicValue = null;
+            token.releaseSession(session);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11ECKeyFactory.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2006, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+import sun.security.util.DerValue;
+import sun.security.util.ECUtil;
+
+/**
+ * EC KeyFactory implementation.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.6
+ */
+final class P11ECKeyFactory extends P11KeyFactory {
+    private static Provider sunECprovider;
+
+    private static Provider getSunECProvider() {
+        if (sunECprovider == null) {
+            sunECprovider = Security.getProvider("SunEC");
+            if (sunECprovider == null) {
+                throw new RuntimeException("Cannot load SunEC provider");
+            }
+        }
+
+        return sunECprovider;
+    }
+
+    P11ECKeyFactory(Token token, String algorithm) {
+        super(token, algorithm);
+    }
+
+    static ECParameterSpec getECParameterSpec(String name) {
+        return ECUtil.getECParameterSpec(getSunECProvider(), name);
+    }
+
+    static ECParameterSpec getECParameterSpec(int keySize) {
+        return ECUtil.getECParameterSpec(getSunECProvider(), keySize);
+    }
+
+    // Check that spec is a known supported curve and convert it to our
+    // ECParameterSpec subclass. If not possible, return null.
+    static ECParameterSpec getECParameterSpec(ECParameterSpec spec) {
+        return ECUtil.getECParameterSpec(getSunECProvider(), spec);
+    }
+
+    static ECParameterSpec decodeParameters(byte[] params) throws IOException {
+        return ECUtil.getECParameterSpec(getSunECProvider(), params);
+    }
+
+    static byte[] encodeParameters(ECParameterSpec params) {
+        return ECUtil.encodeECParameterSpec(getSunECProvider(), params);
+    }
+
+    static ECPoint decodePoint(byte[] encoded, EllipticCurve curve) throws IOException {
+        return ECUtil.decodePoint(encoded, curve);
+    }
+
+    // Used by ECDH KeyAgreement
+    static byte[] getEncodedPublicValue(PublicKey key) throws InvalidKeyException {
+        if (key instanceof ECPublicKey) {
+            ECPublicKey ecKey = (ECPublicKey)key;
+            ECPoint w = ecKey.getW();
+            ECParameterSpec params = ecKey.getParams();
+            return ECUtil.encodePoint(w, params.getCurve());
+        } else {
+            // should never occur
+            throw new InvalidKeyException
+                ("Key class not yet supported: " + key.getClass().getName());
+        }
+    }
+
+    PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException {
+        try {
+            if (key instanceof ECPublicKey) {
+                ECPublicKey ecKey = (ECPublicKey)key;
+                return generatePublic(
+                    ecKey.getW(),
+                    ecKey.getParams()
+                );
+            } else if ("X.509".equals(key.getFormat())) {
+                // let Sun provider parse for us, then recurse
+                byte[] encoded = key.getEncoded();
+
+                try {
+                    key = ECUtil.decodeX509ECPublicKey(encoded);
+                } catch (InvalidKeySpecException ikse) {
+                    throw new InvalidKeyException(ikse);
+                }
+
+                return implTranslatePublicKey(key);
+            } else {
+                throw new InvalidKeyException("PublicKey must be instance "
+                        + "of ECPublicKey or have X.509 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create EC public key", e);
+        }
+    }
+
+    PrivateKey implTranslatePrivateKey(PrivateKey key)
+            throws InvalidKeyException {
+        try {
+            if (key instanceof ECPrivateKey) {
+                ECPrivateKey ecKey = (ECPrivateKey)key;
+                return generatePrivate(
+                    ecKey.getS(),
+                    ecKey.getParams()
+                );
+            } else if ("PKCS#8".equals(key.getFormat())) {
+                // let Sun provider parse for us, then recurse
+                byte[] encoded = key.getEncoded();
+
+                try {
+                    key = ECUtil.decodePKCS8ECPrivateKey(encoded);
+                } catch (InvalidKeySpecException ikse) {
+                    throw new InvalidKeyException(ikse);
+                }
+
+                return implTranslatePrivateKey(key);
+            } else {
+                throw new InvalidKeyException("PrivateKey must be instance "
+                        + "of ECPrivateKey or have PKCS#8 encoding");
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not create EC private key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PublicKey engineGeneratePublic(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof X509EncodedKeySpec) {
+            try {
+                byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded();
+                PublicKey key = ECUtil.decodeX509ECPublicKey(encoded);
+                return implTranslatePublicKey(key);
+            } catch (InvalidKeyException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create EC public key", e);
+            }
+        }
+        if (keySpec instanceof ECPublicKeySpec == false) {
+            throw new InvalidKeySpecException("Only ECPublicKeySpec and "
+                + "X509EncodedKeySpec supported for EC public keys");
+        }
+        try {
+            ECPublicKeySpec ec = (ECPublicKeySpec)keySpec;
+            return generatePublic(
+                ec.getW(),
+                ec.getParams()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create EC public key", e);
+        }
+    }
+
+    // see JCA spec
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if (keySpec instanceof PKCS8EncodedKeySpec) {
+            try {
+                byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded();
+                PrivateKey key = ECUtil.decodePKCS8ECPrivateKey(encoded);
+                return implTranslatePrivateKey(key);
+            } catch (GeneralSecurityException e) {
+                throw new InvalidKeySpecException
+                        ("Could not create EC private key", e);
+            }
+        }
+        if (keySpec instanceof ECPrivateKeySpec == false) {
+            throw new InvalidKeySpecException("Only ECPrivateKeySpec and "
+                + "PKCS8EncodedKeySpec supported for EC private keys");
+        }
+        try {
+            ECPrivateKeySpec ec = (ECPrivateKeySpec)keySpec;
+            return generatePrivate(
+                ec.getS(),
+                ec.getParams()
+            );
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException
+                ("Could not create EC private key", e);
+        }
+    }
+
+    private PublicKey generatePublic(ECPoint point, ECParameterSpec params)
+            throws PKCS11Exception {
+        byte[] encodedParams =
+            ECUtil.encodeECParameterSpec(getSunECProvider(), params);
+        byte[] encodedPoint =
+            ECUtil.encodePoint(point, params.getCurve());
+
+        // Check whether the X9.63 encoding of an EC point shall be wrapped
+        // in an ASN.1 OCTET STRING
+        if (!token.config.getUseEcX963Encoding()) {
+            try {
+                encodedPoint =
+                    new DerValue(DerValue.tag_OctetString, encodedPoint)
+                        .toByteArray();
+            } catch (IOException e) {
+                throw new
+                    IllegalArgumentException("Could not DER encode point", e);
+            }
+        }
+
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
+            new CK_ATTRIBUTE(CKA_EC_POINT, encodedPoint),
+            new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PUBLIC_KEY, CKK_EC, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.publicKey
+                (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private PrivateKey generatePrivate(BigInteger s, ECParameterSpec params)
+            throws PKCS11Exception {
+        byte[] encodedParams =
+            ECUtil.encodeECParameterSpec(getSunECProvider(), params);
+        CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY),
+            new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
+            new CK_ATTRIBUTE(CKA_VALUE, s),
+            new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
+        };
+        attributes = token.getAttributes
+                (O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attributes);
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            long keyID = token.p11.C_CreateObject(session.id(), attributes);
+            return P11Key.privateKey
+                (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    <T extends KeySpec> T implGetPublicKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_EC_POINT),
+                new CK_ATTRIBUTE(CKA_EC_PARAMS),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            try {
+                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);
+            }
+        } else { // X.509 handled in superclass
+            throw new InvalidKeySpecException("Only ECPublicKeySpec and "
+                + "X509EncodedKeySpec supported for EC public keys");
+        }
+    }
+
+    <T extends KeySpec> T implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException {
+        if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            session[0] = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_EC_PARAMS),
+            };
+            token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes);
+            try {
+                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);
+            }
+        } else { // PKCS#8 handled in superclass
+            throw new InvalidKeySpecException("Only ECPrivateKeySpec "
+                + "and PKCS8EncodedKeySpec supported for EC private keys");
+        }
+    }
+
+    KeyFactory implGetSoftwareFactory() throws GeneralSecurityException {
+        return KeyFactory.getInstance("EC", getSunECProvider());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (c) 2003, 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.io.*;
+import java.lang.ref.*;
+import java.math.BigInteger;
+import java.util.*;
+
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import javax.crypto.*;
+import javax.crypto.interfaces.*;
+import javax.crypto.spec.*;
+
+import sun.security.rsa.RSAPublicKeyImpl;
+
+import sun.security.internal.interfaces.TlsMasterSecret;
+
+import sun.security.pkcs11.wrapper.*;
+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;
+
+/**
+ * Key implementation classes.
+ *
+ * In PKCS#11, the components of private and secret keys may or may not
+ * be accessible. If they are, we use the algorithm specific key classes
+ * (e.g. DSAPrivateKey) for compatibility with existing applications.
+ * If the components are not accessible, we use a generic class that
+ * only implements PrivateKey (or SecretKey). Whether the components of a
+ * key are extractable is automatically determined when the key object is
+ * created.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+abstract class P11Key implements Key, Length {
+
+    private static final long serialVersionUID = -2575874101938349339L;
+
+    private final static String PUBLIC = "public";
+    private final static String PRIVATE = "private";
+    private final static String SECRET = "secret";
+
+    // type of key, one of (PUBLIC, PRIVATE, SECRET)
+    final String type;
+
+    // token instance
+    final Token token;
+
+    // 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;
+
+    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;
+        boolean sensitive = false;
+        boolean extractable = true;
+        int n = (attributes == null) ? 0 : attributes.length;
+        for (int i = 0; i < n; i++) {
+            CK_ATTRIBUTE attr = attributes[i];
+            if (attr.type == CKA_TOKEN) {
+                tokenObject = attr.getBoolean();
+            } else if (attr.type == CKA_SENSITIVE) {
+                sensitive = attr.getBoolean();
+            } else if (attr.type == CKA_EXTRACTABLE) {
+                extractable = attr.getBoolean();
+            }
+        }
+        this.tokenObject = tokenObject;
+        this.sensitive = sensitive;
+        this.extractable = extractable;
+        if (tokenObject == false) {
+            sessionKeyRef = new SessionKeyRef(this, keyID, session);
+        } else {
+            sessionKeyRef = null;
+        }
+    }
+
+    // see JCA spec
+    public final String getAlgorithm() {
+        token.ensureValid();
+        return algorithm;
+    }
+
+    // see JCA spec
+    public final byte[] getEncoded() {
+        byte[] b = getEncodedInternal();
+        return (b == null) ? null : b.clone();
+    }
+
+    abstract byte[] getEncodedInternal();
+
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        // equals() should never throw exceptions
+        if (token.isValid() == false) {
+            return false;
+        }
+        if (obj instanceof Key == false) {
+            return false;
+        }
+        String thisFormat = getFormat();
+        if (thisFormat == null) {
+            // no encoding, key only equal to itself
+            // XXX getEncoded() for unextractable keys will change that
+            return false;
+        }
+        Key other = (Key)obj;
+        if (thisFormat.equals(other.getFormat()) == false) {
+            return false;
+        }
+        byte[] thisEnc = this.getEncodedInternal();
+        byte[] otherEnc;
+        if (obj instanceof P11Key) {
+            otherEnc = ((P11Key)other).getEncodedInternal();
+        } else {
+            otherEnc = other.getEncoded();
+        }
+        return MessageDigest.isEqual(thisEnc, otherEnc);
+    }
+
+    public int hashCode() {
+        // hashCode() should never throw exceptions
+        if (token.isValid() == false) {
+            return 0;
+        }
+        byte[] b1 = getEncodedInternal();
+        if (b1 == null) {
+            return 0;
+        }
+        int r = b1.length;
+        for (int i = 0; i < b1.length; i++) {
+            r += (b1[i] & 0xff) * 37;
+        }
+        return r;
+    }
+
+    protected Object writeReplace() throws ObjectStreamException {
+        KeyRep.Type type;
+        String format = getFormat();
+        if (isPrivate() && "PKCS#8".equals(format)) {
+            type = KeyRep.Type.PRIVATE;
+        } else if (isPublic() && "X.509".equals(format)) {
+            type = KeyRep.Type.PUBLIC;
+        } else if (isSecret() && "RAW".equals(format)) {
+            type = KeyRep.Type.SECRET;
+        } else {
+            // XXX short term serialization for unextractable keys
+            throw new NotSerializableException
+                ("Cannot serialize sensitive and unextractable keys");
+        }
+        return new KeyRep(type, getAlgorithm(), format, getEncoded());
+    }
+
+    public String toString() {
+        token.ensureValid();
+        String s1 = token.provider.getName() + " " + algorithm + " " + type
+                + " key, " + keyLength + " bits";
+        s1 += " (id " + keyID + ", "
+                + (tokenObject ? "token" : "session") + " object";
+        if (isPublic()) {
+            s1 += ")";
+        } else {
+            s1 += ", " + (sensitive ? "" : "not ") + "sensitive";
+            s1 += ", " + (extractable ? "" : "un") + "extractable)";
+        }
+        return s1;
+    }
+
+    /**
+     * Return bit length of the key.
+     */
+    @Override
+    public int length() {
+        return keyLength;
+    }
+
+    boolean isPublic() {
+        return type == PUBLIC;
+    }
+
+    boolean isPrivate() {
+        return type == PRIVATE;
+    }
+
+    boolean isSecret() {
+        return type == SECRET;
+    }
+
+    void fetchAttributes(CK_ATTRIBUTE[] attributes) {
+        Session tempSession = null;
+        try {
+            tempSession = token.getOpSession();
+            token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes);
+        } catch (PKCS11Exception e) {
+            throw new ProviderException(e);
+        } finally {
+            token.releaseSession(tempSession);
+        }
+    }
+
+    private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0];
+
+    private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID,
+            CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) {
+        if (knownAttributes == null) {
+            knownAttributes = A0;
+        }
+        for (int i = 0; i < desiredAttributes.length; i++) {
+            // For each desired attribute, check to see if we have the value
+            // available already. If everything is here, we save a native call.
+            CK_ATTRIBUTE attr = desiredAttributes[i];
+            for (CK_ATTRIBUTE known : knownAttributes) {
+                if ((attr.type == known.type) && (known.pValue != null)) {
+                    attr.pValue = known.pValue;
+                    break; // break inner for loop
+                }
+            }
+            if (attr.pValue == null) {
+                // nothing found, need to call C_GetAttributeValue()
+                for (int j = 0; j < i; j++) {
+                    // clear values copied from knownAttributes
+                    desiredAttributes[j].pValue = null;
+                }
+                try {
+                    session.token.p11.C_GetAttributeValue
+                            (session.id(), keyID, desiredAttributes);
+                } catch (PKCS11Exception e) {
+                    throw new ProviderException(e);
+                }
+                break; // break loop, goto return
+            }
+        }
+        return desiredAttributes;
+    }
+
+    static SecretKey secretKey(Session session, long keyID, String algorithm,
+            int keyLength, CK_ATTRIBUTE[] attributes) {
+        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_TOKEN),
+            new CK_ATTRIBUTE(CKA_SENSITIVE),
+            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
+        });
+        return new P11SecretKey(session, keyID, algorithm, keyLength, attributes);
+    }
+
+    static SecretKey masterSecretKey(Session session, long keyID, String algorithm,
+            int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
+        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_TOKEN),
+            new CK_ATTRIBUTE(CKA_SENSITIVE),
+            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
+        });
+        return new P11TlsMasterSecretKey
+                (session, keyID, algorithm, keyLength, attributes, major, minor);
+    }
+
+    // we assume that all components of public keys are always accessible
+    static PublicKey publicKey(Session session, long keyID, String algorithm,
+            int keyLength, CK_ATTRIBUTE[] attributes) {
+        switch (algorithm) {
+            case "RSA":
+                return new P11RSAPublicKey
+                    (session, keyID, algorithm, keyLength, attributes);
+            case "DSA":
+                return new P11DSAPublicKey
+                    (session, keyID, algorithm, keyLength, attributes);
+            case "DH":
+                return new P11DHPublicKey
+                    (session, keyID, algorithm, keyLength, attributes);
+            case "EC":
+                return new P11ECPublicKey
+                    (session, keyID, algorithm, keyLength, attributes);
+            default:
+                throw new ProviderException
+                    ("Unknown public key algorithm " + algorithm);
+        }
+    }
+
+    static PrivateKey privateKey(Session session, long keyID, String algorithm,
+            int keyLength, CK_ATTRIBUTE[] attributes) {
+        attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] {
+            new CK_ATTRIBUTE(CKA_TOKEN),
+            new CK_ATTRIBUTE(CKA_SENSITIVE),
+            new CK_ATTRIBUTE(CKA_EXTRACTABLE),
+        });
+        if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) {
+            return new P11PrivateKey
+                (session, keyID, algorithm, keyLength, attributes);
+        } else {
+            switch (algorithm) {
+                case "RSA":
+                    // In order to decide if this is RSA CRT key, we first query
+                    // and see if all extra CRT attributes are available.
+                    CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] {
+                        new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
+                        new CK_ATTRIBUTE(CKA_PRIME_1),
+                        new CK_ATTRIBUTE(CKA_PRIME_2),
+                        new CK_ATTRIBUTE(CKA_EXPONENT_1),
+                        new CK_ATTRIBUTE(CKA_EXPONENT_2),
+                        new CK_ATTRIBUTE(CKA_COEFFICIENT),
+                    };
+                    boolean crtKey;
+                    try {
+                        session.token.p11.C_GetAttributeValue
+                            (session.id(), keyID, attrs2);
+                        crtKey = ((attrs2[0].pValue instanceof byte[]) &&
+                                  (attrs2[1].pValue instanceof byte[]) &&
+                                  (attrs2[2].pValue instanceof byte[]) &&
+                                  (attrs2[3].pValue instanceof byte[]) &&
+                                  (attrs2[4].pValue instanceof byte[]) &&
+                                  (attrs2[5].pValue instanceof byte[])) ;
+                    } catch (PKCS11Exception e) {
+                        // ignore, assume not available
+                        crtKey = false;
+                    }
+                    if (crtKey) {
+                        return new P11RSAPrivateKey
+                                (session, keyID, algorithm, keyLength, attributes, attrs2);
+                    } else {
+                        return new P11RSAPrivateNonCRTKey
+                                (session, keyID, algorithm, keyLength, attributes);
+                    }
+                case "DSA":
+                    return new P11DSAPrivateKey
+                            (session, keyID, algorithm, keyLength, attributes);
+                case "DH":
+                    return new P11DHPrivateKey
+                            (session, keyID, algorithm, keyLength, attributes);
+                case "EC":
+                    return new P11ECPrivateKey
+                            (session, keyID, algorithm, keyLength, attributes);
+                default:
+                    throw new ProviderException
+                            ("Unknown private key algorithm " + algorithm);
+            }
+        }
+    }
+
+    // class for sensitive and unextractable private keys
+    private static final class P11PrivateKey extends P11Key
+                                                implements PrivateKey {
+        private static final long serialVersionUID = -2138581185214187615L;
+
+        P11PrivateKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+        }
+        // XXX temporary encoding for serialization purposes
+        public String getFormat() {
+            token.ensureValid();
+            return null;
+        }
+        byte[] getEncodedInternal() {
+            token.ensureValid();
+            return null;
+        }
+    }
+
+    private static class P11SecretKey extends P11Key implements SecretKey {
+        private static final long serialVersionUID = -7828241727014329084L;
+        private volatile byte[] encoded;
+        P11SecretKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(SECRET, session, keyID, algorithm, keyLength, attributes);
+        }
+        public String getFormat() {
+            token.ensureValid();
+            if (sensitive || (extractable == false)) {
+                return null;
+            } else {
+                return "RAW";
+            }
+        }
+        byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (getFormat() == null) {
+                return null;
+            }
+            byte[] b = encoded;
+            if (b == null) {
+                synchronized (this) {
+                    b = encoded;
+                    if (b == null) {
+                        Session tempSession = null;
+                        try {
+                            tempSession = token.getOpSession();
+                            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                                new CK_ATTRIBUTE(CKA_VALUE),
+                            };
+                            token.p11.C_GetAttributeValue
+                                (tempSession.id(), keyID, attributes);
+                            b = attributes[0].getByteArray();
+                        } catch (PKCS11Exception e) {
+                            throw new ProviderException(e);
+                        } finally {
+                            token.releaseSession(tempSession);
+                        }
+                        encoded = b;
+                    }
+                }
+            }
+            return b;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static class P11TlsMasterSecretKey extends P11SecretKey
+            implements TlsMasterSecret {
+        private static final long serialVersionUID = -1318560923770573441L;
+
+        private final int majorVersion, minorVersion;
+        P11TlsMasterSecretKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) {
+            super(session, keyID, algorithm, keyLength, attributes);
+            this.majorVersion = major;
+            this.minorVersion = minor;
+        }
+        public int getMajorVersion() {
+            return majorVersion;
+        }
+
+        public int getMinorVersion() {
+            return minorVersion;
+        }
+    }
+
+    // RSA CRT private key
+    private static final class P11RSAPrivateKey extends P11Key
+                implements RSAPrivateCrtKey {
+        private static final long serialVersionUID = 9215872438913515220L;
+
+        private BigInteger n, e, d, p, q, pe, qe, coeff;
+        private byte[] encoded;
+        P11RSAPrivateKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE[] crtAttrs) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attrs);
+
+            for (CK_ATTRIBUTE a : crtAttrs) {
+                if (a.type == CKA_PUBLIC_EXPONENT) {
+                    e = a.getBigInteger();
+                } else if (a.type == CKA_PRIME_1) {
+                    p = a.getBigInteger();
+                } else if (a.type == CKA_PRIME_2) {
+                    q = a.getBigInteger();
+                } else if (a.type == CKA_EXPONENT_1) {
+                    pe = a.getBigInteger();
+                } else if (a.type == CKA_EXPONENT_2) {
+                    qe = a.getBigInteger();
+                } else if (a.type == CKA_COEFFICIENT) {
+                    coeff = a.getBigInteger();
+                }
+            }
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (n != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_MODULUS),
+                new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
+            };
+            fetchAttributes(attributes);
+            n = attributes[0].getBigInteger();
+            d = attributes[1].getBigInteger();
+        }
+
+        public String getFormat() {
+            token.ensureValid();
+            return "PKCS#8";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    // XXX make constructor in SunRsaSign provider public
+                    // and call it directly
+                    KeyFactory factory = KeyFactory.getInstance
+                        ("RSA", P11Util.getSunRsaSignProvider());
+                    Key newKey = factory.translateKey(this);
+                    encoded = newKey.getEncoded();
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getModulus() {
+            fetchValues();
+            return n;
+        }
+        public BigInteger getPublicExponent() {
+            return e;
+        }
+        public BigInteger getPrivateExponent() {
+            fetchValues();
+            return d;
+        }
+        public BigInteger getPrimeP() {
+            return p;
+        }
+        public BigInteger getPrimeQ() {
+            return q;
+        }
+        public BigInteger getPrimeExponentP() {
+            return pe;
+        }
+        public BigInteger getPrimeExponentQ() {
+            return qe;
+        }
+        public BigInteger getCrtCoefficient() {
+            return coeff;
+        }
+    }
+
+    // RSA non-CRT private key
+    private static final class P11RSAPrivateNonCRTKey extends P11Key
+                implements RSAPrivateKey {
+        private static final long serialVersionUID = 1137764983777411481L;
+
+        private BigInteger n, d;
+        private byte[] encoded;
+        P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (n != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_MODULUS),
+                new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT),
+            };
+            fetchAttributes(attributes);
+            n = attributes[0].getBigInteger();
+            d = attributes[1].getBigInteger();
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "PKCS#8";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    // XXX make constructor in SunRsaSign provider public
+                    // and call it directly
+                    KeyFactory factory = KeyFactory.getInstance
+                        ("RSA", P11Util.getSunRsaSignProvider());
+                    Key newKey = factory.translateKey(this);
+                    encoded = newKey.getEncoded();
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getModulus() {
+            fetchValues();
+            return n;
+        }
+        public BigInteger getPrivateExponent() {
+            fetchValues();
+            return d;
+        }
+    }
+
+    private static final class P11RSAPublicKey extends P11Key
+                                                implements RSAPublicKey {
+        private static final long serialVersionUID = -826726289023854455L;
+
+        private BigInteger n, e;
+        private byte[] encoded;
+        P11RSAPublicKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (n != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_MODULUS),
+                new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT),
+            };
+            fetchAttributes(attributes);
+            n = attributes[0].getBigInteger();
+            e = attributes[1].getBigInteger();
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "X.509";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    encoded = new RSAPublicKeyImpl(n, e).getEncoded();
+                } catch (InvalidKeyException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getModulus() {
+            fetchValues();
+            return n;
+        }
+        public BigInteger getPublicExponent() {
+            fetchValues();
+            return e;
+        }
+        public String toString() {
+            fetchValues();
+            return super.toString() +  "\n  modulus: " + n
+                + "\n  public exponent: " + e;
+        }
+    }
+
+    private static final class P11DSAPublicKey extends P11Key
+                                                implements DSAPublicKey {
+        private static final long serialVersionUID = 5989753793316396637L;
+
+        private BigInteger y;
+        private DSAParams params;
+        private byte[] encoded;
+        P11DSAPublicKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (y != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_SUBPRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            fetchAttributes(attributes);
+            y = attributes[0].getBigInteger();
+            params = new DSAParameterSpec(
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger(),
+                attributes[3].getBigInteger()
+            );
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "X.509";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    Key key = new sun.security.provider.DSAPublicKey
+                            (y, params.getP(), params.getQ(), params.getG());
+                    encoded = key.getEncoded();
+                } catch (InvalidKeyException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getY() {
+            fetchValues();
+            return y;
+        }
+        public DSAParams getParams() {
+            fetchValues();
+            return params;
+        }
+        public String toString() {
+            fetchValues();
+            return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
+                + "\n  q: " + params.getQ() + "\n  g: " + params.getG();
+        }
+    }
+
+    private static final class P11DSAPrivateKey extends P11Key
+                                                implements DSAPrivateKey {
+        private static final long serialVersionUID = 3119629997181999389L;
+
+        private BigInteger x;
+        private DSAParams params;
+        private byte[] encoded;
+        P11DSAPrivateKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (x != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_SUBPRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            fetchAttributes(attributes);
+            x = attributes[0].getBigInteger();
+            params = new DSAParameterSpec(
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger(),
+                attributes[3].getBigInteger()
+            );
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "PKCS#8";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    Key key = new sun.security.provider.DSAPrivateKey
+                            (x, params.getP(), params.getQ(), params.getG());
+                    encoded = key.getEncoded();
+                } catch (InvalidKeyException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getX() {
+            fetchValues();
+            return x;
+        }
+        public DSAParams getParams() {
+            fetchValues();
+            return params;
+        }
+    }
+
+    private static final class P11DHPrivateKey extends P11Key
+                                                implements DHPrivateKey {
+        private static final long serialVersionUID = -1698576167364928838L;
+
+        private BigInteger x;
+        private DHParameterSpec params;
+        private byte[] encoded;
+        P11DHPrivateKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (x != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            fetchAttributes(attributes);
+            x = attributes[0].getBigInteger();
+            params = new DHParameterSpec(
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger()
+            );
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "PKCS#8";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    DHPrivateKeySpec spec = new DHPrivateKeySpec
+                        (x, params.getP(), params.getG());
+                    KeyFactory kf = KeyFactory.getInstance
+                        ("DH", P11Util.getSunJceProvider());
+                    Key key = kf.generatePrivate(spec);
+                    encoded = key.getEncoded();
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getX() {
+            fetchValues();
+            return x;
+        }
+        public DHParameterSpec getParams() {
+            fetchValues();
+            return params;
+        }
+        public int hashCode() {
+            if (token.isValid() == false) {
+                return 0;
+            }
+            fetchValues();
+            return Objects.hash(x, params.getP(), params.getG());
+        }
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            // equals() should never throw exceptions
+            if (token.isValid() == false) {
+                return false;
+            }
+            if (!(obj instanceof DHPrivateKey)) {
+                return false;
+            }
+            fetchValues();
+            DHPrivateKey other = (DHPrivateKey) obj;
+            DHParameterSpec otherParams = other.getParams();
+            return ((this.x.compareTo(other.getX()) == 0) &&
+                    (this.params.getP().compareTo(otherParams.getP()) == 0) &&
+                    (this.params.getG().compareTo(otherParams.getG()) == 0));
+        }
+    }
+
+    private static final class P11DHPublicKey extends P11Key
+                                                implements DHPublicKey {
+        static final long serialVersionUID = -598383872153843657L;
+
+        private BigInteger y;
+        private DHParameterSpec params;
+        private byte[] encoded;
+        P11DHPublicKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (y != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_PRIME),
+                new CK_ATTRIBUTE(CKA_BASE),
+            };
+            fetchAttributes(attributes);
+            y = attributes[0].getBigInteger();
+            params = new DHParameterSpec(
+                attributes[1].getBigInteger(),
+                attributes[2].getBigInteger()
+            );
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "X.509";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    DHPublicKeySpec spec = new DHPublicKeySpec
+                        (y, params.getP(), params.getG());
+                    KeyFactory kf = KeyFactory.getInstance
+                        ("DH", P11Util.getSunJceProvider());
+                    Key key = kf.generatePublic(spec);
+                    encoded = key.getEncoded();
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getY() {
+            fetchValues();
+            return y;
+        }
+        public DHParameterSpec getParams() {
+            fetchValues();
+            return params;
+        }
+        public String toString() {
+            fetchValues();
+            return super.toString() +  "\n  y: " + y + "\n  p: " + params.getP()
+                + "\n  g: " + params.getG();
+        }
+        public int hashCode() {
+            if (token.isValid() == false) {
+                return 0;
+            }
+            fetchValues();
+            return Objects.hash(y, params.getP(), params.getG());
+        }
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            // equals() should never throw exceptions
+            if (token.isValid() == false) {
+                return false;
+            }
+            if (!(obj instanceof DHPublicKey)) {
+                return false;
+            }
+            fetchValues();
+            DHPublicKey other = (DHPublicKey) obj;
+            DHParameterSpec otherParams = other.getParams();
+            return ((this.y.compareTo(other.getY()) == 0) &&
+                    (this.params.getP().compareTo(otherParams.getP()) == 0) &&
+                    (this.params.getG().compareTo(otherParams.getG()) == 0));
+        }
+    }
+
+    private static final class P11ECPrivateKey extends P11Key
+                                                implements ECPrivateKey {
+        private static final long serialVersionUID = -7786054399510515515L;
+
+        private BigInteger s;
+        private ECParameterSpec params;
+        private byte[] encoded;
+        P11ECPrivateKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PRIVATE, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (s != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE),
+                new CK_ATTRIBUTE(CKA_EC_PARAMS, params),
+            };
+            fetchAttributes(attributes);
+            s = attributes[0].getBigInteger();
+            try {
+                params = P11ECKeyFactory.decodeParameters
+                            (attributes[1].getByteArray());
+            } catch (Exception e) {
+                throw new RuntimeException("Could not parse key values", e);
+            }
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "PKCS#8";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    Key key = ECUtil.generateECPrivateKey(s, params);
+                    encoded = key.getEncoded();
+                } catch (InvalidKeySpecException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public BigInteger getS() {
+            fetchValues();
+            return s;
+        }
+        public ECParameterSpec getParams() {
+            fetchValues();
+            return params;
+        }
+    }
+
+    private static final class P11ECPublicKey extends P11Key
+                                                implements ECPublicKey {
+        private static final long serialVersionUID = -6371481375154806089L;
+
+        private ECPoint w;
+        private ECParameterSpec params;
+        private byte[] encoded;
+        P11ECPublicKey(Session session, long keyID, String algorithm,
+                int keyLength, CK_ATTRIBUTE[] attributes) {
+            super(PUBLIC, session, keyID, algorithm, keyLength, attributes);
+        }
+        private synchronized void fetchValues() {
+            token.ensureValid();
+            if (w != null) {
+                return;
+            }
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_EC_POINT),
+                new CK_ATTRIBUTE(CKA_EC_PARAMS),
+            };
+            fetchAttributes(attributes);
+
+            try {
+                params = P11ECKeyFactory.decodeParameters
+                            (attributes[1].getByteArray());
+                byte[] ecKey = attributes[0].getByteArray();
+
+                // Check whether the X9.63 encoding of an EC point is wrapped
+                // in an ASN.1 OCTET STRING
+                if (!token.config.getUseEcX963Encoding()) {
+                    DerValue wECPoint = new DerValue(ecKey);
+
+                    if (wECPoint.getTag() != DerValue.tag_OctetString) {
+                        throw new IOException("Could not DER decode EC point." +
+                            " Unexpected tag: " + wECPoint.getTag());
+                    }
+                    w = P11ECKeyFactory.decodePoint
+                        (wECPoint.getDataBytes(), params.getCurve());
+
+                } else {
+                    w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve());
+                }
+
+            } catch (Exception e) {
+                throw new RuntimeException("Could not parse key values", e);
+            }
+        }
+        public String getFormat() {
+            token.ensureValid();
+            return "X.509";
+        }
+        synchronized byte[] getEncodedInternal() {
+            token.ensureValid();
+            if (encoded == null) {
+                fetchValues();
+                try {
+                    return ECUtil.x509EncodeECPublicKey(w, params);
+                } catch (InvalidKeySpecException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            return encoded;
+        }
+        public ECPoint getW() {
+            fetchValues();
+            return w;
+        }
+        public ECParameterSpec getParams() {
+            fetchValues();
+            return params;
+        }
+        public String toString() {
+            fetchValues();
+            return super.toString()
+                + "\n  public x coord: " + w.getAffineX()
+                + "\n  public y coord: " + w.getAffineY()
+                + "\n  parameters: " + params;
+        }
+    }
+}
+
+/*
+ * NOTE: Must use PhantomReference here and not WeakReference
+ * otherwise the key maybe cleared before other objects which
+ * still use these keys during finalization such as SSLSocket.
+ */
+final class SessionKeyRef extends PhantomReference<P11Key>
+    implements Comparable<SessionKeyRef> {
+    private static ReferenceQueue<P11Key> refQueue =
+        new ReferenceQueue<P11Key>();
+    private static Set<SessionKeyRef> refList =
+        Collections.synchronizedSortedSet(new TreeSet<SessionKeyRef>());
+
+    static ReferenceQueue<P11Key> referenceQueue() {
+        return refQueue;
+    }
+
+    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;
+
+    SessionKeyRef(P11Key key , long keyID, Session session) {
+        super(key, refQueue);
+        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() {
+        refList.remove(this);
+        this.clear();
+        session.removeObject();
+    }
+
+    public int compareTo(SessionKeyRef other) {
+        if (this.keyID == other.keyID) {
+            return 0;
+        } else {
+            return (this.keyID < other.keyID) ? -1 : 1;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyAgreement.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2003, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.math.BigInteger;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.*;
+import javax.crypto.interfaces.*;
+import javax.crypto.spec.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+import sun.security.util.KeyUtil;
+
+/**
+ * KeyAgreement implementation class. This class currently supports
+ * DH.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11KeyAgreement extends KeyAgreementSpi {
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // mechanism id
+    private final long mechanism;
+
+    // private key, if initialized
+    private P11Key privateKey;
+
+    // other sides public value ("y"), if doPhase() already called
+    private BigInteger publicValue;
+
+    // length of the secret to be derived
+    private int secretLen;
+
+    // KeyAgreement from SunJCE as fallback for > 2 party agreement
+    private KeyAgreement multiPartyAgreement;
+
+    P11KeyAgreement(Token token, String algorithm, long mechanism) {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = mechanism;
+    }
+
+    // see JCE spec
+    protected void engineInit(Key key, SecureRandom random)
+            throws InvalidKeyException {
+        if (key instanceof PrivateKey == false) {
+            throw new InvalidKeyException
+                        ("Key must be instance of PrivateKey");
+        }
+        privateKey = P11KeyFactory.convertKey(token, key, algorithm);
+        publicValue = null;
+        multiPartyAgreement = null;
+    }
+
+    // see JCE spec
+    protected void engineInit(Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException
+                        ("Parameters not supported");
+        }
+        engineInit(key, random);
+    }
+
+    // see JCE spec
+    protected Key engineDoPhase(Key key, boolean lastPhase)
+            throws InvalidKeyException, IllegalStateException {
+        if (privateKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        if (publicValue != null) {
+            throw new IllegalStateException("Phase already executed");
+        }
+        // PKCS#11 only allows key agreement between 2 parties
+        // JCE allows >= 2 parties. To support that case (for compatibility
+        // and to pass JCK), fall back to SunJCE in this case.
+        // NOTE that we initialize using the P11Key, which will fail if it
+        // is sensitive/unextractable. However, this is not an issue in the
+        // compatibility configuration, which is all we are targeting here.
+        if ((multiPartyAgreement != null) || (lastPhase == false)) {
+            if (multiPartyAgreement == null) {
+                try {
+                    multiPartyAgreement = KeyAgreement.getInstance
+                        ("DH", P11Util.getSunJceProvider());
+                    multiPartyAgreement.init(privateKey);
+                } catch (NoSuchAlgorithmException e) {
+                    throw new InvalidKeyException
+                        ("Could not initialize multi party agreement", e);
+                }
+            }
+            return multiPartyAgreement.doPhase(key, lastPhase);
+        }
+        if ((key instanceof PublicKey == false)
+                || (key.getAlgorithm().equals(algorithm) == false)) {
+            throw new InvalidKeyException
+                ("Key must be a PublicKey with algorithm DH");
+        }
+        BigInteger p, g, y;
+        if (key instanceof DHPublicKey) {
+            DHPublicKey dhKey = (DHPublicKey)key;
+
+            // validate the Diffie-Hellman public key
+            KeyUtil.validate(dhKey);
+
+            y = dhKey.getY();
+            DHParameterSpec params = dhKey.getParams();
+            p = params.getP();
+            g = params.getG();
+        } else {
+            // normally, DH PublicKeys will always implement DHPublicKey
+            // just in case not, attempt conversion
+            P11DHKeyFactory kf = new P11DHKeyFactory(token, "DH");
+            try {
+                DHPublicKeySpec spec = kf.engineGetKeySpec(
+                        key, DHPublicKeySpec.class);
+
+                // validate the Diffie-Hellman public key
+                KeyUtil.validate(spec);
+
+                y = spec.getY();
+                p = spec.getP();
+                g = spec.getG();
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException("Could not obtain key values", e);
+            }
+        }
+        // if parameters of private key are accessible, verify that
+        // they match parameters of public key
+        // XXX p and g should always be readable, even if the key is sensitive
+        if (privateKey instanceof DHPrivateKey) {
+            DHPrivateKey dhKey = (DHPrivateKey)privateKey;
+            DHParameterSpec params = dhKey.getParams();
+            if ((p.equals(params.getP()) == false)
+                                || (g.equals(params.getG()) == false)) {
+                throw new InvalidKeyException
+                ("PublicKey DH parameters must match PrivateKey DH parameters");
+            }
+        }
+        publicValue = y;
+        // length of the secret is length of key
+        secretLen = (p.bitLength() + 7) >> 3;
+        return null;
+    }
+
+    // see JCE spec
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        if (multiPartyAgreement != null) {
+            byte[] val = multiPartyAgreement.generateSecret();
+            multiPartyAgreement = null;
+            return val;
+        }
+        if ((privateKey == null) || (publicValue == null)) {
+            throw new IllegalStateException("Not initialized correctly");
+        }
+        Session session = null;
+        try {
+            session = token.getOpSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
+            };
+            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);
+            attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE)
+            };
+            token.p11.C_GetAttributeValue(session.id(), keyID, attributes);
+            byte[] secret = attributes[0].getByteArray();
+            token.p11.C_DestroyObject(session.id(), keyID);
+            // Some vendors, e.g. NSS, trim off the leading 0x00 byte(s) from
+            // the generated secret. Thus, we need to check the secret length
+            // and trim/pad it so the returned value has the same length as
+            // the modulus size
+            if (secret.length == secretLen) {
+                return secret;
+            } else {
+                if (secret.length > secretLen) {
+                    // Shouldn't happen; but check just in case
+                    throw new ProviderException("generated secret is out-of-range");
+                }
+                byte[] newSecret = new byte[secretLen];
+                System.arraycopy(secret, 0, newSecret, secretLen - secret.length,
+                    secret.length);
+                return newSecret;
+            }
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("Could not derive key", e);
+        } finally {
+            publicValue = null;
+            token.releaseSession(session);
+        }
+    }
+
+    // see JCE spec
+    protected int engineGenerateSecret(byte[] sharedSecret, int
+            offset) throws IllegalStateException, ShortBufferException {
+        if (multiPartyAgreement != null) {
+            int n = multiPartyAgreement.generateSecret(sharedSecret, offset);
+            multiPartyAgreement = null;
+            return n;
+        }
+        if (offset + secretLen > sharedSecret.length) {
+            throw new ShortBufferException("Need " + secretLen
+                + " bytes, only " + (sharedSecret.length - offset) + " available");
+        }
+        byte[] secret = engineGenerateSecret();
+        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
+        return secret.length;
+    }
+
+    // see JCE spec
+    protected SecretKey engineGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException,
+            InvalidKeyException {
+        if (multiPartyAgreement != null) {
+            SecretKey key = multiPartyAgreement.generateSecret(algorithm);
+            multiPartyAgreement = null;
+            return key;
+        }
+        if (algorithm == null) {
+            throw new NoSuchAlgorithmException("Algorithm must not be null");
+        }
+        if (algorithm.equals("TlsPremasterSecret")) {
+            // For now, only perform native derivation for TlsPremasterSecret
+            // as that is required for FIPS compliance.
+            // For other algorithms, there are unresolved issues regarding
+            // how this should work in JCE plus a Solaris truncation bug.
+            // (bug not yet filed).
+            return nativeGenerateSecret(algorithm);
+        }
+        byte[] secret = engineGenerateSecret();
+        // Maintain compatibility for SunJCE:
+        // verify secret length is sensible for algorithm / truncate
+        // return generated key itself if possible
+        int keyLen;
+        if (algorithm.equalsIgnoreCase("DES")) {
+            keyLen = 8;
+        } else if (algorithm.equalsIgnoreCase("DESede")) {
+            keyLen = 24;
+        } else if (algorithm.equalsIgnoreCase("Blowfish")) {
+            keyLen = Math.min(56, secret.length);
+        } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) {
+            keyLen = secret.length;
+        } else {
+            throw new NoSuchAlgorithmException
+                ("Unknown algorithm " + algorithm);
+        }
+        if (secret.length < keyLen) {
+            throw new InvalidKeyException("Secret too short");
+        }
+        if (algorithm.equalsIgnoreCase("DES") ||
+            algorithm.equalsIgnoreCase("DESede")) {
+                for (int i = 0; i < keyLen; i+=8) {
+                    P11SecretKeyFactory.fixDESParity(secret, i);
+                }
+        }
+        return new SecretKeySpec(secret, 0, keyLen, algorithm);
+    }
+
+    private SecretKey nativeGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException,
+            InvalidKeyException {
+        if ((privateKey == null) || (publicValue == null)) {
+            throw new IllegalStateException("Not initialized correctly");
+        }
+        long keyType = CKK_GENERIC_SECRET;
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
+            };
+            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);
+            CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE_LEN),
+            };
+            token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes);
+            int keyLen = (int)lenAttributes[0].getLong();
+            SecretKey key = P11Key.secretKey
+                        (session, keyID, algorithm, keyLen << 3, attributes);
+            if ("RAW".equals(key.getFormat())) {
+                // Workaround for Solaris bug 6318543.
+                // Strip leading zeroes ourselves if possible (key not sensitive).
+                // This should be removed once the Solaris fix is available
+                // as here we always retrieve the CKA_VALUE even for tokens
+                // that do not have that bug.
+                byte[] keyBytes = key.getEncoded();
+                byte[] newBytes = KeyUtil.trimZeroes(keyBytes);
+                if (keyBytes != newBytes) {
+                    key = new SecretKeySpec(newBytes, algorithm);
+                }
+            }
+            return key;
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeyException("Could not derive key", e);
+        } finally {
+            publicValue = null;
+            token.releaseSession(session);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyFactory.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2003, 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.security.*;
+import java.security.spec.*;
+
+import sun.security.pkcs11.wrapper.PKCS11Exception;
+
+/**
+ * KeyFactory base class. Provides common infrastructure for the RSA, DSA,
+ * and DH implementations.
+ *
+ * The subclasses support conversion between keys and keyspecs
+ * using X.509, PKCS#8, and their individual algorithm specific formats,
+ * assuming keys are extractable.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+abstract class P11KeyFactory extends KeyFactorySpi {
+
+    // token instance
+    final Token token;
+
+    // algorithm name, currently one of RSA, DSA, DH
+    final String algorithm;
+
+    P11KeyFactory(Token token, String algorithm) {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+    }
+
+    /**
+     * Convert an arbitrary key of algorithm into a P11Key of token.
+     * Used by P11Signature.init() and RSACipher.init().
+     */
+    static P11Key convertKey(Token token, Key key, String algorithm)
+            throws InvalidKeyException {
+        return (P11Key)token.getKeyFactory(algorithm).engineTranslateKey(key);
+    }
+
+    // see JCA spec
+    protected final <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+        token.ensureValid();
+        if ((key == null) || (keySpec == null)) {
+            throw new InvalidKeySpecException
+                ("key and keySpec must not be null");
+        }
+        // delegate to our Java based providers for PKCS#8 and X.509
+        if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)
+                || X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            try {
+                return implGetSoftwareFactory().getKeySpec(key, keySpec);
+            } catch (GeneralSecurityException e) {
+                throw new InvalidKeySpecException("Could not encode key", e);
+            }
+        }
+        // first translate into a key of this token, if it is not already
+        P11Key p11Key;
+        try {
+            p11Key = (P11Key)engineTranslateKey(key);
+        } catch (InvalidKeyException e) {
+            throw new InvalidKeySpecException("Could not convert key", e);
+        }
+        Session[] session = new Session[1];
+        try {
+            if (p11Key.isPublic()) {
+                return implGetPublicKeySpec(p11Key, keySpec, session);
+            } else {
+                return implGetPrivateKeySpec(p11Key, keySpec, session);
+            }
+        } catch (PKCS11Exception e) {
+            throw new InvalidKeySpecException("Could not generate KeySpec", e);
+        } finally {
+            session[0] = token.releaseSession(session[0]);
+        }
+    }
+
+    // see JCA spec
+    protected final Key engineTranslateKey(Key key) throws InvalidKeyException {
+        token.ensureValid();
+        if (key == null) {
+            throw new InvalidKeyException("Key must not be null");
+        }
+        if (key.getAlgorithm().equals(this.algorithm) == false) {
+            throw new InvalidKeyException
+                ("Key algorithm must be " + algorithm);
+        }
+        if (key instanceof P11Key) {
+            P11Key p11Key = (P11Key)key;
+            if (p11Key.token == token) {
+                // already a key of this token, no need to translate
+                return key;
+            }
+        }
+        P11Key p11Key = token.privateCache.get(key);
+        if (p11Key != null) {
+            return p11Key;
+        }
+        if (key instanceof PublicKey) {
+            PublicKey publicKey = implTranslatePublicKey((PublicKey)key);
+            token.privateCache.put(key, (P11Key)publicKey);
+            return publicKey;
+        } else if (key instanceof PrivateKey) {
+            PrivateKey privateKey = implTranslatePrivateKey((PrivateKey)key);
+            token.privateCache.put(key, (P11Key)privateKey);
+            return privateKey;
+        } else {
+            throw new InvalidKeyException
+                ("Key must be instance of PublicKey or PrivateKey");
+        }
+    }
+
+    abstract <T extends KeySpec> T  implGetPublicKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException;
+
+    abstract <T extends KeySpec> T  implGetPrivateKeySpec(P11Key key, Class<T> keySpec,
+            Session[] session) throws PKCS11Exception, InvalidKeySpecException;
+
+    abstract PublicKey implTranslatePublicKey(PublicKey key)
+            throws InvalidKeyException;
+
+    abstract PrivateKey implTranslatePrivateKey(PrivateKey key)
+            throws InvalidKeyException;
+
+    abstract KeyFactory implGetSoftwareFactory() throws GeneralSecurityException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2003, 2008, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.security.*;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.*;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * KeyGenerator implementation class. This class currently supports
+ * DES, DESede, AES, ARCFOUR, and Blowfish.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11KeyGenerator extends KeyGeneratorSpi {
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // mechanism id
+    private long mechanism;
+
+    // raw key size in bits, e.g. 64 for DES. Always valid.
+    private int keySize;
+
+    // bits of entropy in the key, e.g. 56 for DES. Always valid.
+    private int significantKeySize;
+
+    // keyType (CKK_*), needed for TemplateManager call only.
+    private long keyType;
+
+    // for determining if both 112 and 168 bits of DESede key lengths
+    // are supported.
+    private boolean supportBothKeySizes;
+
+    /**
+     * Utility method for checking if the specified key size is valid
+     * and within the supported range. Return the significant key size
+     * upon successful validation.
+     * @param keyGenMech the PKCS#11 key generation mechanism.
+     * @param keySize the to-be-checked key size for this mechanism.
+     * @param token token which provides this mechanism.
+     * @return the significant key size (in bits) corresponding to the
+     * specified key size.
+     * @throws InvalidParameterException if the specified key size is invalid.
+     * @throws ProviderException if this mechanism isn't supported by SunPKCS11
+     * or underlying native impl.
+     */
+    static int checkKeySize(long keyGenMech, int keySize, Token token)
+        throws InvalidAlgorithmParameterException, ProviderException {
+        int sigKeySize;
+        switch ((int)keyGenMech) {
+            case (int)CKM_DES_KEY_GEN:
+                if ((keySize != 64) && (keySize != 56)) {
+                    throw new InvalidAlgorithmParameterException
+                            ("DES key length must be 56 bits");
+                }
+                sigKeySize = 56;
+                break;
+            case (int)CKM_DES2_KEY_GEN:
+            case (int)CKM_DES3_KEY_GEN:
+                if ((keySize == 112) || (keySize == 128)) {
+                    sigKeySize = 112;
+                } else if ((keySize == 168) || (keySize == 192)) {
+                    sigKeySize = 168;
+                } else {
+                    throw new InvalidAlgorithmParameterException
+                            ("DESede key length must be 112, or 168 bits");
+                }
+                break;
+            default:
+                // Handle all variable-key-length algorithms here
+                CK_MECHANISM_INFO info = null;
+                try {
+                    info = token.getMechanismInfo(keyGenMech);
+                } catch (PKCS11Exception p11e) {
+                    // Should never happen
+                    throw new ProviderException
+                            ("Cannot retrieve mechanism info", p11e);
+                }
+                if (info == null) {
+                    // XXX Unable to retrieve the supported key length from
+                    // the underlying native impl. Skip the checking for now.
+                    return keySize;
+                }
+                // PKCS#11 defines these to be in number of bytes except for
+                // RC4 which is in bits. However, some PKCS#11 impls still use
+                // bytes for all mechs, e.g. NSS. We try to detect this
+                // inconsistency if the minKeySize seems unreasonably small.
+                int minKeySize = (int)info.ulMinKeySize;
+                int maxKeySize = (int)info.ulMaxKeySize;
+                if (keyGenMech != CKM_RC4_KEY_GEN || minKeySize < 8) {
+                    minKeySize = (int)info.ulMinKeySize << 3;
+                    maxKeySize = (int)info.ulMaxKeySize << 3;
+                }
+                // Explicitly disallow keys shorter than 40-bits for security
+                if (minKeySize < 40) minKeySize = 40;
+                if (keySize < minKeySize || keySize > maxKeySize) {
+                    throw new InvalidAlgorithmParameterException
+                            ("Key length must be between " + minKeySize +
+                            " and " + maxKeySize + " bits");
+                }
+                if (keyGenMech == CKM_AES_KEY_GEN) {
+                    if ((keySize != 128) && (keySize != 192) &&
+                        (keySize != 256)) {
+                        throw new InvalidAlgorithmParameterException
+                                ("AES key length must be " + minKeySize +
+                                (maxKeySize >= 192? ", 192":"") +
+                                (maxKeySize >= 256? ", or 256":"") + " bits");
+                    }
+                }
+                sigKeySize = keySize;
+        }
+        return sigKeySize;
+    }
+
+    P11KeyGenerator(Token token, String algorithm, long mechanism)
+            throws PKCS11Exception {
+        super();
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = mechanism;
+
+        if (this.mechanism == CKM_DES3_KEY_GEN) {
+            /* Given the current lookup order specified in SunPKCS11.java,
+               if CKM_DES2_KEY_GEN is used to construct this object, it
+               means that CKM_DES3_KEY_GEN is disabled or unsupported.
+            */
+            supportBothKeySizes =
+                (token.provider.config.isEnabled(CKM_DES2_KEY_GEN) &&
+                 (token.getMechanismInfo(CKM_DES2_KEY_GEN) != null));
+        }
+        setDefaultKeySize();
+    }
+
+    // set default keysize and also initialize keyType
+    private void setDefaultKeySize() {
+        switch ((int)mechanism) {
+        case (int)CKM_DES_KEY_GEN:
+            keySize = 64;
+            keyType = CKK_DES;
+            break;
+        case (int)CKM_DES2_KEY_GEN:
+            keySize = 128;
+            keyType = CKK_DES2;
+            break;
+        case (int)CKM_DES3_KEY_GEN:
+            keySize = 192;
+            keyType = CKK_DES3;
+            break;
+        case (int)CKM_AES_KEY_GEN:
+            keySize = 128;
+            keyType = CKK_AES;
+            break;
+        case (int)CKM_RC4_KEY_GEN:
+            keySize = 128;
+            keyType = CKK_RC4;
+            break;
+        case (int)CKM_BLOWFISH_KEY_GEN:
+            keySize = 128;
+            keyType = CKK_BLOWFISH;
+            break;
+        default:
+            throw new ProviderException("Unknown mechanism " + mechanism);
+        }
+        try {
+            significantKeySize = checkKeySize(mechanism, keySize, token);
+        } catch (InvalidAlgorithmParameterException iape) {
+            throw new ProviderException("Unsupported default key size", iape);
+        }
+    }
+
+    // see JCE spec
+    protected void engineInit(SecureRandom random) {
+        token.ensureValid();
+        setDefaultKeySize();
+    }
+
+    // see JCE spec
+    protected void engineInit(AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidAlgorithmParameterException {
+        throw new InvalidAlgorithmParameterException
+                ("AlgorithmParameterSpec not supported");
+    }
+
+    // see JCE spec
+    protected void engineInit(int keySize, SecureRandom random) {
+        token.ensureValid();
+        int newSignificantKeySize;
+        try {
+            newSignificantKeySize = checkKeySize(mechanism, keySize, token);
+        } catch (InvalidAlgorithmParameterException iape) {
+            throw (InvalidParameterException)
+                    (new InvalidParameterException().initCause(iape));
+        }
+        if ((mechanism == CKM_DES2_KEY_GEN) ||
+            (mechanism == CKM_DES3_KEY_GEN))  {
+            long newMechanism = (newSignificantKeySize == 112 ?
+                CKM_DES2_KEY_GEN : CKM_DES3_KEY_GEN);
+            if (mechanism != newMechanism) {
+                if (supportBothKeySizes) {
+                    mechanism = newMechanism;
+                    // Adjust keyType to reflect the mechanism change
+                    keyType = (mechanism == CKM_DES2_KEY_GEN ?
+                        CKK_DES2 : CKK_DES3);
+                } else {
+                    throw new InvalidParameterException
+                            ("Only " + significantKeySize +
+                             "-bit DESede is supported");
+                }
+            }
+        }
+        this.keySize = keySize;
+        this.significantKeySize = newSignificantKeySize;
+    }
+
+    // see JCE spec
+    protected SecretKey engineGenerateKey() {
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            CK_ATTRIBUTE[] attributes;
+            switch ((int)keyType) {
+            case (int)CKK_DES:
+            case (int)CKK_DES2:
+            case (int)CKK_DES3:
+                // fixed length, do not specify CKA_VALUE_LEN
+                attributes = new CK_ATTRIBUTE[] {
+                    new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                };
+                break;
+            default:
+                attributes = new CK_ATTRIBUTE[] {
+                    new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
+                    new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3),
+                };
+                break;
+            }
+            attributes = token.getAttributes
+                (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
+            long keyID = token.p11.C_GenerateKey
+                (session.id(), new CK_MECHANISM(mechanism), attributes);
+            return P11Key.secretKey
+                (session, keyID, algorithm, significantKeySize, attributes);
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("Could not generate key", e);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyPairGenerator.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2003, 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.math.BigInteger;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import sun.security.provider.ParameterCache;
+
+import static sun.security.pkcs11.TemplateManager.*;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+import sun.security.rsa.RSAKeyFactory;
+
+/**
+ * KeyPairGenerator implementation class. This class currently supports
+ * RSA, DSA, DH, and EC.
+ *
+ * Note that for DSA and DH we rely on the Sun and SunJCE providers to
+ * obtain the parameters from.
+ *
+ * @author  Andreas Sterbenz
+ * @since   1.5
+ */
+final class P11KeyPairGenerator extends KeyPairGeneratorSpi {
+
+    // token instance
+    private final Token token;
+
+    // algorithm name
+    private final String algorithm;
+
+    // mechanism id
+    private final long mechanism;
+
+    // selected or default key size, always valid
+    private int keySize;
+
+    // parameters specified via init, if any
+    private AlgorithmParameterSpec params;
+
+    // for RSA, selected or default value of public exponent, always valid
+    private BigInteger rsaPublicExponent = RSAKeyGenParameterSpec.F4;
+
+    // the supported keysize range of the native PKCS11 library
+    // if the value cannot be retrieved or unspecified, -1 is used.
+    private final int minKeySize;
+    private final int maxKeySize;
+
+    // SecureRandom instance, if specified in init
+    private SecureRandom random;
+
+    P11KeyPairGenerator(Token token, String algorithm, long mechanism)
+            throws PKCS11Exception {
+        super();
+        int minKeyLen = -1;
+        int maxKeyLen = -1;
+        try {
+            CK_MECHANISM_INFO mechInfo = token.getMechanismInfo(mechanism);
+            if (mechInfo != null) {
+                minKeyLen = (int) mechInfo.ulMinKeySize;
+                maxKeyLen = (int) mechInfo.ulMaxKeySize;
+            }
+        } catch (PKCS11Exception p11e) {
+            // Should never happen
+            throw new ProviderException
+                        ("Unexpected error while getting mechanism info", p11e);
+        }
+        // set default key sizes and apply our own algorithm-specific limits
+        // override lower limit to disallow unsecure keys being generated
+        // override upper limit to deter DOS attack
+        if (algorithm.equals("EC")) {
+            keySize = 256;
+            if ((minKeyLen == -1) || (minKeyLen < 112)) {
+                minKeyLen = 112;
+            }
+            if ((maxKeyLen == -1) || (maxKeyLen > 2048)) {
+                maxKeyLen = 2048;
+            }
+        } else {
+            if (algorithm.equals("DSA")) {
+                // keep default keysize at 1024 since larger keysizes may be
+                // incompatible with SHA1withDSA and SHA-2 Signature algs
+                // may not be supported by native pkcs11 implementations
+                keySize = 1024;
+            } else {
+                // RSA and DH
+                keySize = 2048;
+            }
+            if ((minKeyLen == -1) || (minKeyLen < 512)) {
+                minKeyLen = 512;
+            }
+            if (algorithm.equals("RSA")) {
+                if ((maxKeyLen == -1) || (maxKeyLen > 64 * 1024)) {
+                    maxKeyLen = 64 * 1024;
+                }
+            }
+        }
+
+        // auto-adjust default keysize in case it's out-of-range
+        if ((minKeyLen != -1) && (keySize < minKeyLen)) {
+            keySize = minKeyLen;
+        }
+        if ((maxKeyLen != -1) && (keySize > maxKeyLen)) {
+            keySize = maxKeyLen;
+        }
+        this.token = token;
+        this.algorithm = algorithm;
+        this.mechanism = mechanism;
+        this.minKeySize = minKeyLen;
+        this.maxKeySize = maxKeyLen;
+        initialize(keySize, null);
+    }
+
+    // see JCA spec
+    @Override
+    public void initialize(int keySize, SecureRandom random) {
+        token.ensureValid();
+        try {
+            checkKeySize(keySize, null);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidParameterException(e.getMessage());
+        }
+        this.params = null;
+        if (algorithm.equals("EC")) {
+            params = P11ECKeyFactory.getECParameterSpec(keySize);
+            if (params == null) {
+                throw new InvalidParameterException(
+                    "No EC parameters available for key size "
+                    + keySize + " bits");
+            }
+        }
+        this.keySize = keySize;
+        this.random = random;
+    }
+
+    // see JCA spec
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        token.ensureValid();
+        int tmpKeySize;
+        if (algorithm.equals("DH")) {
+            if (params instanceof DHParameterSpec == false) {
+                throw new InvalidAlgorithmParameterException
+                        ("DHParameterSpec required for Diffie-Hellman");
+            }
+            DHParameterSpec dhParams = (DHParameterSpec) params;
+            tmpKeySize = dhParams.getP().bitLength();
+            checkKeySize(tmpKeySize, dhParams);
+            // XXX sanity check params
+        } else if (algorithm.equals("RSA")) {
+            if (params instanceof RSAKeyGenParameterSpec == false) {
+                throw new InvalidAlgorithmParameterException
+                        ("RSAKeyGenParameterSpec required for RSA");
+            }
+            RSAKeyGenParameterSpec rsaParams =
+                (RSAKeyGenParameterSpec) params;
+            tmpKeySize = rsaParams.getKeysize();
+            checkKeySize(tmpKeySize, rsaParams);
+            // override the supplied params to null
+            params = null;
+            this.rsaPublicExponent = rsaParams.getPublicExponent();
+            // XXX sanity check params
+        } else if (algorithm.equals("DSA")) {
+            if (params instanceof DSAParameterSpec == false) {
+                throw new InvalidAlgorithmParameterException
+                        ("DSAParameterSpec required for DSA");
+            }
+            DSAParameterSpec dsaParams = (DSAParameterSpec) params;
+            tmpKeySize = dsaParams.getP().bitLength();
+            checkKeySize(tmpKeySize, dsaParams);
+            // XXX sanity check params
+        } else if (algorithm.equals("EC")) {
+            ECParameterSpec ecParams;
+            if (params instanceof ECParameterSpec) {
+                ecParams = P11ECKeyFactory.getECParameterSpec(
+                    (ECParameterSpec)params);
+                if (ecParams == null) {
+                    throw new InvalidAlgorithmParameterException
+                        ("Unsupported curve: " + params);
+                }
+            } else if (params instanceof ECGenParameterSpec) {
+                String name = ((ECGenParameterSpec) params).getName();
+                ecParams = P11ECKeyFactory.getECParameterSpec(name);
+                if (ecParams == null) {
+                    throw new InvalidAlgorithmParameterException
+                        ("Unknown curve name: " + name);
+                }
+                // override the supplied params with the derived one
+                params = ecParams;
+            } else {
+                throw new InvalidAlgorithmParameterException
+                    ("ECParameterSpec or ECGenParameterSpec required for EC");
+            }
+            tmpKeySize = ecParams.getCurve().getField().getFieldSize();
+            checkKeySize(tmpKeySize, ecParams);
+        } else {
+            throw new ProviderException("Unknown algorithm: " + algorithm);
+        }
+        this.keySize = tmpKeySize;
+        this.params = params;
+        this.random = random;
+    }
+
+    private void checkKeySize(int keySize, AlgorithmParameterSpec params)
+        throws InvalidAlgorithmParameterException {
+        // check native range first
+        if ((minKeySize != -1) && (keySize < minKeySize)) {
+            throw new InvalidAlgorithmParameterException(algorithm +
+                " key must be at least " + minKeySize + " bits. " +
+                "The specific key size " + keySize + " is not supported");
+        }
+        if ((maxKeySize != -1) && (keySize > maxKeySize)) {
+            throw new InvalidAlgorithmParameterException(algorithm +
+                " key must be at most " + maxKeySize + " bits. " +
+                "The specific key size " + keySize + " is not supported");
+        }
+
+        // check our own algorithm-specific limits also
+        if (algorithm.equals("EC")) {
+            if (keySize < 112) {
+                throw new InvalidAlgorithmParameterException(
+                    "EC key size must be at least 112 bit. " +
+                    "The specific key size " + keySize + " is not supported");
+            }
+            if (keySize > 2048) {
+                // sanity check, nobody really wants keys this large
+                throw new InvalidAlgorithmParameterException(
+                    "EC key size must be at most 2048 bit. " +
+                    "The specific key size " + keySize + " is not supported");
+            }
+        } else {
+            // RSA, DH, DSA
+            if (keySize < 512) {
+                throw new InvalidAlgorithmParameterException(algorithm +
+                    " key size must be at least 512 bit. " +
+                    "The specific key size " + keySize + " is not supported");
+            }
+            if (algorithm.equals("RSA")) {
+                BigInteger tmpExponent = rsaPublicExponent;
+                if (params != null) {
+                    tmpExponent =
+                        ((RSAKeyGenParameterSpec)params).getPublicExponent();
+                }
+                try {
+                    // Reuse the checking in SunRsaSign provider.
+                    // If maxKeySize is -1, then replace it with
+                    // Integer.MAX_VALUE to indicate no limit.
+                    RSAKeyFactory.checkKeyLengths(keySize, tmpExponent,
+                        minKeySize,
+                        (maxKeySize==-1? Integer.MAX_VALUE:maxKeySize));
+                } catch (InvalidKeyException e) {
+                    throw new InvalidAlgorithmParameterException(e);
+                }
+            } else if (algorithm.equals("DH")) {
+                if (params != null) {   // initialized with specified parameters
+                    // sanity check, nobody really wants keys this large
+                    if (keySize > 64 * 1024) {
+                        throw new InvalidAlgorithmParameterException(
+                            "DH key size must be at most 65536 bit. " +
+                            "The specific key size " +
+                            keySize + " is not supported");
+                    }
+                } else {        // default parameters will be used.
+                    // Range is based on the values in
+                    // sun.security.provider.ParameterCache class.
+                    if ((keySize > 8192) || (keySize < 512) ||
+                            ((keySize & 0x3f) != 0)) {
+                        throw new InvalidAlgorithmParameterException(
+                            "DH key size must be multiple of 64, and can " +
+                            "only range from 512 to 8192 (inclusive). " +
+                            "The specific key size " +
+                            keySize + " is not supported");
+                    }
+
+                    DHParameterSpec cache =
+                            ParameterCache.getCachedDHParameterSpec(keySize);
+                    // Except 2048 and 3072, not yet support generation of
+                    // parameters bigger than 1024 bits.
+                    if ((cache == null) && (keySize > 1024)) {
+                        throw new InvalidAlgorithmParameterException(
+                                "Unsupported " + keySize +
+                                "-bit DH parameter generation");
+                    }
+                }
+            } else {
+                // this restriction is in the spec for DSA
+                if ((keySize != 3072) && (keySize != 2048) &&
+                        ((keySize > 1024) || ((keySize & 0x3f) != 0))) {
+                    throw new InvalidAlgorithmParameterException(
+                        "DSA key must be multiples of 64 if less than " +
+                        "1024 bits, or 2048, 3072 bits. " +
+                        "The specific key size " +
+                        keySize + " is not supported");
+                }
+            }
+        }
+    }
+
+    // see JCA spec
+    @Override
+    public KeyPair generateKeyPair() {
+        token.ensureValid();
+        CK_ATTRIBUTE[] publicKeyTemplate;
+        CK_ATTRIBUTE[] privateKeyTemplate;
+        long keyType;
+        if (algorithm.equals("RSA")) {
+            keyType = CKK_RSA;
+            publicKeyTemplate = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_MODULUS_BITS, keySize),
+                new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, rsaPublicExponent),
+            };
+            privateKeyTemplate = new CK_ATTRIBUTE[] {
+                // empty
+            };
+        } else if (algorithm.equals("DSA")) {
+            keyType = CKK_DSA;
+            DSAParameterSpec dsaParams;
+            if (params == null) {
+                try {
+                    dsaParams = ParameterCache.getDSAParameterSpec
+                                                    (keySize, random);
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException
+                            ("Could not generate DSA parameters", e);
+                }
+            } else {
+                dsaParams = (DSAParameterSpec)params;
+            }
+            publicKeyTemplate = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_PRIME, dsaParams.getP()),
+                new CK_ATTRIBUTE(CKA_SUBPRIME, dsaParams.getQ()),
+                new CK_ATTRIBUTE(CKA_BASE, dsaParams.getG()),
+            };
+            privateKeyTemplate = new CK_ATTRIBUTE[] {
+                // empty
+            };
+        } else if (algorithm.equals("DH")) {
+            keyType = CKK_DH;
+            DHParameterSpec dhParams;
+            int privateBits;
+            if (params == null) {
+                try {
+                    dhParams = ParameterCache.getDHParameterSpec
+                                                    (keySize, random);
+                } catch (GeneralSecurityException e) {
+                    throw new ProviderException
+                            ("Could not generate DH parameters", e);
+                }
+                privateBits = 0;
+            } else {
+                dhParams = (DHParameterSpec)params;
+                privateBits = dhParams.getL();
+            }
+            if (privateBits <= 0) {
+                // XXX find better defaults
+                privateBits = (keySize >= 1024) ? 768 : 512;
+            }
+            publicKeyTemplate = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_PRIME, dhParams.getP()),
+                new CK_ATTRIBUTE(CKA_BASE, dhParams.getG())
+            };
+            privateKeyTemplate = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_VALUE_BITS, privateBits),
+            };
+        } else if (algorithm.equals("EC")) {
+            keyType = CKK_EC;
+            byte[] encodedParams =
+                    P11ECKeyFactory.encodeParameters((ECParameterSpec)params);
+            publicKeyTemplate = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
+            };
+            privateKeyTemplate = new CK_ATTRIBUTE[] {
+                // empty
+            };
+        } else {
+            throw new ProviderException("Unknown algorithm: " + algorithm);
+        }
+        Session session = null;
+        try {
+            session = token.getObjSession();
+            publicKeyTemplate = token.getAttributes
+                (O_GENERATE, CKO_PUBLIC_KEY, keyType, publicKeyTemplate);
+            privateKeyTemplate = token.getAttributes
+                (O_GENERATE, CKO_PRIVATE_KEY, keyType, privateKeyTemplate);
+            long[] keyIDs = token.p11.C_GenerateKeyPair
+                (session.id(), new CK_MECHANISM(mechanism),
+                publicKeyTemplate, privateKeyTemplate);
+            PublicKey publicKey = P11Key.publicKey
+                (session, keyIDs[0], algorithm, keySize, publicKeyTemplate);
+            PrivateKey privateKey = P11Key.privateKey
+                (session, keyIDs[1], algorithm, keySize, privateKeyTemplate);
+            return new KeyPair(publicKey, privateKey);
+        } catch (PKCS11Exception e) {
+            throw new ProviderException(e);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java	Thu Jan 26 09:19:33 2017 -0800
@@ -0,0 +1,2683 @@
+/*
+ * Copyright (c) 2003, 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.pkcs11;
+
+import java.math.BigInteger;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Set;
+
+import java.security.*;
+import java.security.KeyStore.*;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateException;
+
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.*;
+
+import javax.security.auth.x500.X500Principal;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import sun.security.util.Debug;
+import sun.security.util.DerValue;
+import sun.security.util.ECUtil;
+
+import sun.security.pkcs11.Secmod.*;
+import static sun.security.pkcs11.P11Util.*;
+
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+import sun.security.rsa.RSAKeyFactory;
+
+final class P11KeyStore extends KeyStoreSpi {
+
+    private static final CK_ATTRIBUTE ATTR_CLASS_CERT =
+                        new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE);
+    private static final CK_ATTRIBUTE ATTR_CLASS_PKEY =
+                        new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY);
+    private static final CK_ATTRIBUTE ATTR_CLASS_SKEY =
+                        new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
+
+    private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE =
+                        new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509);
+
+    private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE =
+                        new CK_ATTRIBUTE(CKA_TOKEN, true);
+
+    // XXX for testing purposes only
+    //  - NSS doesn't support persistent secret keys
+    //    (key type gets mangled if secret key is a token key)
+    //  - if debug is turned on, then this is set to false
+    private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE;
+
+    private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE =
+                        new CK_ATTRIBUTE(CKA_TRUSTED, true);
+    private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE =
+                        new CK_ATTRIBUTE(CKA_PRIVATE, true);
+
+    private static final long NO_HANDLE = -1;
+    private static final long FINDOBJECTS_MAX = 100;
+    private static final String ALIAS_SEP = "/";
+
+    private static final boolean NSS_TEST = false;
+    private static final Debug debug =
+                        Debug.getInstance("pkcs11keystore");
+    private static boolean CKA_TRUSTED_SUPPORTED = true;
+
+    private final Token token;
+
+    // If multiple certs are found to share the same CKA_LABEL
+    // at load time (NSS-style keystore), then the keystore is read
+    // and the unique keystore aliases are mapped to the entries.
+    // However, write capabilities are disabled.
+    private boolean writeDisabled = false;
+
+    // Map of unique keystore aliases to entries in the token
+    private HashMap<String, AliasInfo> aliasMap;
+
+    // whether to use NSS Secmod info for trust attributes
+    private final boolean useSecmodTrust;
+
+    // if useSecmodTrust == true, which type of trust we are interested in
+    private Secmod.TrustType nssTrustType;
+
+    /**
+     * The underlying token may contain multiple certs belonging to the
+     * same "personality" (for example, a signing cert and encryption cert),
+     * all sharing the same CKA_LABEL.  These must be resolved
+     * into unique keystore aliases.
+     *
+     * In addition, private keys and certs may not have a CKA_LABEL.
+     * It is assumed that a private key and corresponding certificate
+     * share the same CKA_ID, and that the CKA_ID is unique across the token.
+     * The CKA_ID may not be human-readable.
+     * These pairs must be resolved into unique keystore aliases.
+     *
+     * Furthermore, secret keys are assumed to have a CKA_LABEL
+     * unique across the entire token.
+     *
+     * When the KeyStore is loaded, instances of this class are
+     * created to represent the private keys/secret keys/certs
+     * that reside on the token.
+     */
+    private static class AliasInfo {
+
+        // CKA_CLASS - entry type
+        private CK_ATTRIBUTE type = null;
+
+        // CKA_LABEL of cert and secret key
+        private String label = null;
+
+        // CKA_ID of the private key/cert pair
+        private byte[] id = null;
+
+        // CKA_TRUSTED - true if cert is trusted
+        private boolean trusted = false;
+
+        // either end-entity cert or trusted cert depending on 'type'
+        private X509Certificate cert = null;
+
+        // chain
+        private X509Certificate[] chain = null;
+
+        // true if CKA_ID for private key and cert match up
+        private boolean matched = false;
+
+        // SecretKeyEntry
+        public AliasInfo(String label) {
+            this.type = ATTR_CLASS_SKEY;
+            this.label = label;
+        }
+
+        // PrivateKeyEntry
+        public AliasInfo(String label,
+                        byte[] id,
+                        boolean trusted,
+                        X509Certificate cert) {
+            this.type = ATTR_CLASS_PKEY;
+            this.label = label;
+            this.id = id;
+            this.trusted = trusted;
+            this.cert = cert;
+        }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            if (type == ATTR_CLASS_PKEY) {
+                sb.append("\ttype=[private key]\n");
+            } else if (type == ATTR_CLASS_SKEY) {
+                sb.append("\ttype=[secret key]\n");
+            } else if (type == ATTR_CLASS_CERT) {
+                sb.append("\ttype=[trusted cert]\n");
+            }
+            sb.append("\tlabel=[" + label + "]\n");
+            if (id == null) {
+                sb.append("\tid=[null]\n");
+            } else {
+                sb.append("\tid=" + P11KeyStore.getID(id) + "\n");
+            }
+            sb.append("\ttrusted=[" + trusted + "]\n");
+            sb.append("\tmatched=[" + matched + "]\n");
+            if (cert == null) {
+                sb.append("\tcert=[null]\n");
+            } else {
+                sb.append("\tcert=[\tsubject: " +
+                        cert.getSubjectX500Principal() +
+                        "\n\t\tissuer: " +
+                        cert.getIssuerX500Principal() +
+                        "\n\t\tserialNum: " +
+                        cert.getSerialNumber().toString() +
+                        "]");
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * callback handler for passing password to Provider.login method
+     */
+    private static class PasswordCallbackHandler implements CallbackHandler {
+
+        private char[] password;
+
+        private PasswordCallbackHandler(char[] password) {
+            if (password != null) {
+                this.password = password.clone();
+            }
+        }
+
+        public void handle(Callback[] callbacks)
+                throws IOException, UnsupportedCallbackException {
+            if (!(callbacks[0] instanceof PasswordCallback)) {
+                throw new UnsupportedCallbackException(callbacks[0]);
+            }
+            PasswordCallback pc = (PasswordCallback)callbacks[0];
+            pc.setPassword(password);  // this clones the password if not null
+        }
+
+        protected void finalize() throws Throwable {
+            if (password != null) {
+                Arrays.fill(password, ' ');
+            }
+            super.finalize();
+        }
+    }
+
+    /**
+     * getTokenObject return value.
+     *
+     * if object is not found, type is set to null.
+     * otherwise, type is set to the requested type.
+     */
+    private static class THandle {
+        private final long handle;              // token object handle
+        private final CK_ATTRIBUTE type;        // CKA_CLASS
+
+        private THandle(long handle, CK_ATTRIBUTE type) {
+            this.handle = handle;
+            this.type = type;
+        }
+    }
+
+    P11KeyStore(Token token) {
+        this.token = token;
+        this.useSecmodTrust = token.provider.nssUseSecmodTrust;
+    }
+
+    /**
+     * Returns the key associated with the given alias.
+     * The key must have been associated with
+     * the alias by a call to <code>setKeyEntry</code>,
+     * or by a call to <code>setEntry</code> with a
+     * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>.
+     *
+     * @param alias the alias name
+     * @param password the password, which must be <code>null</code>
+     *
+     * @return the requested key, or null if the given alias does not exist
+     * or does not identify a key-related entry.
+     *
+     * @exception NoSuchAlgorithmException if the algorithm for recovering the
+     * key cannot be found
+     * @exception UnrecoverableKeyException if the key cannot be recovered
+     */
+    public synchronized Key engineGetKey(String alias, char[] password)
+                throws NoSuchAlgorithmException, UnrecoverableKeyException {
+
+        token.ensureValid();
+        if (password != null && !token.config.getKeyStoreCompatibilityMode()) {
+            throw new NoSuchAlgorithmException("password must be null");
+        }
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {
+            return null;
+        }
+
+        Session session = null;
+        try {
+            session = token.getOpSession();
+
+            if (aliasInfo.type == ATTR_CLASS_PKEY) {
+                THandle h = getTokenObject(session,
+                                        aliasInfo.type,
+                                        aliasInfo.id,
+                                        null);
+                if (h.type == ATTR_CLASS_PKEY) {
+                    return loadPkey(session, h.handle);
+                }
+            } else {
+                THandle h = getTokenObject(session,
+                                        ATTR_CLASS_SKEY,
+                                        null,
+                                        alias);
+                if (h.type == ATTR_CLASS_SKEY) {
+                    return loadSkey(session, h.handle);
+                }
+            }
+
+            // did not find anything
+            return null;
+        } catch (PKCS11Exception | KeyStoreException e) {
+            throw new ProviderException(e);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    /**
+     * Returns the certificate chain associated with the given alias.
+     * The certificate chain must have been associated with the alias
+     * by a call to <code>setKeyEntry</code>,
+     * or by a call to <code>setEntry</code> with a
+     * <code>PrivateKeyEntry</code>.
+     *
+     * @param alias the alias name
+     *
+     * @return the certificate chain (ordered with the user's certificate first
+     * and the root certificate authority last), or null if the given alias
+     * does not exist or does not contain a certificate chain
+     */
+    public synchronized Certificate[] engineGetCertificateChain(String alias) {
+
+        token.ensureValid();
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) {
+            return null;
+        }
+        return aliasInfo.chain;
+    }
+
+    /**
+     * Returns the certificate associated with the given alias.
+     *
+     * <p> If the given alias name identifies an entry
+     * created by a call to <code>setCertificateEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>TrustedCertificateEntry</code>,
+     * then the trusted certificate contained in that entry is returned.
+     *
+     * <p> If the given alias name identifies an entry
+     * created by a call to <code>setKeyEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>PrivateKeyEntry</code>,
+     * then the first element of the certificate chain in that entry
+     * (if a chain exists) is returned.
+     *
+     * @param alias the alias name
+     *
+     * @return the certificate, or null if the given alias does not exist or
+     * does not contain a certificate.
+     */
+    public synchronized Certificate engineGetCertificate(String alias) {
+        token.ensureValid();
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null) {
+            return null;
+        }
+        return aliasInfo.cert;
+    }
+
+    /**
+     * Returns the creation date of the entry identified by the given alias.
+     *
+     * @param alias the alias name
+     *
+     * @return the creation date of this entry, or null if the given alias does
+     * not exist
+     */
+    public Date engineGetCreationDate(String alias) {
+        token.ensureValid();
+        throw new ProviderException(new UnsupportedOperationException());
+    }
+
+    /**
+     * Assigns the given key to the given alias, protecting it with the given
+     * password.
+     *
+     * <p>If the given key is of type <code>java.security.PrivateKey</code>,
+     * it must be accompanied by a certificate chain certifying the
+     * corresponding public key.
+     *
+     * <p>If the given alias already exists, the keystore information
+     * associated with it is overridden by the given key (and possibly
+     * certificate chain).
+     *
+     * @param alias the alias name
+     * @param key the key to be associated with the alias
+     * @param password the password to protect the key
+     * @param chain the certificate chain for the corresponding public
+     * key (only required if the given key is of type
+     * <code>java.security.PrivateKey</code>).
+     *
+     * @exception KeyStoreException if the given key cannot be protected, or
+     * this operation fails for some other reason
+     */
+    public synchronized void engineSetKeyEntry(String alias, Key key,
+                                   char[] password,
+                                   Certificate[] chain)
+                throws KeyStoreException {
+
+        token.ensureValid();
+        checkWrite();
+
+        if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) {
+            throw new KeyStoreException("key must be PrivateKey or SecretKey");
+        } else if (key instanceof PrivateKey && chain == null) {
+            throw new KeyStoreException
+                ("PrivateKey must be accompanied by non-null chain");
+        } else if (key instanceof SecretKey && chain != null) {
+            throw new KeyStoreException
+                ("SecretKey must be accompanied by null chain");
+        } else if (password != null &&
+                    !token.config.getKeyStoreCompatibilityMode()) {
+            throw new KeyStoreException("Password must be null");
+        }
+
+        KeyStore.Entry entry = null;
+        try {
+            if (key instanceof PrivateKey) {
+                entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, chain);
+            } else if (key instanceof SecretKey) {
+                entry = new KeyStore.SecretKeyEntry((SecretKey)key);
+            }
+        } catch (NullPointerException | IllegalArgumentException e) {
+            throw new KeyStoreException(e);
+        }
+        engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password));
+    }
+
+    /**
+     * Assigns the given key (that has already been protected) to the given
+     * alias.
+     *
+     * <p>If the protected key is of type
+     * <code>java.security.PrivateKey</code>,
+     * it must be accompanied by a certificate chain certifying the
+     * corresponding public key.
+     *
+     * <p>If the given alias already exists, the keystore information
+     * associated with it is overridden by the given key (and possibly
+     * certificate chain).
+     *
+     * @param alias the alias name
+     * @param key the key (in protected format) to be associated with the alias
+     * @param chain the certificate chain for the corresponding public
+     * key (only useful if the protected key is of type
+     * <code>java.security.PrivateKey</code>).
+     *
+     * @exception KeyStoreException if this operation fails.
+     */
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
+                throws KeyStoreException {
+        token.ensureValid();
+        throw new ProviderException(new UnsupportedOperationException());
+    }
+
+    /**
+     * Assigns the given certificate to the given alias.
+     *
+     * <p> If the given alias identifies an existing entry
+     * created by a call to <code>setCertificateEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>TrustedCertificateEntry</code>,
+     * the trusted certificate in the existing entry
+     * is overridden by the given certificate.
+     *
+     * @param alias the alias name
+     * @param cert the certificate
+     *
+     * @exception KeyStoreException if the given alias already exists and does
+     * not identify an entry containing a trusted certificate,
+     * or this operation fails for some other reason.
+     */
+    public synchronized void engineSetCertificateEntry
+        (String alias, Certificate cert) throws KeyStoreException {
+
+        token.ensureValid();
+        checkWrite();
+
+        if (cert == null) {
+            throw new KeyStoreException("invalid null certificate");
+        }
+
+        KeyStore.Entry entry = null;
+        entry = new KeyStore.TrustedCertificateEntry(cert);
+        engineSetEntry(alias, entry, null);
+    }
+
+    /**
+     * Deletes the entry identified by the given alias from this keystore.
+     *
+     * @param alias the alias name
+     *
+     * @exception KeyStoreException if the entry cannot be removed.
+     */
+    public synchronized void engineDeleteEntry(String alias)
+                throws KeyStoreException {
+        token.ensureValid();
+
+        if (token.isWriteProtected()) {
+            throw new KeyStoreException("token write-protected");
+        }
+        checkWrite();
+        deleteEntry(alias);
+    }
+
+    /**
+     * XXX - not sure whether to keep this
+     */
+    private boolean deleteEntry(String alias) throws KeyStoreException {
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo != null) {
+
+            aliasMap.remove(alias);
+
+            try {
+                if (aliasInfo.type == ATTR_CLASS_CERT) {
+                    // trusted certificate entry
+                    return destroyCert(aliasInfo.id);
+                } else if (aliasInfo.type == ATTR_CLASS_PKEY) {
+                    // private key entry
+                    return destroyPkey(aliasInfo.id) &&
+                                destroyChain(aliasInfo.id);
+                } else if (aliasInfo.type == ATTR_CLASS_SKEY) {
+                    // secret key entry
+                    return destroySkey(alias);
+                } else {
+                    throw new KeyStoreException("unexpected entry type");
+                }
+            } catch (PKCS11Exception | CertificateException e) {
+                throw new KeyStoreException(e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Lists all the alias names of this keystore.
+     *
+     * @return enumeration of the alias names
+     */
+    public synchronized Enumeration<String> engineAliases() {
+        token.ensureValid();
+
+        // don't want returned enumeration to iterate off actual keySet -
+        // otherwise applications that iterate and modify the keystore
+        // may run into concurrent modification problems
+        return Collections.enumeration(new HashSet<String>(aliasMap.keySet()));
+    }
+
+    /**
+     * Checks if the given alias exists in this keystore.
+     *
+     * @param alias the alias name
+     *
+     * @return true if the alias exists, false otherwise
+     */
+    public synchronized boolean engineContainsAlias(String alias) {
+        token.ensureValid();
+        return aliasMap.containsKey(alias);
+    }
+
+    /**
+     * Retrieves the number of entries in this keystore.
+     *
+     * @return the number of entries in this keystore
+     */
+    public synchronized int engineSize() {
+        token.ensureValid();
+        return aliasMap.size();
+    }
+
+    /**
+     * Returns true if the entry identified by the given alias
+     * was created by a call to <code>setKeyEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>PrivateKeyEntry</code> or a <code>SecretKeyEntry</code>.
+     *
+     * @param alias the alias for the keystore entry to be checked
+     *
+     * @return true if the entry identified by the given alias is a
+     * key-related, false otherwise.
+     */
+    public synchronized boolean engineIsKeyEntry(String alias) {
+        token.ensureValid();
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the entry identified by the given alias
+     * was created by a call to <code>setCertificateEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>TrustedCertificateEntry</code>.
+     *
+     * @param alias the alias for the keystore entry to be checked
+     *
+     * @return true if the entry identified by the given alias contains a
+     * trusted certificate, false otherwise.
+     */
+    public synchronized boolean engineIsCertificateEntry(String alias) {
+        token.ensureValid();
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the (alias) name of the first keystore entry whose certificate
+     * matches the given certificate.
+     *
+     * <p>This method attempts to match the given certificate with each
+     * keystore entry. If the entry being considered was
+     * created by a call to <code>setCertificateEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>TrustedCertificateEntry</code>,
+     * then the given certificate is compared to that entry's certificate.
+     *
+     * <p> If the entry being considered was
+     * created by a call to <code>setKeyEntry</code>,
+     * or created by a call to <code>setEntry</code> with a
+     * <code>PrivateKeyEntry</code>,
+     * then the given certificate is compared to the first
+     * element of that entry's certificate chain.
+     *
+     * @param cert the certificate to match with.
+     *
+     * @return the alias name of the first entry with matching certificate,
+     * or null if no such entry exists in this keystore.
+     */
+    public synchronized String engineGetCertificateAlias(Certificate cert) {
+        token.ensureValid();
+        Enumeration<String> e = engineAliases();
+        while (e.hasMoreElements()) {
+            String alias = e.nextElement();
+            Certificate tokenCert = engineGetCertificate(alias);
+            if (tokenCert != null && tokenCert.equals(cert)) {
+                return alias;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * engineStore currently is a No-op.
+     * Entries are stored to the token during engineSetEntry
+     *
+     * @param stream this must be <code>null</code>
+     * @param password this must be <code>null</code>
+     */
+    public synchronized void engineStore(OutputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException {
+        token.ensureValid();
+        if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {
+            throw new IOException("output stream must be null");
+        }
+
+        if (password != null && !token.config.getKeyStoreCompatibilityMode()) {
+            throw new IOException("password must be null");
+        }
+    }
+
+    /**
+     * engineStore currently is a No-op.
+     * Entries are stored to the token during engineSetEntry
+     *
+     * @param param this must be <code>null</code>
+     *
+     * @exception IllegalArgumentException if the given
+     *          <code>KeyStore.LoadStoreParameter</code>
+     *          input is not <code>null</code>
+     */
+    public synchronized void engineStore(KeyStore.LoadStoreParameter param)
+        throws IOException, NoSuchAlgorithmException, CertificateException {
+        token.ensureValid();
+        if (param != null) {
+            throw new IllegalArgumentException
+                ("LoadStoreParameter must be null");
+        }
+    }
+
+    /**
+     * Loads the keystore.
+     *
+     * @param stream the input stream, which must be <code>null</code>
+     * @param password the password used to unlock the keystore,
+     *          or <code>null</code> if the token supports a
+     *          CKF_PROTECTED_AUTHENTICATION_PATH
+     *
+     * @exception IOException if the given <code>stream</code> is not
+     *          <code>null</code>, if the token supports a
+     *          CKF_PROTECTED_AUTHENTICATION_PATH and a non-null
+     *          password is given, of if the token login operation failed
+     */
+    public synchronized void engineLoad(InputStream stream, char[] password)
+        throws IOException, NoSuchAlgorithmException, CertificateException {
+
+        token.ensureValid();
+
+        if (NSS_TEST) {
+            ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
+        }
+
+        if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {
+            throw new IOException("input stream must be null");
+        }
+
+        if (useSecmodTrust) {
+            nssTrustType = Secmod.TrustType.ALL;
+        }
+
+        try {
+            if (password == null) {
+                login(null);
+            } else {
+                login(new PasswordCallbackHandler(password));
+            }
+        } catch(LoginException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof PKCS11Exception) {
+                PKCS11Exception pe = (PKCS11Exception) cause;
+                if (pe.getErrorCode() == CKR_PIN_INCORRECT) {
+                    // if password is wrong, the cause of the IOException
+                    // should be an UnrecoverableKeyException
+                    throw new IOException("load failed",
+                            new UnrecoverableKeyException().initCause(e));
+                }
+            }
+            throw new IOException("load failed", e);
+        }
+
+        try {
+            if (mapLabels() == true) {
+                // CKA_LABELs are shared by multiple certs
+                writeDisabled = true;
+            }
+            if (debug != null) {
+                dumpTokenMap();
+            }
+        } catch (KeyStoreException | PKCS11Exception e) {
+            throw new IOException("load failed", e);
+        }
+    }
+
+    /**
+     * Loads the keystore using the given
+     * <code>KeyStore.LoadStoreParameter</code>.
+     *
+     * <p> The <code>LoadStoreParameter.getProtectionParameter()</code>
+     * method is expected to return a <code>KeyStore.PasswordProtection</code>
+     * object.  The password is retrieved from that object and used
+     * to unlock the PKCS#11 token.
+     *
+     * <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH
+     * then the provided password must be <code>null</code>.
+     *
+     * @param param the <code>KeyStore.LoadStoreParameter</code>
+     *
+     * @exception IllegalArgumentException if the given
+     *          <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,
+     *          or if that parameter returns a <code>null</code>
+     *          <code>ProtectionParameter</code> object.
+     *          input is not recognized
+     * @exception IOException if the token supports a
+     *          CKF_PROTECTED_AUTHENTICATION_PATH and the provided password
+     *          is non-null, or if the token login operation fails
+     */
+    public synchronized void engineLoad(KeyStore.LoadStoreParameter param)
+                throws IOException, NoSuchAlgorithmException,
+                CertificateException {
+
+        token.ensureValid();
+
+        if (NSS_TEST) {
+            ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
+        }
+
+        // if caller wants to pass a NULL password,
+        // force it to pass a non-NULL PasswordProtection that returns
+        // a NULL password
+
+        if (param == null) {
+            throw new IllegalArgumentException
+                        ("invalid null LoadStoreParameter");
+        }
+        if (useSecmodTrust) {
+            if (param instanceof Secmod.KeyStoreLoadParameter) {
+                nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();
+            } else {
+                nssTrustType = Secmod.TrustType.ALL;
+            }
+        }
+
+        CallbackHandler handler;
+        KeyStore.ProtectionParameter pp = param.getProtectionParameter();
+        if (pp instanceof PasswordProtection) {
+            char[] password = ((PasswordProtection)pp).getPassword();
+            if (password == null) {
+                handler = null;
+            } else {
+                handler = new PasswordCallbackHandler(password);
+            }
+        } else if (pp instanceof CallbackHandlerProtection) {
+            handler = ((CallbackHandlerProtection)pp).getCallbackHandler();
+        } else {
+            throw new IllegalArgumentException
+                        ("ProtectionParameter must be either " +
+                        "PasswordProtection or CallbackHandlerProtection");
+        }
+
+        try {
+            login(handler);
+            if (mapLabels() == true) {
+                // CKA_LABELs are shared by multiple certs
+                writeDisabled = true;
+            }
+            if (debug != null) {
+                dumpTokenMap();
+            }
+        } catch (LoginException | KeyStoreException | PKCS11Exception e) {
+            throw new IOException("load failed", e);
+        }
+    }
+
+    private void login(CallbackHandler handler) throws LoginException {
+        if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {
+            token.provider.login(null, handler);
+        } else {
+            // token supports protected authentication path
+            // (external pin-pad, for example)
+            if (handler != null &&
+                !token.config.getKeyStoreCompatibilityMode()) {
+                throw new LoginException("can not specify password if token " +
+                                "supports protected authentication path");
+            }
+
+            // must rely on application-set or default handler
+            // if one is necessary
+            token.provider.login(null, null);
+        }
+    }
+
+    /**
+     * Get a <code>KeyStore.Entry</code> for the specified alias
+     *
+     * @param alias get the <code>KeyStore.Entry</code> for this alias
+     * @param protParam this must be <code>null</code>
+     *
+     * @return the <code>KeyStore.Entry</code> for the specified alias,
+     *          or <code>null</code> if there is no such entry
+     *
+     * @exception KeyStoreException if the operation failed
+     * @exception NoSuchAlgorithmException if the algorithm for recovering the
+     *          entry cannot be found
+     * @exception UnrecoverableEntryException if the specified
+     *          <code>protParam</code> were insufficient or invalid
+     *
+     * @since 1.5
+     */
+    public synchronized KeyStore.Entry engineGetEntry(String alias,
+                        KeyStore.ProtectionParameter protParam)
+                throws KeyStoreException, NoSuchAlgorithmException,
+                UnrecoverableEntryException {
+
+        token.ensureValid();
+
+        if (protParam != null &&
+            protParam instanceof KeyStore.PasswordProtection &&
+            ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
+            !token.config.getKeyStoreCompatibilityMode()) {
+            throw new KeyStoreException("ProtectionParameter must be null");
+        }
+
+        AliasInfo aliasInfo = aliasMap.get(alias);
+        if (aliasInfo == null) {
+            if (debug != null) {
+                debug.println("engineGetEntry did not find alias [" +
+                        alias +
+                        "] in map");
+            }
+            return null;
+        }
+
+        Session session = null;
+        try {
+            session = token.getOpSession();
+
+            if (aliasInfo.type == ATTR_CLASS_CERT) {
+                // trusted certificate entry
+                if (debug != null) {
+                    debug.println("engineGetEntry found trusted cert entry");
+                }
+                return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);
+            } else if (aliasInfo.type == ATTR_CLASS_SKEY) {
+                // secret key entry
+                if (debug != null) {
+                    debug.println("engineGetEntry found secret key entry");
+                }
+
+                THandle h = getTokenObject
+                        (session, ATTR_CLASS_SKEY, null, aliasInfo.label);
+                if (h.type != ATTR_CLASS_SKEY) {
+                    throw new KeyStoreException
+                        ("expected but could not find secret key");
+                } else {
+                    SecretKey skey = loadSkey(session, h.handle);
+                    return new KeyStore.SecretKeyEntry(skey);
+                }
+            } else {
+                // private key entry
+                if (debug != null) {
+                    debug.println("engineGetEntry found private key entry");
+                }
+
+                THandle h = getTokenObject
+                        (session, ATTR_CLASS_PKEY, aliasInfo.id, null);
+                if (h.type != ATTR_CLASS_PKEY) {
+                    throw new KeyStoreException
+                        ("expected but could not find private key");
+                } else {
+                    PrivateKey pkey = loadPkey(session, h.handle);
+                    Certificate[] chain = aliasInfo.chain;
+                    if ((pkey != null) && (chain != null)) {
+                        return new KeyStore.PrivateKeyEntry(pkey, chain);
+                    } else {
+                        if (debug != null) {
+                            debug.println
+                                ("engineGetEntry got null cert chain or private key");
+                        }
+                    }
+                }
+            }
+            return null;
+        } catch (PKCS11Exception pe) {
+            throw new KeyStoreException(pe);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    /**
+     * Save a <code>KeyStore.Entry</code> under the specified alias.
+     *
+     * <p> If an entry already exists for the specified alias,
+     * it is overridden.
+     *
+     * <p> This KeyStore implementation only supports the standard
+     * entry types, and only supports X509Certificates in
+     * TrustedCertificateEntries.  Also, this implementation does not support
+     * protecting entries using a different password
+     * from the one used for token login.
+     *
+     * <p> Entries are immediately stored on the token.
+     *
+     * @param alias save the <code>KeyStore.Entry</code> under this alias
+     * @param entry the <code>Entry</code> to save
+     * @param protParam this must be <code>null</code>
+     *
+     * @exception KeyStoreException if this operation fails
+     *
+     * @since 1.5
+     */
+    public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
+                        KeyStore.ProtectionParameter protParam)
+                throws KeyStoreException {
+
+        token.ensureValid();
+        checkWrite();
+
+        if (protParam != null &&
+            protParam instanceof KeyStore.PasswordProtection &&
+            ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
+            !token.config.getKeyStoreCompatibilityMode()) {
+            throw new KeyStoreException(new UnsupportedOperationException
+                                ("ProtectionParameter must be null"));
+        }
+
+        if (token.isWriteProtected()) {
+            throw new KeyStoreException("token write-protected");
+        }
+
+        if (entry instanceof KeyStore.TrustedCertificateEntry) {
+
+            if (useSecmodTrust == false) {
+                // PKCS #11 does not allow app to modify trusted certs -
+                throw new KeyStoreException(new UnsupportedOperationException
+                                    ("trusted certificates may only be set by " +
+                                    "token initialization application"));
+            }
+            Module module = token.provider.nssModule;
+            if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {
+                // XXX allow TRUSTANCHOR module
+                throw new KeyStoreException("Trusted certificates can only be "
+                    + "added to the NSS KeyStore module");
+            }
+            Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();
+            if (cert instanceof X509Certificate == false) {
+                throw new KeyStoreException("Certificate must be an X509Certificate");
+            }
+            X509Certificate xcert = (X509Certificate)cert;
+            AliasInfo info = aliasMap.get(alias);
+            if (info != null) {
+                // XXX try to update
+                deleteEntry(alias);
+            }
+            try {
+                storeCert(alias, xcert);
+                module.setTrust(token, xcert);
+                mapLabels();
+            } catch (PKCS11Exception | CertificateException e) {
+                throw new KeyStoreException(e);
+            }
+
+        } else {
+
+            if (entry instanceof KeyStore.PrivateKeyEntry) {
+
+                PrivateKey key =
+                        ((KeyStore.PrivateKeyEntry)entry).getPrivateKey();
+                if (!(key instanceof P11Key) &&
+                    !(key instanceof RSAPrivateKey) &&
+                    !(key instanceof DSAPrivateKey) &&
+                    !(key instanceof DHPrivateKey) &&
+                    !(key instanceof ECPrivateKey)) {
+                    throw new KeyStoreException("unsupported key type: " +
+                                                key.getClass().getName());
+                }
+
+                // only support X509Certificate chains
+                Certificate[] chain =
+                    ((KeyStore.PrivateKeyEntry)entry).getCertificateChain();
+                if (!(chain instanceof X509Certificate[])) {
+                    throw new KeyStoreException
+                        (new UnsupportedOperationException
+                                ("unsupported certificate array type: " +
+                                chain.getClass().getName()));
+                }
+
+                try {
+                    boolean updatedAlias = false;
+                    Set<String> aliases = aliasMap.keySet();
+                    for (String oldAlias : aliases) {
+
+                        // see if there's an existing entry with the same info
+
+                        AliasInfo aliasInfo = aliasMap.get(oldAlias);
+                        if (aliasInfo.type == ATTR_CLASS_PKEY &&
+                            aliasInfo.cert.getPublicKey().equals
+                                        (chain[0].getPublicKey())) {
+
+                            // found existing entry -
+                            // caller is renaming entry or updating cert chain
+                            //
+                            // set new CKA_LABEL/CKA_ID
+                            // and update certs if necessary
+
+                            updatePkey(alias,
+                                        aliasInfo.id,
+                                        (X509Certificate[])chain,
+                                        !aliasInfo.cert.equals(chain[0]));
+                            updatedAlias = true;
+                            break;
+                        }
+                    }
+
+                    if (!updatedAlias) {
+                        // caller adding new entry
+                        engineDeleteEntry(alias);
+                        storePkey(alias, (KeyStore.PrivateKeyEntry)entry);
+                    }
+
+                } catch (PKCS11Exception | CertificateException pe) {
+                    throw new KeyStoreException(pe);
+                }
+
+            } else if (entry instanceof KeyStore.SecretKeyEntry) {
+
+                KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
+                SecretKey skey = ske.getSecretKey();
+
+                try {
+                    // first check if the key already exists
+                    AliasInfo aliasInfo = aliasMap.get(alias);
+
+                    if (aliasInfo != null) {
+                        engineDeleteEntry(alias);
+                    }
+                    storeSkey(alias, ske);
+
+                } catch (PKCS11Exception pe) {
+                    throw new KeyStoreException(pe);
+                }
+
+            } else {
+                throw new KeyStoreException(new UnsupportedOperationException
+                    ("unsupported entry type: " + entry.getClass().getName()));
+            }
+
+            try {
+
+                // XXX  NSS does not write out the CKA_ID we pass to them
+                //
+                // therefore we must re-map labels
+                // (can not simply update aliasMap)
+
+                mapLabels();
+                if (debug != null) {
+                    dumpTokenMap();
+                }
+            } catch (PKCS11Exception | CertificateException pe) {
+                throw new KeyStoreException(pe);
+            }
+        }
+
+        if (debug != null) {
+            debug.println
+                ("engineSetEntry added new entry for [" +
+                alias +
+                "] to token");
+        }
+    }
+
+    /**
+     * Determines if the keystore <code>Entry</code> for the specified
+     * <code>alias</code> is an instance or subclass of the specified
+     * <code>entryClass</code>.
+     *
+     * @param alias the alias name
+     * @param entryClass the entry class
+     *
+     * @return true if the keystore <code>Entry</code> for the specified
+     *          <code>alias</code> is an instance or subclass of the
+     *          specified <code>entryClass</code>, false otherwise
+     */
+    public synchronized boolean engineEntryInstanceOf
+                (String alias, Class<? extends KeyStore.Entry> entryClass) {
+        token.ensureValid();
+        return super.engineEntryInstanceOf(alias, entryClass);
+    }
+
+    private X509Certificate loadCert(Session session, long oHandle)
+                throws PKCS11Exception, CertificateException {
+
+        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]
+                        { new CK_ATTRIBUTE(CKA_VALUE) };
+        token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+
+        byte[] bytes = attrs[0].getByteArray();
+        if (bytes == null) {
+            throw new CertificateException
+                        ("unexpectedly retrieved null byte array");
+        }
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        return (X509Certificate)cf.generateCertificate
+                        (new ByteArrayInputStream(bytes));
+    }
+
+    private X509Certificate[] loadChain(Session session,
+                                        X509Certificate endCert)
+                throws PKCS11Exception, CertificateException {
+
+        ArrayList<X509Certificate> lChain = null;
+
+        if (endCert.getSubjectX500Principal().equals
+            (endCert.getIssuerX500Principal())) {
+            // self signed
+            return new X509Certificate[] { endCert };
+        } else {
+            lChain = new ArrayList<X509Certificate>();
+            lChain.add(endCert);
+        }
+
+        // try loading remaining certs in chain by following
+        // issuer->subject links
+
+        X509Certificate next = endCert;
+        while (true) {
+            CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                        ATTR_TOKEN_TRUE,
+                        ATTR_CLASS_CERT,
+                        new CK_ATTRIBUTE(CKA_SUBJECT,
+                                next.getIssuerX500Principal().getEncoded()) };
+            long[] ch = findObjects(session, attrs);
+
+            if (ch == null || ch.length == 0) {
+                // done
+                break;
+            } else {
+                // if more than one found, use first
+                if (debug != null && ch.length > 1) {
+                    debug.println("engineGetEntry found " +
+                                ch.length +
+                                " certificate entries for subject [" +
+                                next.getIssuerX500Principal().toString() +
+                                "] in token - using first entry");
+                }
+
+                next = loadCert(session, ch[0]);
+                lChain.add(next);
+                if (next.getSubjectX500Principal().equals
+                    (next.getIssuerX500Principal())) {
+                    // self signed
+                    break;
+                }
+            }
+        }
+
+        return lChain.toArray(new X509Certificate[lChain.size()]);
+    }
+
+    private SecretKey loadSkey(Session session, long oHandle)
+                throws PKCS11Exception {
+
+        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                        new CK_ATTRIBUTE(CKA_KEY_TYPE) };
+        token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+        long kType = attrs[0].getLong();
+
+        String keyType = null;
+        int keyLength = -1;
+
+        // XXX NSS mangles the stored key type for secret key token objects
+
+        if (kType == CKK_DES || kType == CKK_DES3) {
+            if (kType == CKK_DES) {
+                keyType = "DES";
+                keyLength = 64;
+            } else if (kType == CKK_DES3) {
+                keyType = "DESede";
+                keyLength = 192;
+            }
+        } else {
+            if (kType == CKK_AES) {
+                keyType = "AES";
+            } else if (kType == CKK_BLOWFISH) {
+                keyType = "Blowfish";
+            } else if (kType == CKK_RC4) {
+                keyType = "ARCFOUR";
+            } else {
+                if (debug != null) {
+                    debug.println("unknown key type [" +
+                                kType +
+                                "] - using 'Generic Secret'");
+                }
+                keyType = "Generic Secret";
+            }
+
+            // XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?
+            if (NSS_TEST) {
+                keyLength = 128;
+            } else {
+                attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };
+                token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+                keyLength = (int)attrs[0].getLong();
+            }
+        }
+
+        return P11Key.secretKey(session, oHandle, keyType, keyLength, null);
+    }
+
+    private PrivateKey loadPkey(Session session, long oHandle)
+        throws PKCS11Exception, KeyStoreException {
+
+        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                        new CK_ATTRIBUTE(CKA_KEY_TYPE) };
+        token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+        long kType = attrs[0].getLong();
+        String keyType = null;
+        int keyLength = 0;
+
+        if (kType == CKK_RSA) {
+
+            keyType = "RSA";
+
+            attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };
+            token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+            BigInteger modulus = attrs[0].getBigInteger();
+            keyLength = modulus.bitLength();
+
+            // This check will combine our "don't care" values here
+            // with the system-wide min/max values.
+            try {
+                RSAKeyFactory.checkKeyLengths(keyLength, null,
+                    -1, Integer.MAX_VALUE);
+            } catch (InvalidKeyException e) {
+                throw new KeyStoreException(e.getMessage());
+            }
+
+            return P11Key.privateKey(session,
+                                oHandle,
+                                keyType,
+                                keyLength,
+                                null);
+
+        } else if (kType == CKK_DSA) {
+
+            keyType = "DSA";
+
+            attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
+            token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+            BigInteger prime = attrs[0].getBigInteger();
+            keyLength = prime.bitLength();
+
+            return P11Key.privateKey(session,
+                                oHandle,
+                                keyType,
+                                keyLength,
+                                null);
+
+        } else if (kType == CKK_DH) {
+
+            keyType = "DH";
+
+            attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
+            token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+            BigInteger prime = attrs[0].getBigInteger();
+            keyLength = prime.bitLength();
+
+            return P11Key.privateKey(session,
+                                oHandle,
+                                keyType,
+                                keyLength,
+                                null);
+
+        } else if (kType == CKK_EC) {
+
+            attrs = new CK_ATTRIBUTE[] {
+                new CK_ATTRIBUTE(CKA_EC_PARAMS),
+            };
+            token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
+            byte[] encodedParams = attrs[0].getByteArray();
+            try {
+                ECParameterSpec params =
+                    ECUtil.getECParameterSpec(null, encodedParams);
+                keyLength = params.getCurve().getField().getFieldSize();
+            } catch (IOException e) {
+                // we do not want to accept key with unsupported parameters
+                throw new KeyStoreException("Unsupported parameters", e);
+            }
+
+            return P11Key.privateKey(session, oHandle, "EC", keyLength, null);
+
+        } else {
+            if (debug != null) {
+                debug.println("unknown key type [" + kType + "]");
+            }
+            throw new KeyStoreException("unknown key type");
+        }
+    }
+
+
+    /**
+     * XXX  On ibutton, when you C_SetAttribute(CKA_ID) for a private key
+     *      it not only changes the CKA_ID of the private key,
+     *      it changes the CKA_ID of the corresponding cert too.
+     *      And vice versa.
+     *
+     * XXX  On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)
+     *      for a private key, and then try to delete the corresponding cert.
+     *      So this code reverses the order.
+     *      After the cert is first destroyed (if necessary),
+     *      then the CKA_ID of the private key can be changed successfully.
+     *
+     * @param replaceCert if true, then caller is updating alias info for
+     *                  existing cert (only update CKA_ID/CKA_LABEL).
+     *                  if false, then caller is updating cert chain
+     *                  (delete old end cert and add new chain).
+     */
+    private void updatePkey(String alias,
+                        byte[] cka_id,
+                        X509Certificate[] chain,
+                        boolean replaceCert) throws
+                KeyStoreException, CertificateException, PKCS11Exception {
+
+        // XXX
+        //
+        // always set replaceCert to true
+        //
+        // NSS does not allow resetting of CKA_LABEL on an existing cert
+        // (C_SetAttribute call succeeds, but is ignored)
+
+        replaceCert = true;
+
+        Session session = null;
+        try {
+            session = token.getOpSession();
+
+            // first get private key object handle and hang onto it
+
+            THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
+            long pKeyHandle;
+            if (h.type == ATTR_CLASS_PKEY) {
+                pKeyHandle = h.handle;
+            } else {
+                throw new KeyStoreException
+                        ("expected but could not find private key " +
+                        "with CKA_ID " +
+                        getID(cka_id));
+            }
+
+            // next find existing end entity cert
+
+            h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
+            if (h.type != ATTR_CLASS_CERT) {
+                throw new KeyStoreException
+                        ("expected but could not find certificate " +
+                        "with CKA_ID " +
+                        getID(cka_id));
+            } else {
+                if (replaceCert) {
+                    // replacing existing cert and chain
+                    destroyChain(cka_id);
+                } else {
+                    // renaming alias for existing cert
+                    CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                        new CK_ATTRIBUTE(CKA_LABEL, alias),
+                        new CK_ATTRIBUTE(CKA_ID, alias) };
+                    token.p11.C_SetAttributeValue
+                        (session.id(), h.handle, attrs);
+                }
+            }
+
+            // add new chain
+
+            if (replaceCert) {
+                // add all certs in chain
+                storeChain(alias, chain);
+            } else {
+                // already updated alias info for existing end cert -
+                // just update CA certs
+                storeCaCerts(chain, 1);
+            }
+
+            // finally update CKA_ID for private key
+            //
+            // ibutton may have already done this (that is ok)
+
+            CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                                new CK_ATTRIBUTE(CKA_ID, alias) };
+            token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);
+
+            if (debug != null) {
+                debug.println("updatePkey set new alias [" +
+                                alias +
+                                "] for private key entry");
+            }
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
+                throws PKCS11Exception {
+
+        // if token key, update alias.
+        // if session key, convert to token key.
+
+        Session session = null;
+        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);
+                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[] {
+                    ATTR_TOKEN_TRUE,
+                    new CK_ATTRIBUTE(CKA_ID, alias),
+                };
+                if (attribute != null) {
+                    attrs = addAttribute(attrs, attribute);
+                }
+                token.p11.C_CopyObject(session.id(), key.keyID, attrs);
+                if (debug != null) {
+                    debug.println("updateP11Pkey copied private session key " +
+                                "for [" +
+                                alias +
+                                "] to token entry");
+                }
+            }
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private void storeCert(String alias, X509Certificate cert)
+                throws PKCS11Exception, CertificateException {
+
+        ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();
+        attrList.add(ATTR_TOKEN_TRUE);
+        attrList.add(ATTR_CLASS_CERT);
+        attrList.add(ATTR_X509_CERT_TYPE);
+        attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,
+                                cert.getSubjectX500Principal().getEncoded()));
+        attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,
+                                cert.getIssuerX500Principal().getEncoded()));
+        attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,
+                                cert.getSerialNumber().toByteArray()));
+        attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));
+
+        if (alias != null) {
+            attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));
+            attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));
+        } else {
+            // ibutton requires something to be set
+            // - alias must be unique
+            attrList.add(new CK_ATTRIBUTE(CKA_ID,
+                        getID(cert.getSubjectX500Principal().getName
+                                        (X500Principal.CANONICAL), cert)));
+        }
+
+        Session session = null;
+        try {
+            session = token.getOpSession();
+            token.p11.C_CreateObject(session.id(),
+                        attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));
+        } finally {
+            token.releaseSession(session);
+        }
+    }
+
+    private void storeChain(String alias, X509Certificate[] chain)
+                throws PKCS11Exception, CertificateException {
+
+        // add new chain
+        //
+        // end cert has CKA_LABEL and CKA_ID set to alias.
+        // other certs in chain have neither set.
+
+        storeCert(alias, chain[0]);
+        storeCaCerts(chain, 1);
+    }
+
+    private void storeCaCerts(X509Certificate[] chain, int start)
+                throws PKCS11Exception, CertificateException {
+
+        // do not add duplicate CA cert if already in token
+        //
+        // XXX   ibutton stores duplicate CA certs, NSS does not
+
+        Session session = null;
+        HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();
+        try {
+            session = token.getOpSession();
+            CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+                        ATTR_TOKEN_TRUE,
+                        ATTR_CLASS_CERT };
+            long[] handles = findObjects(session, attrs);
+
+            // load certs currently on the token
+            for (long handle : handles) {
+                cacerts.add(loadCert(session, handle));
+            }
+        } finally {
+            token.releaseSession(session);
+        }
+
+        for (int i = start; i < chain.length; i++) {
+            if (!cacerts.contains(chain[i])) {
+                storeCert(null, chain[i]);
+            } else if (debug != null) {
+                debug.println("ignoring duplicate CA cert for [" +
+                        chain[i].getSubjectX500Principal() +
+                        "]");
+            }
+        }
+    }
+
+    private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)
+                throws PKCS11Exception, KeyStoreException {
+
+        SecretKey skey = ske.getSecretKey();
+        // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
+        // they are handled in P11SecretKeyFactory.createKey() method.
+        CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
+            ATTR_SKEY_TOKEN_TRUE,
+            ATTR_PRIVATE_TRUE,
+            new CK_ATTRIBUTE(CKA_LABEL, alias),
+        };
+        try {
+            P11SecretKeyFactory.convertKey(token, skey, null, attrs);
+        } catch (InvalidKeyException ike) {
+            // re-throw KeyStoreException to match javadoc
+            throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
+        }
+
+        // update global alias map
+        aliasMap.put(alias, new AliasInfo(alias));
+
+        if (debug != null) {
+            debug.println("storeSkey created token secret key for [" +
+                          alias + "]");
+        }
+    }
+
+    private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {
+        int n = attrs.length;
+        CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];
+        System.arraycopy(attrs, 0, newAttrs, 0, n);
+        newAttrs[n] = attr;
+        return newAttrs;
+    }
+
+    private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)
+        throws PKCS11Exception, CertificateException, KeyStoreException  {
+
+        PrivateKey key = pke.getPrivateKey();
+        CK_ATTRIBUTE[] attrs = null;
+
+        // If the key is a token object on this token, update it instead
+        // of creating a duplicate key object.
+        // Otherwise, treat a P11Key like any other key, if it is extractable.
+        if (key instanceof P11Key) {
+            P11Key p11Key = (P11Key)key;
+            if (p11Key.tokenObject && (p11Key.token == this.token)) {
+                updateP11Pkey(alias, null, p11Key);
+                storeChain(alias, (X509Certificate[])pke.getCertificateChain());
+                return;
+            }
+        }
+
+        boolean useNDB = token.config.getNssNetscapeDbWorkaround();
+        PublicKey publicKey = pke.getCertificate().getPublicKey();
+
+        if (key instanceof RSAPrivateKey) {
+
+            X509Certificate cert = (X509Certificate)pke.getCertificate();
+            attrs = getRsaPrivKeyAttrs
+                (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());
+
+        } else if (key instanceof DSAPrivateKey) {
+
+            DSAPrivateKey dsaKey = (DSAPrivateKey)key;
+
+            CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
+            if (idAttrs[0] == null) {
+                idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
+            }
+
+            attrs = new CK_ATTRIBUTE[] {
+                ATTR_TOKEN_TRUE,
+                ATTR_CLASS_PKEY,
+                ATTR_PRIVATE_TRUE,
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
+                idAttrs[0],
+                new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),
+                new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),
+                new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),
+                new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),
+            };
+            if (idAttrs[1] != null) {
+                attrs = addAttribute(attrs, idAttrs[1]);
+            }
+
+            attrs = token.getAttributes
+                (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);
+
+            if (debug != null) {
+                debug.println("storePkey created DSA template");
+            }
+
+        } else if (key instanceof DHPrivateKey) {
+
+            DHPrivateKey dhKey = (DHPrivateKey)key;
+
+            CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
+            if (idAttrs[0] == null) {
+                idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
+            }
+
+            attrs = new CK_ATTRIBUTE[] {
+                ATTR_TOKEN_TRUE,
+                ATTR_CLASS_PKEY,
+                ATTR_PRIVATE_TRUE,
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
+                idAttrs[0],
+                new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),
+                new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),
+                new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),
+            };
+            if (idAttrs[1] != null) {
+                attrs = addAttribute(attrs, idAttrs[1]);
+            }
+
+            attrs = token.getAttributes
+                (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);
+
+        } else if (key instanceof ECPrivateKey) {
+
+            ECPrivateKey ecKey = (ECPrivateKey)key;
+
+            CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
+            if (idAttrs[0] == null) {
+                idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
+            }
+
+            byte[] encodedParams =
+                ECUtil.encodeECParameterSpec(null, ecKey.getParams());
+            attrs = new CK_ATTRIBUTE[] {
+                ATTR_TOKEN_TRUE,
+                ATTR_CLASS_PKEY,
+                ATTR_PRIVATE_TRUE,
+                new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
+                idAttrs[0],
+                new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),
+                new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
+            };
+            if (idAttrs[1] != null) {
+                attrs = addAttribute(attrs, idAttrs[1]);
+            }
+
+            attrs = token.getAttributes
+                (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);
+
+            if (debug != null) {
+                debug