8086002: Move apple.security.AppleProvider to a proper module
Summary: Move Apple provider to java.base module with "libosxsecurity" native library.
Reviewed-by: mchung
--- a/jdk/make/lib/Lib-java.base.gmk Tue Aug 18 12:49:00 2015 -0700
+++ b/jdk/make/lib/Lib-java.base.gmk Tue Aug 18 21:16:27 2015 +0000
@@ -32,3 +32,4 @@
include CoreLibraries.gmk
include NetworkingLibraries.gmk
include NioLibraries.gmk
+include SecurityLibraries.gmk
--- a/jdk/make/lib/Lib-jdk.deploy.osx.gmk Tue Aug 18 12:49:00 2015 -0700
+++ b/jdk/make/lib/Lib-jdk.deploy.osx.gmk Tue Aug 18 21:16:27 2015 +0000
@@ -80,7 +80,6 @@
-framework ApplicationServices \
-framework JavaNativeFoundation \
-framework JavaRuntimeSupport \
- -framework Security \
-framework SystemConfiguration \
$(LDFLAGS_JDKLIB_SUFFIX), \
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosx, \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/make/lib/SecurityLibraries.gmk Tue Aug 18 21:16:27 2015 +0000
@@ -0,0 +1,63 @@
+#
+# Copyright (c) 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.
+#
+
+include LibCommon.gmk
+
+ifeq ($(OPENJDK_TARGET_OS), macosx)
+
+ ################################################################################
+
+ LIBOSXSECURITY_DIRS := $(JDK_TOPDIR)/src/java.base/macosx/native/libosxsecurity
+ LIBOSXSECURITY_CFLAGS := -I$(LIBOSXSECURITY_DIRS) \
+ $(LIBJAVA_HEADER_FLAGS) \
+ -I$(SUPPORT_OUTPUTDIR)/headers/java.base \
+
+ $(eval $(call SetupNativeCompilation,BUILD_LIBOSXSECURITY, \
+ LIBRARY := osxsecurity, \
+ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
+ SRC := $(LIBOSXSECURITY_DIRS), \
+ OPTIMIZATION := LOW, \
+ CFLAGS := $(CFLAGS_JDKLIB) \
+ $(LIBOSXSECURITY_CFLAGS), \
+ DISABLED_WARNINGS_clang := deprecated-declarations, \
+ LDFLAGS := $(LDFLAGS_JDKLIB) \
+ -L$(SUPPORT_OUTPUTDIR)/modules_libs/java.base \
+ $(call SET_SHARED_LIBRARY_ORIGIN), \
+ LDFLAGS_SUFFIX_macosx := \
+ -fobjc-link-runtime \
+ -framework JavaNativeFoundation \
+ -framework CoreServices \
+ -framework Security \
+ $(LDFLAGS_JDKLIB_SUFFIX), \
+ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxsecurity, \
+ DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
+
+ $(BUILD_LIBOSXSECURITY): $(BUILD_LIBJAVA)
+
+ TARGETS += $(BUILD_LIBOSXSECURITY)
+
+ ################################################################################
+
+endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/classes/apple/security/AppleProvider.java Tue Aug 18 21:16:27 2015 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011, 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 apple.security;
+
+import java.security.*;
+
+/**
+ * The Apple Security Provider.
+ */
+
+/**
+ * Defines the Apple provider.
+ *
+ * This provider only exists to provide access to the Apple keychain-based KeyStore implementation
+ */
+@SuppressWarnings("serial") // JDK implementation class
+public final class AppleProvider extends Provider {
+
+ private static final String info = "Apple Provider";
+
+ private static final class ProviderService extends Provider.Service {
+ ProviderService(Provider p, String type, String algo, String cn) {
+ super(p, type, algo, cn, null, null);
+ }
+
+ @Override
+ public Object newInstance(Object ctrParamObj)
+ throws NoSuchAlgorithmException {
+ String type = getType();
+ if (ctrParamObj != null) {
+ throw new InvalidParameterException
+ ("constructorParameter not used with " + type + " engines");
+ }
+
+ String algo = getAlgorithm();
+ try {
+ if (type.equals("KeyStore")) {
+ if (algo.equals("KeychainStore")) {
+ return new KeychainStore();
+ }
+ }
+ } catch (Exception ex) {
+ throw new NoSuchAlgorithmException("Error constructing " +
+ type + " for " + algo + " using Apple", ex);
+ }
+ throw new ProviderException("No impl for " + algo +
+ " " + type);
+ }
+ }
+
+
+ public AppleProvider() {
+ /* We are the Apple provider */
+ super("Apple", 1.9d, info);
+
+ final Provider p = this;
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ putService(new ProviderService(p, "KeyStore",
+ "KeychainStore", "apple.security.KeychainStore"));
+ return null;
+ }
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/classes/apple/security/KeychainStore.java Tue Aug 18 21:16:27 2015 +0000
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (c) 2011, 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 apple.security;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.security.spec.*;
+import java.util.*;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+import javax.security.auth.x500.*;
+
+import sun.security.pkcs.*;
+import sun.security.pkcs.EncryptedPrivateKeyInfo;
+import sun.security.util.*;
+import sun.security.x509.*;
+
+/**
+ * This class provides the keystore implementation referred to as "KeychainStore".
+ * It uses the current user's keychain as its backing storage, and does NOT support
+ * a file-based implementation.
+ */
+
+public final class KeychainStore extends KeyStoreSpi {
+
+ // Private keys and their supporting certificate chains
+ // If a key came from the keychain it has a SecKeyRef and one or more
+ // SecCertificateRef. When we delete the key we have to delete all of the corresponding
+ // native objects.
+ class KeyEntry {
+ Date date; // the creation date of this entry
+ byte[] protectedPrivKey;
+ char[] password;
+ long keyRef; // SecKeyRef for this key
+ Certificate chain[];
+ long chainRefs[]; // SecCertificateRefs for this key's chain.
+ };
+
+ // Trusted certificates
+ class TrustedCertEntry {
+ Date date; // the creation date of this entry
+
+ Certificate cert;
+ long certRef; // SecCertificateRef for this key
+ };
+
+ /**
+ * Entries that have been deleted. When something calls engineStore we'll
+ * remove them from the keychain.
+ */
+ private Hashtable<String, Object> deletedEntries = new Hashtable<>();
+
+ /**
+ * Entries that have been added. When something calls engineStore we'll
+ * add them to the keychain.
+ */
+ private Hashtable<String, Object> addedEntries = new Hashtable<>();
+
+ /**
+ * Private keys and certificates are stored in a hashtable.
+ * Hash entries are keyed by alias names.
+ */
+ private Hashtable<String, Object> entries = new Hashtable<>();
+
+ /**
+ * Algorithm identifiers and corresponding OIDs for the contents of the PKCS12 bag we get from the Keychain.
+ */
+ private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
+ private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3};
+ private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
+ private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
+
+ /**
+ * Constnats used in PBE decryption.
+ */
+ private static final int iterationCount = 1024;
+ private static final int SALT_LEN = 20;
+
+ static {
+ AccessController.doPrivileged(
+ new PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("osxsecurity");
+ return null;
+ }
+ });
+ try {
+ PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
+ pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
+ } catch (IOException ioe) {
+ // should not happen
+ }
+ }
+
+ private static void permissionCheck() {
+ SecurityManager sec = System.getSecurityManager();
+
+ if (sec != null) {
+ sec.checkPermission(new RuntimePermission("useKeychainStore"));
+ }
+ }
+
+
+ /**
+ * Verify the Apple provider in the constructor.
+ *
+ * @exception SecurityException if fails to verify
+ * its own integrity
+ */
+ public KeychainStore() { }
+
+ /**
+ * Returns the key associated with the given alias, using the given
+ * password to recover it.
+ *
+ * @param alias the alias name
+ * @param password the password for recovering the key. This password is
+ * used internally as the key is exported in a PKCS12 format.
+ *
+ * @return the requested key, or null if the given alias does not exist
+ * or does not identify a <i>key entry</i>.
+ *
+ * @exception NoSuchAlgorithmException if the algorithm for recovering the
+ * key cannot be found
+ * @exception UnrecoverableKeyException if the key cannot be recovered
+ * (e.g., the given password is wrong).
+ */
+ public Key engineGetKey(String alias, char[] password)
+ throws NoSuchAlgorithmException, UnrecoverableKeyException
+ {
+ permissionCheck();
+
+ // An empty password is rejected by MacOS API, no private key data
+ // is exported. If no password is passed (as is the case when
+ // this implementation is used as browser keystore in various
+ // deployment scenarios like Webstart, JFX and applets), create
+ // a dummy password so MacOS API is happy.
+ if (password == null || password.length == 0) {
+ // Must not be a char array with only a 0, as this is an empty
+ // string.
+ if (random == null) {
+ random = new SecureRandom();
+ }
+ password = Long.toString(random.nextLong()).toCharArray();
+ }
+
+ Object entry = entries.get(alias.toLowerCase());
+
+ if (entry == null || !(entry instanceof KeyEntry)) {
+ return null;
+ }
+
+ // This call gives us a PKCS12 bag, with the key inside it.
+ byte[] exportedKeyInfo = _getEncodedKeyData(((KeyEntry)entry).keyRef, password);
+ if (exportedKeyInfo == null) {
+ return null;
+ }
+
+ PrivateKey returnValue = null;
+
+ try {
+ byte[] pkcs8KeyData = fetchPrivateKeyFromBag(exportedKeyInfo);
+ byte[] encryptedKey;
+ AlgorithmParameters algParams;
+ ObjectIdentifier algOid;
+ try {
+ // get the encrypted private key
+ EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(pkcs8KeyData);
+ encryptedKey = encrInfo.getEncryptedData();
+
+ // parse Algorithm parameters
+ DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
+ DerInputStream in = val.toDerInputStream();
+ algOid = in.getOID();
+ algParams = parseAlgParameters(in);
+
+ } catch (IOException ioe) {
+ UnrecoverableKeyException uke =
+ new UnrecoverableKeyException("Private key not stored as "
+ + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
+ uke.initCause(ioe);
+ throw uke;
+ }
+
+ // Use JCE to decrypt the data using the supplied password.
+ SecretKey skey = getPBEKey(password);
+ Cipher cipher = Cipher.getInstance(algOid.toString());
+ cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
+ byte[] decryptedPrivateKey = cipher.doFinal(encryptedKey);
+ PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(decryptedPrivateKey);
+
+ // Parse the key algorithm and then use a JCA key factory to create the private key.
+ DerValue val = new DerValue(decryptedPrivateKey);
+ DerInputStream in = val.toDerInputStream();
+
+ // Ignore this -- version should be 0.
+ int i = in.getInteger();
+
+ // Get the Algorithm ID next
+ DerValue[] value = in.getSequence(2);
+ AlgorithmId algId = new AlgorithmId(value[0].getOID());
+ String algName = algId.getName();
+
+ // Get a key factory for this algorithm. It's likely to be 'RSA'.
+ KeyFactory kfac = KeyFactory.getInstance(algName);
+ returnValue = kfac.generatePrivate(kspec);
+ } catch (Exception e) {
+ UnrecoverableKeyException uke =
+ new UnrecoverableKeyException("Get Key failed: " +
+ e.getMessage());
+ uke.initCause(e);
+ throw uke;
+ }
+
+ return returnValue;
+ }
+
+ private native byte[] _getEncodedKeyData(long secKeyRef, char[] password);
+
+ /**
+ * Returns the certificate chain associated with the given alias.
+ *
+ * @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 (i.e., the given
+ * alias identifies either a <i>trusted certificate entry</i> or a
+ * <i>key entry</i> without a certificate chain).
+ */
+ public Certificate[] engineGetCertificateChain(String alias) {
+ permissionCheck();
+
+ Object entry = entries.get(alias.toLowerCase());
+
+ if (entry != null && entry instanceof KeyEntry) {
+ if (((KeyEntry)entry).chain == null) {
+ return null;
+ } else {
+ return ((KeyEntry)entry).chain.clone();
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the certificate associated with the given alias.
+ *
+ * <p>If the given alias name identifies a
+ * <i>trusted certificate entry</i>, the certificate associated with that
+ * entry is returned. If the given alias name identifies a
+ * <i>key entry</i>, the first element of the certificate chain of that
+ * entry is returned, or null if that entry does not have a certificate
+ * chain.
+ *
+ * @param alias the alias name
+ *
+ * @return the certificate, or null if the given alias does not exist or
+ * does not contain a certificate.
+ */
+ public Certificate engineGetCertificate(String alias) {
+ permissionCheck();
+
+ Object entry = entries.get(alias.toLowerCase());
+
+ if (entry != null) {
+ if (entry instanceof TrustedCertEntry) {
+ return ((TrustedCertEntry)entry).cert;
+ } else {
+ KeyEntry ke = (KeyEntry)entry;
+ if (ke.chain == null || ke.chain.length == 0) {
+ return null;
+ }
+ return ke.chain[0];
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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) {
+ permissionCheck();
+
+ Object entry = entries.get(alias.toLowerCase());
+
+ if (entry != null) {
+ if (entry instanceof TrustedCertEntry) {
+ return new Date(((TrustedCertEntry)entry).date.getTime());
+ } else {
+ return new Date(((KeyEntry)entry).date.getTime());
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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 void engineSetKeyEntry(String alias, Key key, char[] password,
+ Certificate[] chain)
+ throws KeyStoreException
+ {
+ permissionCheck();
+
+ synchronized(entries) {
+ try {
+ KeyEntry entry = new KeyEntry();
+ entry.date = new Date();
+
+ if (key instanceof PrivateKey) {
+ if ((key.getFormat().equals("PKCS#8")) ||
+ (key.getFormat().equals("PKCS8"))) {
+ entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password);
+ entry.password = password.clone();
+ } else {
+ throw new KeyStoreException("Private key is not encoded as PKCS#8");
+ }
+ } else {
+ throw new KeyStoreException("Key is not a PrivateKey");
+ }
+
+ // clone the chain
+ if (chain != null) {
+ if ((chain.length > 1) && !validateChain(chain)) {
+ throw new KeyStoreException("Certificate chain does not validate");
+ }
+
+ entry.chain = chain.clone();
+ entry.chainRefs = new long[entry.chain.length];
+ }
+
+ String lowerAlias = alias.toLowerCase();
+ if (entries.get(lowerAlias) != null) {
+ deletedEntries.put(lowerAlias, entries.get(lowerAlias));
+ }
+
+ entries.put(lowerAlias, entry);
+ addedEntries.put(lowerAlias, entry);
+ } catch (Exception nsae) {
+ KeyStoreException ke = new KeyStoreException("Key protection algorithm not found: " + nsae);
+ ke.initCause(nsae);
+ throw ke;
+ }
+ }
+ }
+
+ /**
+ * 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. If the
+ * underlying keystore implementation is of type <code>jks</code>,
+ * <code>key</code> must be encoded as an
+ * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
+ *
+ * <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
+ {
+ permissionCheck();
+
+ synchronized(entries) {
+ // key must be encoded as EncryptedPrivateKeyInfo as defined in
+ // PKCS#8
+ KeyEntry entry = new KeyEntry();
+ try {
+ EncryptedPrivateKeyInfo privateKey = new EncryptedPrivateKeyInfo(key);
+ entry.protectedPrivKey = privateKey.getEncoded();
+ } catch (IOException ioe) {
+ throw new KeyStoreException("key is not encoded as "
+ + "EncryptedPrivateKeyInfo");
+ }
+
+ entry.date = new Date();
+
+ if ((chain != null) &&
+ (chain.length != 0)) {
+ entry.chain = chain.clone();
+ entry.chainRefs = new long[entry.chain.length];
+ }
+
+ String lowerAlias = alias.toLowerCase();
+ if (entries.get(lowerAlias) != null) {
+ deletedEntries.put(lowerAlias, entries.get(alias));
+ }
+ entries.put(lowerAlias, entry);
+ addedEntries.put(lowerAlias, entry);
+ }
+ }
+
+ /**
+ * Assigns the given certificate to the given alias.
+ *
+ * <p>If the given alias already exists in this keystore and identifies a
+ * <i>trusted certificate entry</i>, the certificate associated with it 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 a <i>trusted certificate entry</i>, or this operation
+ * fails for some other reason.
+ */
+ public void engineSetCertificateEntry(String alias, Certificate cert)
+ throws KeyStoreException
+ {
+ permissionCheck();
+
+ synchronized(entries) {
+
+ Object entry = entries.get(alias.toLowerCase());
+ if ((entry != null) && (entry instanceof KeyEntry)) {
+ throw new KeyStoreException
+ ("Cannot overwrite key entry with certificate");
+ }
+
+ // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry.
+ // Security framework doesn't support the same certificate twice in a keychain.
+ Collection<Object> allValues = entries.values();
+
+ for (Object value : allValues) {
+ if (value instanceof TrustedCertEntry) {
+ TrustedCertEntry tce = (TrustedCertEntry)value;
+ if (tce.cert.equals(cert)) {
+ throw new KeyStoreException("Keychain does not support mulitple copies of same certificate.");
+ }
+ }
+ }
+
+ TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
+ trustedCertEntry.cert = cert;
+ trustedCertEntry.date = new Date();
+ String lowerAlias = alias.toLowerCase();
+ if (entries.get(lowerAlias) != null) {
+ deletedEntries.put(lowerAlias, entries.get(lowerAlias));
+ }
+ entries.put(lowerAlias, trustedCertEntry);
+ addedEntries.put(lowerAlias, trustedCertEntry);
+ }
+ }
+
+ /**
+ * 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 void engineDeleteEntry(String alias)
+ throws KeyStoreException
+ {
+ permissionCheck();
+
+ synchronized(entries) {
+ Object entry = entries.remove(alias.toLowerCase());
+ deletedEntries.put(alias.toLowerCase(), entry);
+ }
+ }
+
+ /**
+ * Lists all the alias names of this keystore.
+ *
+ * @return enumeration of the alias names
+ */
+ public Enumeration<String> engineAliases() {
+ permissionCheck();
+ return entries.keys();
+ }
+
+ /**
+ * Checks if the given alias exists in this keystore.
+ *
+ * @param alias the alias name
+ *
+ * @return true if the alias exists, false otherwise
+ */
+ public boolean engineContainsAlias(String alias) {
+ permissionCheck();
+ return entries.containsKey(alias.toLowerCase());
+ }
+
+ /**
+ * Retrieves the number of entries in this keystore.
+ *
+ * @return the number of entries in this keystore
+ */
+ public int engineSize() {
+ permissionCheck();
+ return entries.size();
+ }
+
+ /**
+ * Returns true if the entry identified by the given alias is a
+ * <i>key entry</i>, and false otherwise.
+ *
+ * @return true if the entry identified by the given alias is a
+ * <i>key entry</i>, false otherwise.
+ */
+ public boolean engineIsKeyEntry(String alias) {
+ permissionCheck();
+ Object entry = entries.get(alias.toLowerCase());
+ if ((entry != null) && (entry instanceof KeyEntry)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the entry identified by the given alias is a
+ * <i>trusted certificate entry</i>, and false otherwise.
+ *
+ * @return true if the entry identified by the given alias is a
+ * <i>trusted certificate entry</i>, false otherwise.
+ */
+ public boolean engineIsCertificateEntry(String alias) {
+ permissionCheck();
+ Object entry = entries.get(alias.toLowerCase());
+ if ((entry != null) && (entry instanceof TrustedCertEntry)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 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
+ * is a <i>trusted certificate entry</i>, the given certificate is
+ * compared to that entry's certificate. If the entry being considered is
+ * a <i>key entry</i>, the given certificate is compared to the first
+ * element of that entry's certificate chain (if a chain exists).
+ *
+ * @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 String engineGetCertificateAlias(Certificate cert) {
+ permissionCheck();
+ Certificate certElem;
+
+ for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
+ String alias = e.nextElement();
+ Object entry = entries.get(alias);
+ if (entry instanceof TrustedCertEntry) {
+ certElem = ((TrustedCertEntry)entry).cert;
+ } else {
+ KeyEntry ke = (KeyEntry)entry;
+ if (ke.chain == null || ke.chain.length == 0) {
+ continue;
+ }
+ certElem = ke.chain[0];
+ }
+ if (certElem.equals(cert)) {
+ return alias;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Stores this keystore to the given output stream, and protects its
+ * integrity with the given password.
+ *
+ * @param stream Ignored. the output stream to which this keystore is written.
+ * @param password the password to generate the keystore integrity check
+ *
+ * @exception IOException if there was an I/O problem with data
+ * @exception NoSuchAlgorithmException if the appropriate data integrity
+ * algorithm could not be found
+ * @exception CertificateException if any of the certificates included in
+ * the keystore data could not be stored
+ */
+ public void engineStore(OutputStream stream, char[] password)
+ throws IOException, NoSuchAlgorithmException, CertificateException
+ {
+ permissionCheck();
+
+ // Delete items that do have a keychain item ref.
+ for (Enumeration<String> e = deletedEntries.keys(); e.hasMoreElements(); ) {
+ String alias = e.nextElement();
+ Object entry = deletedEntries.get(alias);
+ if (entry instanceof TrustedCertEntry) {
+ if (((TrustedCertEntry)entry).certRef != 0) {
+ _removeItemFromKeychain(((TrustedCertEntry)entry).certRef);
+ _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
+ }
+ } else {
+ Certificate certElem;
+ KeyEntry keyEntry = (KeyEntry)entry;
+
+ if (keyEntry.chain != null) {
+ for (int i = 0; i < keyEntry.chain.length; i++) {
+ if (keyEntry.chainRefs[i] != 0) {
+ _removeItemFromKeychain(keyEntry.chainRefs[i]);
+ _releaseKeychainItemRef(keyEntry.chainRefs[i]);
+ }
+ }
+
+ if (keyEntry.keyRef != 0) {
+ _removeItemFromKeychain(keyEntry.keyRef);
+ _releaseKeychainItemRef(keyEntry.keyRef);
+ }
+ }
+ }
+ }
+
+ // Add all of the certs or keys in the added entries.
+ // No need to check for 0 refs, as they are in the added list.
+ for (Enumeration<String> e = addedEntries.keys(); e.hasMoreElements(); ) {
+ String alias = e.nextElement();
+ Object entry = addedEntries.get(alias);
+ if (entry instanceof TrustedCertEntry) {
+ TrustedCertEntry tce = (TrustedCertEntry)entry;
+ Certificate certElem;
+ certElem = tce.cert;
+ tce.certRef = addCertificateToKeychain(alias, certElem);
+ } else {
+ KeyEntry keyEntry = (KeyEntry)entry;
+
+ if (keyEntry.chain != null) {
+ for (int i = 0; i < keyEntry.chain.length; i++) {
+ keyEntry.chainRefs[i] = addCertificateToKeychain(alias, keyEntry.chain[i]);
+ }
+
+ keyEntry.keyRef = _addItemToKeychain(alias, false, keyEntry.protectedPrivKey, keyEntry.password);
+ }
+ }
+ }
+
+ // Clear the added and deletedEntries hashtables here, now that we're done with the updates.
+ // For the deleted entries, we freed up the native references above.
+ deletedEntries.clear();
+ addedEntries.clear();
+ }
+
+ private long addCertificateToKeychain(String alias, Certificate cert) {
+ byte[] certblob = null;
+ long returnValue = 0;
+
+ try {
+ certblob = cert.getEncoded();
+ returnValue = _addItemToKeychain(alias, true, certblob, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return returnValue;
+ }
+
+ private native long _addItemToKeychain(String alias, boolean isCertificate, byte[] datablob, char[] password);
+ private native int _removeItemFromKeychain(long certRef);
+ private native void _releaseKeychainItemRef(long keychainItemRef);
+
+ /**
+ * Loads the keystore from the Keychain.
+ *
+ * @param stream Ignored - here for API compatibility.
+ * @param password Ignored - if user needs to unlock keychain Security
+ * framework will post any dialogs.
+ *
+ * @exception IOException if there is an I/O or format problem with the
+ * keystore data
+ * @exception NoSuchAlgorithmException if the algorithm used to check
+ * the integrity of the keystore cannot be found
+ * @exception CertificateException if any of the certificates in the
+ * keystore could not be loaded
+ */
+ public void engineLoad(InputStream stream, char[] password)
+ throws IOException, NoSuchAlgorithmException, CertificateException
+ {
+ permissionCheck();
+
+ // Release any stray keychain references before clearing out the entries.
+ synchronized(entries) {
+ for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
+ String alias = e.nextElement();
+ Object entry = entries.get(alias);
+ if (entry instanceof TrustedCertEntry) {
+ if (((TrustedCertEntry)entry).certRef != 0) {
+ _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
+ }
+ } else {
+ KeyEntry keyEntry = (KeyEntry)entry;
+
+ if (keyEntry.chain != null) {
+ for (int i = 0; i < keyEntry.chain.length; i++) {
+ if (keyEntry.chainRefs[i] != 0) {
+ _releaseKeychainItemRef(keyEntry.chainRefs[i]);
+ }
+ }
+
+ if (keyEntry.keyRef != 0) {
+ _releaseKeychainItemRef(keyEntry.keyRef);
+ }
+ }
+ }
+ }
+
+ entries.clear();
+ _scanKeychain();
+ }
+ }
+
+ private native void _scanKeychain();
+
+ /**
+ * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called.
+ */
+ private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) {
+ TrustedCertEntry tce = new TrustedCertEntry();
+
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ InputStream input = new ByteArrayInputStream(derStream);
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
+ input.close();
+ tce.cert = cert;
+ tce.certRef = keychainItemRef;
+
+ // Make a creation date.
+ if (creationDate != 0)
+ tce.date = new Date(creationDate);
+ else
+ tce.date = new Date();
+
+ int uniqueVal = 1;
+ String originalAlias = alias;
+
+ while (entries.containsKey(alias.toLowerCase())) {
+ alias = originalAlias + " " + uniqueVal;
+ uniqueVal++;
+ }
+
+ entries.put(alias.toLowerCase(), tce);
+ } catch (Exception e) {
+ // The certificate will be skipped.
+ System.err.println("KeychainStore Ignored Exception: " + e);
+ }
+ }
+
+ /**
+ * Callback method from _scanKeychain. If an identity is found, this method will be called to create Java certificate
+ * and private key objects from the keychain data.
+ */
+ private void createKeyEntry(String alias, long creationDate, long secKeyRef, long[] secCertificateRefs, byte[][] rawCertData)
+ throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
+ KeyEntry ke = new KeyEntry();
+
+ // First, store off the private key information. This is the easy part.
+ ke.protectedPrivKey = null;
+ ke.keyRef = secKeyRef;
+
+ // Make a creation date.
+ if (creationDate != 0)
+ ke.date = new Date(creationDate);
+ else
+ ke.date = new Date();
+
+ // Next, create X.509 Certificate objects from the raw data. This is complicated
+ // because a certificate's public key may be too long for Java's default encryption strength.
+ List<CertKeychainItemPair> createdCerts = new ArrayList<>();
+
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ for (int i = 0; i < rawCertData.length; i++) {
+ try {
+ InputStream input = new ByteArrayInputStream(rawCertData[i]);
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
+ input.close();
+
+ // We successfully created the certificate, so track it and its corresponding SecCertificateRef.
+ createdCerts.add(new CertKeychainItemPair(secCertificateRefs[i], cert));
+ } catch (CertificateException e) {
+ // The certificate will be skipped.
+ System.err.println("KeychainStore Ignored Exception: " + e);
+ }
+ }
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ } catch (IOException ioe) {
+ ioe.printStackTrace(); // How would this happen?
+ }
+
+ // We have our certificates in the List, so now extract them into an array of
+ // Certificates and SecCertificateRefs.
+ CertKeychainItemPair[] objArray = createdCerts.toArray(new CertKeychainItemPair[0]);
+ Certificate[] certArray = new Certificate[objArray.length];
+ long[] certRefArray = new long[objArray.length];
+
+ for (int i = 0; i < objArray.length; i++) {
+ CertKeychainItemPair addedItem = objArray[i];
+ certArray[i] = addedItem.mCert;
+ certRefArray[i] = addedItem.mCertificateRef;
+ }
+
+ ke.chain = certArray;
+ ke.chainRefs = certRefArray;
+
+ // If we don't have already have an item with this item's alias
+ // create a new one for it.
+ int uniqueVal = 1;
+ String originalAlias = alias;
+
+ while (entries.containsKey(alias.toLowerCase())) {
+ alias = originalAlias + " " + uniqueVal;
+ uniqueVal++;
+ }
+
+ entries.put(alias.toLowerCase(), ke);
+ }
+
+ private class CertKeychainItemPair {
+ long mCertificateRef;
+ Certificate mCert;
+
+ CertKeychainItemPair(long inCertRef, Certificate cert) {
+ mCertificateRef = inCertRef;
+ mCert = cert;
+ }
+ }
+
+ /*
+ * Validate Certificate Chain
+ */
+ private boolean validateChain(Certificate[] certChain)
+ {
+ for (int i = 0; i < certChain.length-1; i++) {
+ X500Principal issuerDN =
+ ((X509Certificate)certChain[i]).getIssuerX500Principal();
+ X500Principal subjectDN =
+ ((X509Certificate)certChain[i+1]).getSubjectX500Principal();
+ if (!(issuerDN.equals(subjectDN)))
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ private byte[] fetchPrivateKeyFromBag(byte[] privateKeyInfo) throws IOException, NoSuchAlgorithmException, CertificateException
+ {
+ byte[] returnValue = null;
+ DerValue val = new DerValue(new ByteArrayInputStream(privateKeyInfo));
+ DerInputStream s = val.toDerInputStream();
+ int version = s.getInteger();
+
+ if (version != 3) {
+ throw new IOException("PKCS12 keystore not in version 3 format");
+ }
+
+ /*
+ * Read the authSafe.
+ */
+ byte[] authSafeData;
+ ContentInfo authSafe = new ContentInfo(s);
+ ObjectIdentifier contentType = authSafe.getContentType();
+
+ if (contentType.equals(ContentInfo.DATA_OID)) {
+ authSafeData = authSafe.getData();
+ } else /* signed data */ {
+ throw new IOException("public key protected PKCS12 not supported");
+ }
+
+ DerInputStream as = new DerInputStream(authSafeData);
+ DerValue[] safeContentsArray = as.getSequence(2);
+ int count = safeContentsArray.length;
+
+ /*
+ * Spin over the ContentInfos.
+ */
+ for (int i = 0; i < count; i++) {
+ byte[] safeContentsData;
+ ContentInfo safeContents;
+ DerInputStream sci;
+ byte[] eAlgId = null;
+
+ sci = new DerInputStream(safeContentsArray[i].toByteArray());
+ safeContents = new ContentInfo(sci);
+ contentType = safeContents.getContentType();
+ safeContentsData = null;
+
+ if (contentType.equals(ContentInfo.DATA_OID)) {
+ safeContentsData = safeContents.getData();
+ } else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) {
+ // The password was used to export the private key from the keychain.
+ // The Keychain won't export the key with encrypted data, so we don't need
+ // to worry about it.
+ continue;
+ } else {
+ throw new IOException("public key protected PKCS12" +
+ " not supported");
+ }
+ DerInputStream sc = new DerInputStream(safeContentsData);
+ returnValue = extractKeyData(sc);
+ }
+
+ return returnValue;
+ }
+
+ @SuppressWarnings("deprecation")
+ private byte[] extractKeyData(DerInputStream stream)
+ throws IOException, NoSuchAlgorithmException, CertificateException
+ {
+ byte[] returnValue = null;
+ DerValue[] safeBags = stream.getSequence(2);
+ int count = safeBags.length;
+
+ /*
+ * Spin over the SafeBags.
+ */
+ for (int i = 0; i < count; i++) {
+ ObjectIdentifier bagId;
+ DerInputStream sbi;
+ DerValue bagValue;
+ Object bagItem = null;
+
+ sbi = safeBags[i].toDerInputStream();
+ bagId = sbi.getOID();
+ bagValue = sbi.getDerValue();
+ if (!bagValue.isContextSpecific((byte)0)) {
+ throw new IOException("unsupported PKCS12 bag value type "
+ + bagValue.tag);
+ }
+ bagValue = bagValue.data.getDerValue();
+ if (bagId.equals(PKCS8ShroudedKeyBag_OID)) {
+ // got what we were looking for. Return it.
+ returnValue = bagValue.toByteArray();
+ } else {
+ // log error message for "unsupported PKCS12 bag type"
+ System.out.println("Unsupported bag type '" + bagId + "'");
+ }
+ }
+
+ return returnValue;
+ }
+
+ /*
+ * Generate PBE Algorithm Parameters
+ */
+ private AlgorithmParameters getAlgorithmParameters(String algorithm)
+ throws IOException
+ {
+ AlgorithmParameters algParams = null;
+
+ // create PBE parameters from salt and iteration count
+ PBEParameterSpec paramSpec =
+ new PBEParameterSpec(getSalt(), iterationCount);
+ try {
+ algParams = AlgorithmParameters.getInstance(algorithm);
+ algParams.init(paramSpec);
+ } catch (Exception e) {
+ IOException ioe =
+ new IOException("getAlgorithmParameters failed: " +
+ e.getMessage());
+ ioe.initCause(e);
+ throw ioe;
+ }
+ return algParams;
+ }
+
+ // the source of randomness
+ private SecureRandom random;
+
+ /*
+ * Generate random salt
+ */
+ private byte[] getSalt()
+ {
+ // Generate a random salt.
+ byte[] salt = new byte[SALT_LEN];
+ if (random == null) {
+ random = new SecureRandom();
+ }
+ salt = random.generateSeed(SALT_LEN);
+ return salt;
+ }
+
+ /*
+ * parse Algorithm Parameters
+ */
+ private AlgorithmParameters parseAlgParameters(DerInputStream in)
+ throws IOException
+ {
+ AlgorithmParameters algParams = null;
+ try {
+ DerValue params;
+ if (in.available() == 0) {
+ params = null;
+ } else {
+ params = in.getDerValue();
+ if (params.tag == DerValue.tag_Null) {
+ params = null;
+ }
+ }
+ if (params != null) {
+ algParams = AlgorithmParameters.getInstance("PBE");
+ algParams.init(params.toByteArray());
+ }
+ } catch (Exception e) {
+ IOException ioe =
+ new IOException("parseAlgParameters failed: " +
+ e.getMessage());
+ ioe.initCause(e);
+ throw ioe;
+ }
+ return algParams;
+ }
+
+ /*
+ * Generate PBE key
+ */
+ private SecretKey getPBEKey(char[] password) throws IOException
+ {
+ SecretKey skey = null;
+
+ try {
+ PBEKeySpec keySpec = new PBEKeySpec(password);
+ SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
+ skey = skFac.generateSecret(keySpec);
+ } catch (Exception e) {
+ IOException ioe = new IOException("getSecretKey failed: " +
+ e.getMessage());
+ ioe.initCause(e);
+ throw ioe;
+ }
+ return skey;
+ }
+
+ /*
+ * Encrypt private key using Password-based encryption (PBE)
+ * as defined in PKCS#5.
+ *
+ * NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
+ * used to derive the key and IV.
+ *
+ * @return encrypted private key encoded as EncryptedPrivateKeyInfo
+ */
+ private byte[] encryptPrivateKey(byte[] data, char[] password)
+ throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
+ {
+ byte[] key = null;
+
+ try {
+ // create AlgorithmParameters
+ AlgorithmParameters algParams =
+ getAlgorithmParameters("PBEWithSHA1AndDESede");
+
+ // Use JCE
+ SecretKey skey = getPBEKey(password);
+ Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede");
+ cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
+ byte[] encryptedKey = cipher.doFinal(data);
+
+ // wrap encrypted private key in EncryptedPrivateKeyInfo
+ // as defined in PKCS#8
+ AlgorithmId algid =
+ new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams);
+ EncryptedPrivateKeyInfo encrInfo =
+ new EncryptedPrivateKeyInfo(algid, encryptedKey);
+ key = encrInfo.getEncoded();
+ } catch (Exception e) {
+ UnrecoverableKeyException uke =
+ new UnrecoverableKeyException("Encrypt Private Key failed: "
+ + e.getMessage());
+ uke.initCause(e);
+ throw uke;
+ }
+
+ return key;
+ }
+
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m Tue Aug 18 21:16:27 2015 +0000
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+#import "apple_security_KeychainStore.h"
+
+#import <Security/Security.h>
+#import <Security/SecImportExport.h>
+#import <CoreServices/CoreServices.h> // (for require() macros)
+#import <JavaNativeFoundation/JavaNativeFoundation.h>
+
+
+static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore");
+static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V");
+static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V");
+
+static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem)
+{
+ OSStatus status;
+ jstring returnValue = NULL;
+ char *attribCString = NULL;
+
+ SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } };
+ SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
+
+ status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
+
+ if(status) {
+ cssmPerror("getLabelFromItem: SecKeychainItemCopyContent", status);
+ goto errOut;
+ }
+
+ attribCString = malloc(itemAttrs[0].length + 1);
+ strncpy(attribCString, itemAttrs[0].data, itemAttrs[0].length);
+ attribCString[itemAttrs[0].length] = '\0';
+ returnValue = (*env)->NewStringUTF(env, attribCString);
+
+errOut:
+ SecKeychainItemFreeContent(&attrList, NULL);
+ if (attribCString) free(attribCString);
+ return returnValue;
+}
+
+static jlong getModDateFromItem(JNIEnv *env, SecKeychainItemRef inItem)
+{
+ OSStatus status;
+ SecKeychainAttribute itemAttrs[] = { { kSecModDateItemAttr, 0, NULL } };
+ SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
+ jlong returnValue = 0;
+
+ status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
+
+ if(status) {
+ // This is almost always missing, so don't dump an error.
+ // cssmPerror("getModDateFromItem: SecKeychainItemCopyContent", status);
+ goto errOut;
+ }
+
+ memcpy(&returnValue, itemAttrs[0].data, itemAttrs[0].length);
+
+errOut:
+ SecKeychainItemFreeContent(&attrList, NULL);
+ return returnValue;
+}
+
+static void setLabelForItem(NSString *inLabel, SecKeychainItemRef inItem)
+{
+ OSStatus status;
+ const char *labelCString = [inLabel UTF8String];
+
+ // Set up attribute vector (each attribute consists of {tag, length, pointer}):
+ SecKeychainAttribute attrs[] = {
+ { kSecLabelItemAttr, strlen(labelCString), (void *)labelCString }
+ };
+
+ const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
+
+ // Not changing data here, just attributes.
+ status = SecKeychainItemModifyContent(inItem, &attributes, 0, NULL);
+
+ if(status) {
+ cssmPerror("setLabelForItem: SecKeychainItemModifyContent", status);
+ }
+}
+
+/*
+ * Given a SecIdentityRef, do our best to construct a complete, ordered, and
+ * verified cert chain, returning the result in a CFArrayRef. The result is
+ * can be passed back to Java as a chain for a private key.
+ */
+static OSStatus completeCertChain(
+ SecIdentityRef identity,
+ SecCertificateRef trustedAnchor, // optional additional trusted anchor
+ bool includeRoot, // include the root in outArray
+ CFArrayRef *outArray) // created and RETURNED
+{
+ SecTrustRef secTrust = NULL;
+ SecPolicyRef policy = NULL;
+ SecPolicySearchRef policySearch = NULL;
+ SecTrustResultType secTrustResult;
+ CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used
+ CFArrayRef certChain = NULL; // constructed chain, CERTS ONLY
+ CFMutableArrayRef subjCerts; // passed to SecTrust
+ CFMutableArrayRef certArray; // returned array starting with
+ // identity
+ CFIndex numResCerts;
+ CFIndex dex;
+ OSStatus ortn;
+ SecCertificateRef certRef;
+
+ /* First element in out array is the SecIdentity */
+ certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(certArray, identity);
+
+ /* the single element in certs-to-be-evaluated comes from the identity */
+ ortn = SecIdentityCopyCertificate(identity, &certRef);
+ if(ortn) {
+ /* should never happen */
+ cssmPerror("SecIdentityCopyCertificate", ortn);
+ return ortn;
+ }
+
+ /*
+ * Now use SecTrust to get a complete cert chain, using all of the
+ * user's keychains to look for intermediate certs.
+ * NOTE this does NOT handle root certs which are not in the system
+ * root cert DB.
+ */
+ subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
+ CFArraySetValueAtIndex(subjCerts, 0, certRef);
+
+ /* the array owns the subject cert ref now */
+ CFRelease(certRef);
+
+ /* Get a SecPolicyRef for generic X509 cert chain verification */
+ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
+ &CSSMOID_APPLE_X509_BASIC,
+ NULL, // value
+ &policySearch);
+ if(ortn) {
+ /* should never happen */
+ cssmPerror("SecPolicySearchCreate", ortn);
+ goto errOut;
+ }
+ ortn = SecPolicySearchCopyNext(policySearch, &policy);
+ if(ortn) {
+ /* should never happen */
+ cssmPerror("SecPolicySearchCopyNext", ortn);
+ goto errOut;
+ }
+
+ /* build a SecTrustRef for specified policy and certs */
+ ortn = SecTrustCreateWithCertificates(subjCerts,
+ policy, &secTrust);
+ if(ortn) {
+ cssmPerror("SecTrustCreateWithCertificates", ortn);
+ goto errOut;
+ }
+
+ if(trustedAnchor) {
+ /*
+ * Tell SecTrust to trust this one in addition to the current
+ * trusted system-wide anchors.
+ */
+ CFMutableArrayRef newAnchors;
+ CFArrayRef currAnchors;
+
+ ortn = SecTrustCopyAnchorCertificates(&currAnchors);
+ if(ortn) {
+ /* should never happen */
+ cssmPerror("SecTrustCopyAnchorCertificates", ortn);
+ goto errOut;
+ }
+ newAnchors = CFArrayCreateMutableCopy(NULL,
+ CFArrayGetCount(currAnchors) + 1,
+ currAnchors);
+ CFRelease(currAnchors);
+ CFArrayAppendValue(newAnchors, trustedAnchor);
+ ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);
+ CFRelease(newAnchors);
+ if(ortn) {
+ cssmPerror("SecTrustSetAnchorCertificates", ortn);
+ goto errOut;
+ }
+ }
+
+ /* evaluate: GO */
+ ortn = SecTrustEvaluate(secTrust, &secTrustResult);
+ if(ortn) {
+ cssmPerror("SecTrustEvaluate", ortn);
+ goto errOut;
+ }
+ switch(secTrustResult) {
+ case kSecTrustResultUnspecified:
+ /* cert chain valid, no special UserTrust assignments; drop thru */
+ case kSecTrustResultProceed:
+ /* cert chain valid AND user explicitly trusts this */
+ break;
+ default:
+ /*
+ * Cert chain construction failed.
+ * Just go with the single subject cert we were given; maybe the
+ * peer can complete the chain.
+ */
+ ortn = noErr;
+ goto errOut;
+ }
+
+ /* get resulting constructed cert chain */
+ ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
+ if(ortn) {
+ cssmPerror("SecTrustEvaluate", ortn);
+ goto errOut;
+ }
+
+ /*
+ * Copy certs from constructed chain to our result array, skipping
+ * the leaf (which is already there, as a SecIdentityRef) and possibly
+ * a root.
+ */
+ numResCerts = CFArrayGetCount(certChain);
+ if(numResCerts < 1) {
+ /*
+ * Can't happen: If chain doesn't verify to a root, we'd
+ * have bailed after SecTrustEvaluate().
+ */
+ ortn = noErr;
+ goto errOut;
+ }
+ if(!includeRoot) {
+ /* skip the last (root) cert) */
+ numResCerts--;
+ }
+ for(dex=1; dex<numResCerts; dex++) {
+ certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
+ CFArrayAppendValue(certArray, certRef);
+ }
+errOut:
+ /* clean up */
+ if(secTrust) {
+ CFRelease(secTrust);
+ }
+ if(subjCerts) {
+ CFRelease(subjCerts);
+ }
+ if(policy) {
+ CFRelease(policy);
+ }
+ if(policySearch) {
+ CFRelease(policySearch);
+ }
+ *outArray = certArray;
+ return ortn;
+}
+
+static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore)
+{
+ // Search the user keychain list for all identities. Identities are a certificate/private key association that
+ // can be chosen for a purpose such as signing or an SSL connection.
+ SecIdentitySearchRef identitySearch = NULL;
+ // Pass 0 if you want all identities returned by this search
+ OSStatus err = SecIdentitySearchCreate(NULL, 0, &identitySearch);
+ SecIdentityRef theIdentity = NULL;
+ OSErr searchResult = noErr;
+
+ do {
+ searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity);
+
+ if (searchResult == noErr) {
+ // Get the cert from the identity, then generate a chain.
+ SecCertificateRef certificate;
+ SecIdentityCopyCertificate(theIdentity, &certificate);
+ CFArrayRef certChain = NULL;
+
+ // *** Should do something with this error...
+ err = completeCertChain(theIdentity, NULL, TRUE, &certChain);
+
+ CFIndex i, certCount = CFArrayGetCount(certChain);
+
+ // Make a java array of certificate data from the chain.
+ jclass byteArrayClass = (*env)->FindClass(env, "[B");
+ if (byteArrayClass == NULL) {
+ goto errOut;
+ }
+ jobjectArray javaCertArray = (*env)->NewObjectArray(env, certCount, byteArrayClass, NULL);
+ // Cleanup first then check for a NULL return code
+ (*env)->DeleteLocalRef(env, byteArrayClass);
+ if (javaCertArray == NULL) {
+ goto errOut;
+ }
+
+ // And, make an array of the certificate refs.
+ jlongArray certRefArray = (*env)->NewLongArray(env, certCount);
+ if (certRefArray == NULL) {
+ goto errOut;
+ }
+
+ SecCertificateRef currCertRef = NULL;
+
+ for (i = 0; i < certCount; i++) {
+ CSSM_DATA currCertData;
+
+ if (i == 0)
+ currCertRef = certificate;
+ else
+ currCertRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
+
+ bzero(&currCertData, sizeof(CSSM_DATA));
+ err = SecCertificateGetData(currCertRef, &currCertData);
+ jbyteArray encodedCertData = (*env)->NewByteArray(env, currCertData.Length);
+ if (encodedCertData == NULL) {
+ goto errOut;
+ }
+ (*env)->SetByteArrayRegion(env, encodedCertData, 0, currCertData.Length, (jbyte *)currCertData.Data);
+ (*env)->SetObjectArrayElement(env, javaCertArray, i, encodedCertData);
+ jlong certRefElement = ptr_to_jlong(currCertRef);
+ (*env)->SetLongArrayRegion(env, certRefArray, i, 1, &certRefElement);
+ }
+
+ // Get the private key. When needed we'll export the data from it later.
+ SecKeyRef privateKeyRef;
+ err = SecIdentityCopyPrivateKey(theIdentity, &privateKeyRef);
+
+ // Find the label. It's a 'blob', but we interpret as characters.
+ jstring alias = getLabelFromItem(env, (SecKeychainItemRef)certificate);
+ if (alias == NULL) {
+ goto errOut;
+ }
+
+ // Find the creation date.
+ jlong creationDate = getModDateFromItem(env, (SecKeychainItemRef)certificate);
+
+ // Call back to the Java object to create Java objects corresponding to this security object.
+ jlong nativeKeyRef = ptr_to_jlong(privateKeyRef);
+ JNFCallVoidMethod(env, keyStore, jm_createKeyEntry, alias, creationDate, nativeKeyRef, certRefArray, javaCertArray);
+ }
+ } while (searchResult == noErr);
+
+errOut:
+ if (identitySearch != NULL) {
+ CFRelease(identitySearch);
+ }
+}
+
+static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
+{
+ // Search the user keychain list for all X509 certificates.
+ SecKeychainSearchRef keychainItemSearch = NULL;
+ OSStatus err = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &keychainItemSearch);
+ SecKeychainItemRef theItem = NULL;
+ OSErr searchResult = noErr;
+
+ do {
+ searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem);
+
+ if (searchResult == noErr) {
+ // Make a byte array with the DER-encoded contents of the certificate.
+ SecCertificateRef certRef = (SecCertificateRef)theItem;
+ CSSM_DATA currCertificate;
+ err = SecCertificateGetData(certRef, &currCertificate);
+ jbyteArray certData = (*env)->NewByteArray(env, currCertificate.Length);
+ if (certData == NULL) {
+ goto errOut;
+ }
+ (*env)->SetByteArrayRegion(env, certData, 0, currCertificate.Length, (jbyte *)currCertificate.Data);
+
+ // Find the label. It's a 'blob', but we interpret as characters.
+ jstring alias = getLabelFromItem(env, theItem);
+ if (alias == NULL) {
+ goto errOut;
+ }
+
+ // Find the creation date.
+ jlong creationDate = getModDateFromItem(env, theItem);
+
+ // Call back to the Java object to create Java objects corresponding to this security object.
+ jlong nativeRef = ptr_to_jlong(certRef);
+ JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData);
+ }
+ } while (searchResult == noErr);
+
+errOut:
+ if (keychainItemSearch != NULL) {
+ CFRelease(keychainItemSearch);
+ }
+}
+
+/*
+ * Class: apple_security_KeychainStore
+ * Method: _getEncodedKeyData
+ * Signature: (J)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_apple_security_KeychainStore__1getEncodedKeyData
+(JNIEnv *env, jobject this, jlong keyRefLong, jcharArray passwordObj)
+{
+ SecKeyRef keyRef = (SecKeyRef)jlong_to_ptr(keyRefLong);
+ SecKeyImportExportParameters paramBlock;
+ OSStatus err = noErr;
+ CFDataRef exportedData = NULL;
+ jbyteArray returnValue = NULL;
+ CFStringRef passwordStrRef = NULL;
+
+ jsize passwordLen = 0;
+ jchar *passwordChars = NULL;
+
+ if (passwordObj) {
+ passwordLen = (*env)->GetArrayLength(env, passwordObj);
+
+ if (passwordLen > 0) {
+ passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
+ if (passwordChars == NULL) {
+ goto errOut;
+ }
+ passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen);
+ }
+ }
+
+ paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.
+ paramBlock.flags = 0;
+ paramBlock.passphrase = passwordStrRef;
+ paramBlock.alertTitle = NULL;
+ paramBlock.alertPrompt = NULL;
+ paramBlock.accessRef = NULL;
+ paramBlock.keyUsage = CSSM_KEYUSE_ANY;
+ paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
+
+ err = SecKeychainItemExport(keyRef, kSecFormatPKCS12, 0, ¶mBlock, &exportedData);
+
+ if (err == noErr) {
+ CFIndex size = CFDataGetLength(exportedData);
+ returnValue = (*env)->NewByteArray(env, size);
+ if (returnValue == NULL) {
+ goto errOut;
+ }
+ (*env)->SetByteArrayRegion(env, returnValue, 0, size, (jbyte *)CFDataGetBytePtr(exportedData));
+ }
+
+errOut:
+ if (exportedData) CFRelease(exportedData);
+ if (passwordStrRef) CFRelease(passwordStrRef);
+
+ return returnValue;
+}
+
+
+/*
+ * Class: apple_security_KeychainStore
+ * Method: _scanKeychain
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1scanKeychain
+(JNIEnv *env, jobject this)
+{
+ // Look for 'identities' -- private key and certificate chain pairs -- and add those.
+ // Search for these first, because a certificate that's found here as part of an identity will show up
+ // again later as a certificate.
+ addIdentitiesToKeystore(env, this);
+
+ // Scan current keychain for trusted certificates.
+ addCertificatesToKeystore(env, this);
+
+}
+
+/*
+ * Class: apple_security_KeychainStore
+ * Method: _addItemToKeychain
+ * Signature: (Ljava/lang/String;[B)I
+*/
+JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain
+(JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj)
+{
+ OSStatus err;
+ jlong returnValue = 0;
+
+JNF_COCOA_ENTER(env);
+
+ jsize dataSize = (*env)->GetArrayLength(env, rawDataObj);
+ jbyte *rawData = (*env)->GetByteArrayElements(env, rawDataObj, NULL);
+ if (rawData == NULL) {
+ goto errOut;
+ }
+
+ CFDataRef cfDataToImport = CFDataCreate(kCFAllocatorDefault, (UInt8 *)rawData, dataSize);
+ CFArrayRef createdItems = NULL;
+
+ SecKeychainRef defaultKeychain = NULL;
+ SecKeychainCopyDefault(&defaultKeychain);
+
+ SecExternalItemType dataType = (isCertificate == JNI_TRUE ? kSecFormatX509Cert : kSecFormatWrappedPKCS8);
+
+ // Convert the password obj into a CFStringRef that the keychain importer can use for encryption.
+ SecKeyImportExportParameters paramBlock;
+ CFStringRef passwordStrRef = NULL;
+
+ jsize passwordLen = 0;
+ jchar *passwordChars = NULL;
+
+ if (passwordObj) {
+ passwordLen = (*env)->GetArrayLength(env, passwordObj);
+ passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
+ passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen);
+ }
+
+ paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.
+ paramBlock.flags = 0;
+ paramBlock.passphrase = passwordStrRef;
+ paramBlock.alertTitle = NULL;
+ paramBlock.alertPrompt = NULL;
+ paramBlock.accessRef = NULL;
+ paramBlock.keyUsage = CSSM_KEYUSE_ANY;
+ paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
+
+ err = SecKeychainItemImport(cfDataToImport, NULL, &dataType, NULL,
+ 0, ¶mBlock, defaultKeychain, &createdItems);
+
+ if (err == noErr) {
+ SecKeychainItemRef anItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(createdItems, 0);
+
+ // Don't bother labeling keys. They become part of an identity, and are not an accessible part of the keychain.
+ if (CFGetTypeID(anItem) == SecCertificateGetTypeID()) {
+ setLabelForItem(JNFJavaToNSString(env, alias), anItem);
+ }
+
+ // Retain the item, since it will be released once when the array holding it gets released.
+ CFRetain(anItem);
+ returnValue = ptr_to_jlong(anItem);
+ } else {
+ cssmPerror("_addItemToKeychain: SecKeychainItemImport", err);
+ }
+
+ (*env)->ReleaseByteArrayElements(env, rawDataObj, rawData, JNI_ABORT);
+
+ if (createdItems != NULL) {
+ CFRelease(createdItems);
+ }
+
+errOut: ;
+
+JNF_COCOA_EXIT(env);
+
+ return returnValue;
+}
+
+/*
+ * Class: apple_security_KeychainStore
+ * Method: _removeItemFromKeychain
+ * Signature: (J)I
+*/
+JNIEXPORT jint JNICALL Java_apple_security_KeychainStore__1removeItemFromKeychain
+(JNIEnv *env, jobject this, jlong keychainItem)
+{
+ SecKeychainItemRef itemToRemove = jlong_to_ptr(keychainItem);
+ return SecKeychainItemDelete(itemToRemove);
+}
+
+/*
+ * Class: apple_security_KeychainStore
+ * Method: _releaseKeychainItemRef
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1releaseKeychainItemRef
+(JNIEnv *env, jobject this, jlong keychainItem)
+{
+ SecKeychainItemRef itemToFree = jlong_to_ptr(keychainItem);
+ CFRelease(itemToFree);
+}
--- a/jdk/src/java.base/share/classes/sun/security/jca/ProviderConfig.java Tue Aug 18 12:49:00 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/jca/ProviderConfig.java Tue Aug 18 21:16:27 2015 +0000
@@ -178,6 +178,26 @@
p = new com.sun.crypto.provider.SunJCE();
} else if (provName.equals("SunJSSE") || provName.equals("com.sun.net.ssl.internal.ssl.Provider")) {
p = new com.sun.net.ssl.internal.ssl.Provider();
+ } else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) {
+ // need to use reflection since this class only exists on MacOsx
+ p = AccessController.doPrivileged(new PrivilegedAction<Provider>() {
+ public Provider run() {
+ try {
+ Class<?> c = Class.forName("apple.security.AppleProvider");
+ if (Provider.class.isAssignableFrom(c)) {
+ return (Provider) c.newInstance();
+ } else {
+ return null;
+ }
+ } catch (Exception ex) {
+ if (debug != null) {
+ debug.println("Error loading provider Apple");
+ ex.printStackTrace();
+ }
+ return null;
+ }
+ }
+ });
} else {
if (isLoading) {
// because this method is synchronized, this can only
--- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/security/AppleProvider.java Tue Aug 18 12:49:00 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2011, 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 apple.security;
-
-import java.security.*;
-
-/**
- * The Apple Security Provider.
- */
-
-/**
- * Defines the Apple provider.
- *
- * This provider only exists to provide access to the Apple keychain-based KeyStore implementation
- */
-@SuppressWarnings("serial") // JDK implementation class
-public final class AppleProvider extends Provider {
-
- private static final String info = "Apple Provider";
-
- private static final class ProviderService extends Provider.Service {
- ProviderService(Provider p, String type, String algo, String cn) {
- super(p, type, algo, cn, null, null);
- }
-
- @Override
- public Object newInstance(Object ctrParamObj)
- throws NoSuchAlgorithmException {
- String type = getType();
- if (ctrParamObj != null) {
- throw new InvalidParameterException
- ("constructorParameter not used with " + type + " engines");
- }
-
- String algo = getAlgorithm();
- try {
- if (type.equals("KeyStore")) {
- if (algo.equals("KeychainStore")) {
- return new KeychainStore();
- }
- }
- } catch (Exception ex) {
- throw new NoSuchAlgorithmException("Error constructing " +
- type + " for " + algo + " using Apple", ex);
- }
- throw new ProviderException("No impl for " + algo +
- " " + type);
- }
- }
-
-
- public AppleProvider() {
- /* We are the Apple provider */
- super("Apple", 1.9d, info);
-
- final Provider p = this;
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- putService(new ProviderService(p, "KeyStore",
- "KeychainStore", "apple.security.KeychainStore"));
- return null;
- }
- });
- }
-}
--- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/security/KeychainStore.java Tue Aug 18 12:49:00 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1149 +0,0 @@
-/*
- * Copyright (c) 2011, 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 apple.security;
-
-import java.io.*;
-import java.security.*;
-import java.security.cert.*;
-import java.security.cert.Certificate;
-import java.security.spec.*;
-import java.util.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
-import javax.security.auth.x500.*;
-
-import sun.security.pkcs.*;
-import sun.security.pkcs.EncryptedPrivateKeyInfo;
-import sun.security.util.*;
-import sun.security.x509.*;
-
-/**
- * This class provides the keystore implementation referred to as "KeychainStore".
- * It uses the current user's keychain as its backing storage, and does NOT support
- * a file-based implementation.
- */
-
-public final class KeychainStore extends KeyStoreSpi {
-
- // Private keys and their supporting certificate chains
- // If a key came from the keychain it has a SecKeyRef and one or more
- // SecCertificateRef. When we delete the key we have to delete all of the corresponding
- // native objects.
- class KeyEntry {
- Date date; // the creation date of this entry
- byte[] protectedPrivKey;
- char[] password;
- long keyRef; // SecKeyRef for this key
- Certificate chain[];
- long chainRefs[]; // SecCertificateRefs for this key's chain.
- };
-
- // Trusted certificates
- class TrustedCertEntry {
- Date date; // the creation date of this entry
-
- Certificate cert;
- long certRef; // SecCertificateRef for this key
- };
-
- /**
- * Entries that have been deleted. When something calls engineStore we'll
- * remove them from the keychain.
- */
- private Hashtable<String, Object> deletedEntries = new Hashtable<>();
-
- /**
- * Entries that have been added. When something calls engineStore we'll
- * add them to the keychain.
- */
- private Hashtable<String, Object> addedEntries = new Hashtable<>();
-
- /**
- * Private keys and certificates are stored in a hashtable.
- * Hash entries are keyed by alias names.
- */
- private Hashtable<String, Object> entries = new Hashtable<>();
-
- /**
- * Algorithm identifiers and corresponding OIDs for the contents of the PKCS12 bag we get from the Keychain.
- */
- private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
- private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3};
- private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
- private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
-
- /**
- * Constnats used in PBE decryption.
- */
- private static final int iterationCount = 1024;
- private static final int SALT_LEN = 20;
-
- static {
- AccessController.doPrivileged(
- new PrivilegedAction<Void>() {
- public Void run() {
- System.loadLibrary("osx");
- return null;
- }
- });
- try {
- PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
- pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
- } catch (IOException ioe) {
- // should not happen
- }
- }
-
- private static void permissionCheck() {
- SecurityManager sec = System.getSecurityManager();
-
- if (sec != null) {
- sec.checkPermission(new RuntimePermission("useKeychainStore"));
- }
- }
-
-
- /**
- * Verify the Apple provider in the constructor.
- *
- * @exception SecurityException if fails to verify
- * its own integrity
- */
- public KeychainStore() { }
-
- /**
- * Returns the key associated with the given alias, using the given
- * password to recover it.
- *
- * @param alias the alias name
- * @param password the password for recovering the key. This password is
- * used internally as the key is exported in a PKCS12 format.
- *
- * @return the requested key, or null if the given alias does not exist
- * or does not identify a <i>key entry</i>.
- *
- * @exception NoSuchAlgorithmException if the algorithm for recovering the
- * key cannot be found
- * @exception UnrecoverableKeyException if the key cannot be recovered
- * (e.g., the given password is wrong).
- */
- public Key engineGetKey(String alias, char[] password)
- throws NoSuchAlgorithmException, UnrecoverableKeyException
- {
- permissionCheck();
-
- // An empty password is rejected by MacOS API, no private key data
- // is exported. If no password is passed (as is the case when
- // this implementation is used as browser keystore in various
- // deployment scenarios like Webstart, JFX and applets), create
- // a dummy password so MacOS API is happy.
- if (password == null || password.length == 0) {
- // Must not be a char array with only a 0, as this is an empty
- // string.
- if (random == null) {
- random = new SecureRandom();
- }
- password = Long.toString(random.nextLong()).toCharArray();
- }
-
- Object entry = entries.get(alias.toLowerCase());
-
- if (entry == null || !(entry instanceof KeyEntry)) {
- return null;
- }
-
- // This call gives us a PKCS12 bag, with the key inside it.
- byte[] exportedKeyInfo = _getEncodedKeyData(((KeyEntry)entry).keyRef, password);
- if (exportedKeyInfo == null) {
- return null;
- }
-
- PrivateKey returnValue = null;
-
- try {
- byte[] pkcs8KeyData = fetchPrivateKeyFromBag(exportedKeyInfo);
- byte[] encryptedKey;
- AlgorithmParameters algParams;
- ObjectIdentifier algOid;
- try {
- // get the encrypted private key
- EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(pkcs8KeyData);
- encryptedKey = encrInfo.getEncryptedData();
-
- // parse Algorithm parameters
- DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
- DerInputStream in = val.toDerInputStream();
- algOid = in.getOID();
- algParams = parseAlgParameters(in);
-
- } catch (IOException ioe) {
- UnrecoverableKeyException uke =
- new UnrecoverableKeyException("Private key not stored as "
- + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
- uke.initCause(ioe);
- throw uke;
- }
-
- // Use JCE to decrypt the data using the supplied password.
- SecretKey skey = getPBEKey(password);
- Cipher cipher = Cipher.getInstance(algOid.toString());
- cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
- byte[] decryptedPrivateKey = cipher.doFinal(encryptedKey);
- PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(decryptedPrivateKey);
-
- // Parse the key algorithm and then use a JCA key factory to create the private key.
- DerValue val = new DerValue(decryptedPrivateKey);
- DerInputStream in = val.toDerInputStream();
-
- // Ignore this -- version should be 0.
- int i = in.getInteger();
-
- // Get the Algorithm ID next
- DerValue[] value = in.getSequence(2);
- AlgorithmId algId = new AlgorithmId(value[0].getOID());
- String algName = algId.getName();
-
- // Get a key factory for this algorithm. It's likely to be 'RSA'.
- KeyFactory kfac = KeyFactory.getInstance(algName);
- returnValue = kfac.generatePrivate(kspec);
- } catch (Exception e) {
- UnrecoverableKeyException uke =
- new UnrecoverableKeyException("Get Key failed: " +
- e.getMessage());
- uke.initCause(e);
- throw uke;
- }
-
- return returnValue;
- }
-
- private native byte[] _getEncodedKeyData(long secKeyRef, char[] password);
-
- /**
- * Returns the certificate chain associated with the given alias.
- *
- * @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 (i.e., the given
- * alias identifies either a <i>trusted certificate entry</i> or a
- * <i>key entry</i> without a certificate chain).
- */
- public Certificate[] engineGetCertificateChain(String alias) {
- permissionCheck();
-
- Object entry = entries.get(alias.toLowerCase());
-
- if (entry != null && entry instanceof KeyEntry) {
- if (((KeyEntry)entry).chain == null) {
- return null;
- } else {
- return ((KeyEntry)entry).chain.clone();
- }
- } else {
- return null;
- }
- }
-
- /**
- * Returns the certificate associated with the given alias.
- *
- * <p>If the given alias name identifies a
- * <i>trusted certificate entry</i>, the certificate associated with that
- * entry is returned. If the given alias name identifies a
- * <i>key entry</i>, the first element of the certificate chain of that
- * entry is returned, or null if that entry does not have a certificate
- * chain.
- *
- * @param alias the alias name
- *
- * @return the certificate, or null if the given alias does not exist or
- * does not contain a certificate.
- */
- public Certificate engineGetCertificate(String alias) {
- permissionCheck();
-
- Object entry = entries.get(alias.toLowerCase());
-
- if (entry != null) {
- if (entry instanceof TrustedCertEntry) {
- return ((TrustedCertEntry)entry).cert;
- } else {
- KeyEntry ke = (KeyEntry)entry;
- if (ke.chain == null || ke.chain.length == 0) {
- return null;
- }
- return ke.chain[0];
- }
- } else {
- return null;
- }
- }
-
- /**
- * 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) {
- permissionCheck();
-
- Object entry = entries.get(alias.toLowerCase());
-
- if (entry != null) {
- if (entry instanceof TrustedCertEntry) {
- return new Date(((TrustedCertEntry)entry).date.getTime());
- } else {
- return new Date(((KeyEntry)entry).date.getTime());
- }
- } else {
- return null;
- }
- }
-
- /**
- * 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 void engineSetKeyEntry(String alias, Key key, char[] password,
- Certificate[] chain)
- throws KeyStoreException
- {
- permissionCheck();
-
- synchronized(entries) {
- try {
- KeyEntry entry = new KeyEntry();
- entry.date = new Date();
-
- if (key instanceof PrivateKey) {
- if ((key.getFormat().equals("PKCS#8")) ||
- (key.getFormat().equals("PKCS8"))) {
- entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password);
- entry.password = password.clone();
- } else {
- throw new KeyStoreException("Private key is not encoded as PKCS#8");
- }
- } else {
- throw new KeyStoreException("Key is not a PrivateKey");
- }
-
- // clone the chain
- if (chain != null) {
- if ((chain.length > 1) && !validateChain(chain)) {
- throw new KeyStoreException("Certificate chain does not validate");
- }
-
- entry.chain = chain.clone();
- entry.chainRefs = new long[entry.chain.length];
- }
-
- String lowerAlias = alias.toLowerCase();
- if (entries.get(lowerAlias) != null) {
- deletedEntries.put(lowerAlias, entries.get(lowerAlias));
- }
-
- entries.put(lowerAlias, entry);
- addedEntries.put(lowerAlias, entry);
- } catch (Exception nsae) {
- KeyStoreException ke = new KeyStoreException("Key protection algorithm not found: " + nsae);
- ke.initCause(nsae);
- throw ke;
- }
- }
- }
-
- /**
- * 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. If the
- * underlying keystore implementation is of type <code>jks</code>,
- * <code>key</code> must be encoded as an
- * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
- *
- * <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
- {
- permissionCheck();
-
- synchronized(entries) {
- // key must be encoded as EncryptedPrivateKeyInfo as defined in
- // PKCS#8
- KeyEntry entry = new KeyEntry();
- try {
- EncryptedPrivateKeyInfo privateKey = new EncryptedPrivateKeyInfo(key);
- entry.protectedPrivKey = privateKey.getEncoded();
- } catch (IOException ioe) {
- throw new KeyStoreException("key is not encoded as "
- + "EncryptedPrivateKeyInfo");
- }
-
- entry.date = new Date();
-
- if ((chain != null) &&
- (chain.length != 0)) {
- entry.chain = chain.clone();
- entry.chainRefs = new long[entry.chain.length];
- }
-
- String lowerAlias = alias.toLowerCase();
- if (entries.get(lowerAlias) != null) {
- deletedEntries.put(lowerAlias, entries.get(alias));
- }
- entries.put(lowerAlias, entry);
- addedEntries.put(lowerAlias, entry);
- }
- }
-
- /**
- * Assigns the given certificate to the given alias.
- *
- * <p>If the given alias already exists in this keystore and identifies a
- * <i>trusted certificate entry</i>, the certificate associated with it 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 a <i>trusted certificate entry</i>, or this operation
- * fails for some other reason.
- */
- public void engineSetCertificateEntry(String alias, Certificate cert)
- throws KeyStoreException
- {
- permissionCheck();
-
- synchronized(entries) {
-
- Object entry = entries.get(alias.toLowerCase());
- if ((entry != null) && (entry instanceof KeyEntry)) {
- throw new KeyStoreException
- ("Cannot overwrite key entry with certificate");
- }
-
- // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry.
- // Security framework doesn't support the same certificate twice in a keychain.
- Collection<Object> allValues = entries.values();
-
- for (Object value : allValues) {
- if (value instanceof TrustedCertEntry) {
- TrustedCertEntry tce = (TrustedCertEntry)value;
- if (tce.cert.equals(cert)) {
- throw new KeyStoreException("Keychain does not support mulitple copies of same certificate.");
- }
- }
- }
-
- TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
- trustedCertEntry.cert = cert;
- trustedCertEntry.date = new Date();
- String lowerAlias = alias.toLowerCase();
- if (entries.get(lowerAlias) != null) {
- deletedEntries.put(lowerAlias, entries.get(lowerAlias));
- }
- entries.put(lowerAlias, trustedCertEntry);
- addedEntries.put(lowerAlias, trustedCertEntry);
- }
- }
-
- /**
- * 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 void engineDeleteEntry(String alias)
- throws KeyStoreException
- {
- permissionCheck();
-
- synchronized(entries) {
- Object entry = entries.remove(alias.toLowerCase());
- deletedEntries.put(alias.toLowerCase(), entry);
- }
- }
-
- /**
- * Lists all the alias names of this keystore.
- *
- * @return enumeration of the alias names
- */
- public Enumeration<String> engineAliases() {
- permissionCheck();
- return entries.keys();
- }
-
- /**
- * Checks if the given alias exists in this keystore.
- *
- * @param alias the alias name
- *
- * @return true if the alias exists, false otherwise
- */
- public boolean engineContainsAlias(String alias) {
- permissionCheck();
- return entries.containsKey(alias.toLowerCase());
- }
-
- /**
- * Retrieves the number of entries in this keystore.
- *
- * @return the number of entries in this keystore
- */
- public int engineSize() {
- permissionCheck();
- return entries.size();
- }
-
- /**
- * Returns true if the entry identified by the given alias is a
- * <i>key entry</i>, and false otherwise.
- *
- * @return true if the entry identified by the given alias is a
- * <i>key entry</i>, false otherwise.
- */
- public boolean engineIsKeyEntry(String alias) {
- permissionCheck();
- Object entry = entries.get(alias.toLowerCase());
- if ((entry != null) && (entry instanceof KeyEntry)) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Returns true if the entry identified by the given alias is a
- * <i>trusted certificate entry</i>, and false otherwise.
- *
- * @return true if the entry identified by the given alias is a
- * <i>trusted certificate entry</i>, false otherwise.
- */
- public boolean engineIsCertificateEntry(String alias) {
- permissionCheck();
- Object entry = entries.get(alias.toLowerCase());
- if ((entry != null) && (entry instanceof TrustedCertEntry)) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * 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
- * is a <i>trusted certificate entry</i>, the given certificate is
- * compared to that entry's certificate. If the entry being considered is
- * a <i>key entry</i>, the given certificate is compared to the first
- * element of that entry's certificate chain (if a chain exists).
- *
- * @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 String engineGetCertificateAlias(Certificate cert) {
- permissionCheck();
- Certificate certElem;
-
- for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
- String alias = e.nextElement();
- Object entry = entries.get(alias);
- if (entry instanceof TrustedCertEntry) {
- certElem = ((TrustedCertEntry)entry).cert;
- } else {
- KeyEntry ke = (KeyEntry)entry;
- if (ke.chain == null || ke.chain.length == 0) {
- continue;
- }
- certElem = ke.chain[0];
- }
- if (certElem.equals(cert)) {
- return alias;
- }
- }
- return null;
- }
-
- /**
- * Stores this keystore to the given output stream, and protects its
- * integrity with the given password.
- *
- * @param stream Ignored. the output stream to which this keystore is written.
- * @param password the password to generate the keystore integrity check
- *
- * @exception IOException if there was an I/O problem with data
- * @exception NoSuchAlgorithmException if the appropriate data integrity
- * algorithm could not be found
- * @exception CertificateException if any of the certificates included in
- * the keystore data could not be stored
- */
- public void engineStore(OutputStream stream, char[] password)
- throws IOException, NoSuchAlgorithmException, CertificateException
- {
- permissionCheck();
-
- // Delete items that do have a keychain item ref.
- for (Enumeration<String> e = deletedEntries.keys(); e.hasMoreElements(); ) {
- String alias = e.nextElement();
- Object entry = deletedEntries.get(alias);
- if (entry instanceof TrustedCertEntry) {
- if (((TrustedCertEntry)entry).certRef != 0) {
- _removeItemFromKeychain(((TrustedCertEntry)entry).certRef);
- _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
- }
- } else {
- Certificate certElem;
- KeyEntry keyEntry = (KeyEntry)entry;
-
- if (keyEntry.chain != null) {
- for (int i = 0; i < keyEntry.chain.length; i++) {
- if (keyEntry.chainRefs[i] != 0) {
- _removeItemFromKeychain(keyEntry.chainRefs[i]);
- _releaseKeychainItemRef(keyEntry.chainRefs[i]);
- }
- }
-
- if (keyEntry.keyRef != 0) {
- _removeItemFromKeychain(keyEntry.keyRef);
- _releaseKeychainItemRef(keyEntry.keyRef);
- }
- }
- }
- }
-
- // Add all of the certs or keys in the added entries.
- // No need to check for 0 refs, as they are in the added list.
- for (Enumeration<String> e = addedEntries.keys(); e.hasMoreElements(); ) {
- String alias = e.nextElement();
- Object entry = addedEntries.get(alias);
- if (entry instanceof TrustedCertEntry) {
- TrustedCertEntry tce = (TrustedCertEntry)entry;
- Certificate certElem;
- certElem = tce.cert;
- tce.certRef = addCertificateToKeychain(alias, certElem);
- } else {
- KeyEntry keyEntry = (KeyEntry)entry;
-
- if (keyEntry.chain != null) {
- for (int i = 0; i < keyEntry.chain.length; i++) {
- keyEntry.chainRefs[i] = addCertificateToKeychain(alias, keyEntry.chain[i]);
- }
-
- keyEntry.keyRef = _addItemToKeychain(alias, false, keyEntry.protectedPrivKey, keyEntry.password);
- }
- }
- }
-
- // Clear the added and deletedEntries hashtables here, now that we're done with the updates.
- // For the deleted entries, we freed up the native references above.
- deletedEntries.clear();
- addedEntries.clear();
- }
-
- private long addCertificateToKeychain(String alias, Certificate cert) {
- byte[] certblob = null;
- long returnValue = 0;
-
- try {
- certblob = cert.getEncoded();
- returnValue = _addItemToKeychain(alias, true, certblob, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return returnValue;
- }
-
- private native long _addItemToKeychain(String alias, boolean isCertificate, byte[] datablob, char[] password);
- private native int _removeItemFromKeychain(long certRef);
- private native void _releaseKeychainItemRef(long keychainItemRef);
-
- /**
- * Loads the keystore from the Keychain.
- *
- * @param stream Ignored - here for API compatibility.
- * @param password Ignored - if user needs to unlock keychain Security
- * framework will post any dialogs.
- *
- * @exception IOException if there is an I/O or format problem with the
- * keystore data
- * @exception NoSuchAlgorithmException if the algorithm used to check
- * the integrity of the keystore cannot be found
- * @exception CertificateException if any of the certificates in the
- * keystore could not be loaded
- */
- public void engineLoad(InputStream stream, char[] password)
- throws IOException, NoSuchAlgorithmException, CertificateException
- {
- permissionCheck();
-
- // Release any stray keychain references before clearing out the entries.
- synchronized(entries) {
- for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
- String alias = e.nextElement();
- Object entry = entries.get(alias);
- if (entry instanceof TrustedCertEntry) {
- if (((TrustedCertEntry)entry).certRef != 0) {
- _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
- }
- } else {
- KeyEntry keyEntry = (KeyEntry)entry;
-
- if (keyEntry.chain != null) {
- for (int i = 0; i < keyEntry.chain.length; i++) {
- if (keyEntry.chainRefs[i] != 0) {
- _releaseKeychainItemRef(keyEntry.chainRefs[i]);
- }
- }
-
- if (keyEntry.keyRef != 0) {
- _releaseKeychainItemRef(keyEntry.keyRef);
- }
- }
- }
- }
-
- entries.clear();
- _scanKeychain();
- }
- }
-
- private native void _scanKeychain();
-
- /**
- * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called.
- */
- private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) {
- TrustedCertEntry tce = new TrustedCertEntry();
-
- try {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- InputStream input = new ByteArrayInputStream(derStream);
- X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
- input.close();
- tce.cert = cert;
- tce.certRef = keychainItemRef;
-
- // Make a creation date.
- if (creationDate != 0)
- tce.date = new Date(creationDate);
- else
- tce.date = new Date();
-
- int uniqueVal = 1;
- String originalAlias = alias;
-
- while (entries.containsKey(alias.toLowerCase())) {
- alias = originalAlias + " " + uniqueVal;
- uniqueVal++;
- }
-
- entries.put(alias.toLowerCase(), tce);
- } catch (Exception e) {
- // The certificate will be skipped.
- System.err.println("KeychainStore Ignored Exception: " + e);
- }
- }
-
- /**
- * Callback method from _scanKeychain. If an identity is found, this method will be called to create Java certificate
- * and private key objects from the keychain data.
- */
- private void createKeyEntry(String alias, long creationDate, long secKeyRef, long[] secCertificateRefs, byte[][] rawCertData)
- throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
- KeyEntry ke = new KeyEntry();
-
- // First, store off the private key information. This is the easy part.
- ke.protectedPrivKey = null;
- ke.keyRef = secKeyRef;
-
- // Make a creation date.
- if (creationDate != 0)
- ke.date = new Date(creationDate);
- else
- ke.date = new Date();
-
- // Next, create X.509 Certificate objects from the raw data. This is complicated
- // because a certificate's public key may be too long for Java's default encryption strength.
- List<CertKeychainItemPair> createdCerts = new ArrayList<>();
-
- try {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
-
- for (int i = 0; i < rawCertData.length; i++) {
- try {
- InputStream input = new ByteArrayInputStream(rawCertData[i]);
- X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
- input.close();
-
- // We successfully created the certificate, so track it and its corresponding SecCertificateRef.
- createdCerts.add(new CertKeychainItemPair(secCertificateRefs[i], cert));
- } catch (CertificateException e) {
- // The certificate will be skipped.
- System.err.println("KeychainStore Ignored Exception: " + e);
- }
- }
- } catch (CertificateException e) {
- e.printStackTrace();
- } catch (IOException ioe) {
- ioe.printStackTrace(); // How would this happen?
- }
-
- // We have our certificates in the List, so now extract them into an array of
- // Certificates and SecCertificateRefs.
- CertKeychainItemPair[] objArray = createdCerts.toArray(new CertKeychainItemPair[0]);
- Certificate[] certArray = new Certificate[objArray.length];
- long[] certRefArray = new long[objArray.length];
-
- for (int i = 0; i < objArray.length; i++) {
- CertKeychainItemPair addedItem = objArray[i];
- certArray[i] = addedItem.mCert;
- certRefArray[i] = addedItem.mCertificateRef;
- }
-
- ke.chain = certArray;
- ke.chainRefs = certRefArray;
-
- // If we don't have already have an item with this item's alias
- // create a new one for it.
- int uniqueVal = 1;
- String originalAlias = alias;
-
- while (entries.containsKey(alias.toLowerCase())) {
- alias = originalAlias + " " + uniqueVal;
- uniqueVal++;
- }
-
- entries.put(alias.toLowerCase(), ke);
- }
-
- private class CertKeychainItemPair {
- long mCertificateRef;
- Certificate mCert;
-
- CertKeychainItemPair(long inCertRef, Certificate cert) {
- mCertificateRef = inCertRef;
- mCert = cert;
- }
- }
-
- /*
- * Validate Certificate Chain
- */
- private boolean validateChain(Certificate[] certChain)
- {
- for (int i = 0; i < certChain.length-1; i++) {
- X500Principal issuerDN =
- ((X509Certificate)certChain[i]).getIssuerX500Principal();
- X500Principal subjectDN =
- ((X509Certificate)certChain[i+1]).getSubjectX500Principal();
- if (!(issuerDN.equals(subjectDN)))
- return false;
- }
- return true;
- }
-
- @SuppressWarnings("deprecation")
- private byte[] fetchPrivateKeyFromBag(byte[] privateKeyInfo) throws IOException, NoSuchAlgorithmException, CertificateException
- {
- byte[] returnValue = null;
- DerValue val = new DerValue(new ByteArrayInputStream(privateKeyInfo));
- DerInputStream s = val.toDerInputStream();
- int version = s.getInteger();
-
- if (version != 3) {
- throw new IOException("PKCS12 keystore not in version 3 format");
- }
-
- /*
- * Read the authSafe.
- */
- byte[] authSafeData;
- ContentInfo authSafe = new ContentInfo(s);
- ObjectIdentifier contentType = authSafe.getContentType();
-
- if (contentType.equals(ContentInfo.DATA_OID)) {
- authSafeData = authSafe.getData();
- } else /* signed data */ {
- throw new IOException("public key protected PKCS12 not supported");
- }
-
- DerInputStream as = new DerInputStream(authSafeData);
- DerValue[] safeContentsArray = as.getSequence(2);
- int count = safeContentsArray.length;
-
- /*
- * Spin over the ContentInfos.
- */
- for (int i = 0; i < count; i++) {
- byte[] safeContentsData;
- ContentInfo safeContents;
- DerInputStream sci;
- byte[] eAlgId = null;
-
- sci = new DerInputStream(safeContentsArray[i].toByteArray());
- safeContents = new ContentInfo(sci);
- contentType = safeContents.getContentType();
- safeContentsData = null;
-
- if (contentType.equals(ContentInfo.DATA_OID)) {
- safeContentsData = safeContents.getData();
- } else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) {
- // The password was used to export the private key from the keychain.
- // The Keychain won't export the key with encrypted data, so we don't need
- // to worry about it.
- continue;
- } else {
- throw new IOException("public key protected PKCS12" +
- " not supported");
- }
- DerInputStream sc = new DerInputStream(safeContentsData);
- returnValue = extractKeyData(sc);
- }
-
- return returnValue;
- }
-
- @SuppressWarnings("deprecation")
- private byte[] extractKeyData(DerInputStream stream)
- throws IOException, NoSuchAlgorithmException, CertificateException
- {
- byte[] returnValue = null;
- DerValue[] safeBags = stream.getSequence(2);
- int count = safeBags.length;
-
- /*
- * Spin over the SafeBags.
- */
- for (int i = 0; i < count; i++) {
- ObjectIdentifier bagId;
- DerInputStream sbi;
- DerValue bagValue;
- Object bagItem = null;
-
- sbi = safeBags[i].toDerInputStream();
- bagId = sbi.getOID();
- bagValue = sbi.getDerValue();
- if (!bagValue.isContextSpecific((byte)0)) {
- throw new IOException("unsupported PKCS12 bag value type "
- + bagValue.tag);
- }
- bagValue = bagValue.data.getDerValue();
- if (bagId.equals(PKCS8ShroudedKeyBag_OID)) {
- // got what we were looking for. Return it.
- returnValue = bagValue.toByteArray();
- } else {
- // log error message for "unsupported PKCS12 bag type"
- System.out.println("Unsupported bag type '" + bagId + "'");
- }
- }
-
- return returnValue;
- }
-
- /*
- * Generate PBE Algorithm Parameters
- */
- private AlgorithmParameters getAlgorithmParameters(String algorithm)
- throws IOException
- {
- AlgorithmParameters algParams = null;
-
- // create PBE parameters from salt and iteration count
- PBEParameterSpec paramSpec =
- new PBEParameterSpec(getSalt(), iterationCount);
- try {
- algParams = AlgorithmParameters.getInstance(algorithm);
- algParams.init(paramSpec);
- } catch (Exception e) {
- IOException ioe =
- new IOException("getAlgorithmParameters failed: " +
- e.getMessage());
- ioe.initCause(e);
- throw ioe;
- }
- return algParams;
- }
-
- // the source of randomness
- private SecureRandom random;
-
- /*
- * Generate random salt
- */
- private byte[] getSalt()
- {
- // Generate a random salt.
- byte[] salt = new byte[SALT_LEN];
- if (random == null) {
- random = new SecureRandom();
- }
- salt = random.generateSeed(SALT_LEN);
- return salt;
- }
-
- /*
- * parse Algorithm Parameters
- */
- private AlgorithmParameters parseAlgParameters(DerInputStream in)
- throws IOException
- {
- AlgorithmParameters algParams = null;
- try {
- DerValue params;
- if (in.available() == 0) {
- params = null;
- } else {
- params = in.getDerValue();
- if (params.tag == DerValue.tag_Null) {
- params = null;
- }
- }
- if (params != null) {
- algParams = AlgorithmParameters.getInstance("PBE");
- algParams.init(params.toByteArray());
- }
- } catch (Exception e) {
- IOException ioe =
- new IOException("parseAlgParameters failed: " +
- e.getMessage());
- ioe.initCause(e);
- throw ioe;
- }
- return algParams;
- }
-
- /*
- * Generate PBE key
- */
- private SecretKey getPBEKey(char[] password) throws IOException
- {
- SecretKey skey = null;
-
- try {
- PBEKeySpec keySpec = new PBEKeySpec(password);
- SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
- skey = skFac.generateSecret(keySpec);
- } catch (Exception e) {
- IOException ioe = new IOException("getSecretKey failed: " +
- e.getMessage());
- ioe.initCause(e);
- throw ioe;
- }
- return skey;
- }
-
- /*
- * Encrypt private key using Password-based encryption (PBE)
- * as defined in PKCS#5.
- *
- * NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
- * used to derive the key and IV.
- *
- * @return encrypted private key encoded as EncryptedPrivateKeyInfo
- */
- private byte[] encryptPrivateKey(byte[] data, char[] password)
- throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
- {
- byte[] key = null;
-
- try {
- // create AlgorithmParameters
- AlgorithmParameters algParams =
- getAlgorithmParameters("PBEWithSHA1AndDESede");
-
- // Use JCE
- SecretKey skey = getPBEKey(password);
- Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede");
- cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
- byte[] encryptedKey = cipher.doFinal(data);
-
- // wrap encrypted private key in EncryptedPrivateKeyInfo
- // as defined in PKCS#8
- AlgorithmId algid =
- new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams);
- EncryptedPrivateKeyInfo encrInfo =
- new EncryptedPrivateKeyInfo(algid, encryptedKey);
- key = encrInfo.getEncoded();
- } catch (Exception e) {
- UnrecoverableKeyException uke =
- new UnrecoverableKeyException("Encrypt Private Key failed: "
- + e.getMessage());
- uke.initCause(e);
- throw uke;
- }
-
- return key;
- }
-
-
-}
-
--- a/jdk/src/jdk.deploy.osx/macosx/native/libosx/KeystoreImpl.m Tue Aug 18 12:49:00 2015 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,589 +0,0 @@
-/*
- * Copyright (c) 2011, 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.
- */
-
-#import "apple_security_KeychainStore.h"
-
-#import <Security/Security.h>
-#import <Security/SecImportExport.h>
-#import <CoreServices/CoreServices.h> // (for require() macros)
-#import <JavaNativeFoundation/JavaNativeFoundation.h>
-
-
-static JNF_CLASS_CACHE(jc_KeychainStore, "apple/security/KeychainStore");
-static JNF_MEMBER_CACHE(jm_createTrustedCertEntry, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V");
-static JNF_MEMBER_CACHE(jm_createKeyEntry, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V");
-
-static jstring getLabelFromItem(JNIEnv *env, SecKeychainItemRef inItem)
-{
- OSStatus status;
- jstring returnValue = NULL;
- char *attribCString = NULL;
-
- SecKeychainAttribute itemAttrs[] = { { kSecLabelItemAttr, 0, NULL } };
- SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
-
- status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
-
- if(status) {
- cssmPerror("getLabelFromItem: SecKeychainItemCopyContent", status);
- goto errOut;
- }
-
- attribCString = malloc(itemAttrs[0].length + 1);
- strncpy(attribCString, itemAttrs[0].data, itemAttrs[0].length);
- attribCString[itemAttrs[0].length] = '\0';
- returnValue = (*env)->NewStringUTF(env, attribCString);
-
-errOut:
- SecKeychainItemFreeContent(&attrList, NULL);
- if (attribCString) free(attribCString);
- return returnValue;
-}
-
-static jlong getModDateFromItem(JNIEnv *env, SecKeychainItemRef inItem)
-{
- OSStatus status;
- SecKeychainAttribute itemAttrs[] = { { kSecModDateItemAttr, 0, NULL } };
- SecKeychainAttributeList attrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
- jlong returnValue = 0;
-
- status = SecKeychainItemCopyContent(inItem, NULL, &attrList, NULL, NULL);
-
- if(status) {
- // This is almost always missing, so don't dump an error.
- // cssmPerror("getModDateFromItem: SecKeychainItemCopyContent", status);
- goto errOut;
- }
-
- memcpy(&returnValue, itemAttrs[0].data, itemAttrs[0].length);
-
-errOut:
- SecKeychainItemFreeContent(&attrList, NULL);
- return returnValue;
-}
-
-static void setLabelForItem(NSString *inLabel, SecKeychainItemRef inItem)
-{
- OSStatus status;
- const char *labelCString = [inLabel UTF8String];
-
- // Set up attribute vector (each attribute consists of {tag, length, pointer}):
- SecKeychainAttribute attrs[] = {
- { kSecLabelItemAttr, strlen(labelCString), (void *)labelCString }
- };
-
- const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
-
- // Not changing data here, just attributes.
- status = SecKeychainItemModifyContent(inItem, &attributes, 0, NULL);
-
- if(status) {
- cssmPerror("setLabelForItem: SecKeychainItemModifyContent", status);
- }
-}
-
-/*
- * Given a SecIdentityRef, do our best to construct a complete, ordered, and
- * verified cert chain, returning the result in a CFArrayRef. The result is
- * can be passed back to Java as a chain for a private key.
- */
-static OSStatus completeCertChain(
- SecIdentityRef identity,
- SecCertificateRef trustedAnchor, // optional additional trusted anchor
- bool includeRoot, // include the root in outArray
- CFArrayRef *outArray) // created and RETURNED
-{
- SecTrustRef secTrust = NULL;
- SecPolicyRef policy = NULL;
- SecPolicySearchRef policySearch = NULL;
- SecTrustResultType secTrustResult;
- CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used
- CFArrayRef certChain = NULL; // constructed chain, CERTS ONLY
- CFMutableArrayRef subjCerts; // passed to SecTrust
- CFMutableArrayRef certArray; // returned array starting with
- // identity
- CFIndex numResCerts;
- CFIndex dex;
- OSStatus ortn;
- SecCertificateRef certRef;
-
- /* First element in out array is the SecIdentity */
- certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFArrayAppendValue(certArray, identity);
-
- /* the single element in certs-to-be-evaluated comes from the identity */
- ortn = SecIdentityCopyCertificate(identity, &certRef);
- if(ortn) {
- /* should never happen */
- cssmPerror("SecIdentityCopyCertificate", ortn);
- return ortn;
- }
-
- /*
- * Now use SecTrust to get a complete cert chain, using all of the
- * user's keychains to look for intermediate certs.
- * NOTE this does NOT handle root certs which are not in the system
- * root cert DB.
- */
- subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
- CFArraySetValueAtIndex(subjCerts, 0, certRef);
-
- /* the array owns the subject cert ref now */
- CFRelease(certRef);
-
- /* Get a SecPolicyRef for generic X509 cert chain verification */
- ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
- &CSSMOID_APPLE_X509_BASIC,
- NULL, // value
- &policySearch);
- if(ortn) {
- /* should never happen */
- cssmPerror("SecPolicySearchCreate", ortn);
- goto errOut;
- }
- ortn = SecPolicySearchCopyNext(policySearch, &policy);
- if(ortn) {
- /* should never happen */
- cssmPerror("SecPolicySearchCopyNext", ortn);
- goto errOut;
- }
-
- /* build a SecTrustRef for specified policy and certs */
- ortn = SecTrustCreateWithCertificates(subjCerts,
- policy, &secTrust);
- if(ortn) {
- cssmPerror("SecTrustCreateWithCertificates", ortn);
- goto errOut;
- }
-
- if(trustedAnchor) {
- /*
- * Tell SecTrust to trust this one in addition to the current
- * trusted system-wide anchors.
- */
- CFMutableArrayRef newAnchors;
- CFArrayRef currAnchors;
-
- ortn = SecTrustCopyAnchorCertificates(&currAnchors);
- if(ortn) {
- /* should never happen */
- cssmPerror("SecTrustCopyAnchorCertificates", ortn);
- goto errOut;
- }
- newAnchors = CFArrayCreateMutableCopy(NULL,
- CFArrayGetCount(currAnchors) + 1,
- currAnchors);
- CFRelease(currAnchors);
- CFArrayAppendValue(newAnchors, trustedAnchor);
- ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors);
- CFRelease(newAnchors);
- if(ortn) {
- cssmPerror("SecTrustSetAnchorCertificates", ortn);
- goto errOut;
- }
- }
-
- /* evaluate: GO */
- ortn = SecTrustEvaluate(secTrust, &secTrustResult);
- if(ortn) {
- cssmPerror("SecTrustEvaluate", ortn);
- goto errOut;
- }
- switch(secTrustResult) {
- case kSecTrustResultUnspecified:
- /* cert chain valid, no special UserTrust assignments; drop thru */
- case kSecTrustResultProceed:
- /* cert chain valid AND user explicitly trusts this */
- break;
- default:
- /*
- * Cert chain construction failed.
- * Just go with the single subject cert we were given; maybe the
- * peer can complete the chain.
- */
- ortn = noErr;
- goto errOut;
- }
-
- /* get resulting constructed cert chain */
- ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv);
- if(ortn) {
- cssmPerror("SecTrustEvaluate", ortn);
- goto errOut;
- }
-
- /*
- * Copy certs from constructed chain to our result array, skipping
- * the leaf (which is already there, as a SecIdentityRef) and possibly
- * a root.
- */
- numResCerts = CFArrayGetCount(certChain);
- if(numResCerts < 1) {
- /*
- * Can't happen: If chain doesn't verify to a root, we'd
- * have bailed after SecTrustEvaluate().
- */
- ortn = noErr;
- goto errOut;
- }
- if(!includeRoot) {
- /* skip the last (root) cert) */
- numResCerts--;
- }
- for(dex=1; dex<numResCerts; dex++) {
- certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex);
- CFArrayAppendValue(certArray, certRef);
- }
-errOut:
- /* clean up */
- if(secTrust) {
- CFRelease(secTrust);
- }
- if(subjCerts) {
- CFRelease(subjCerts);
- }
- if(policy) {
- CFRelease(policy);
- }
- if(policySearch) {
- CFRelease(policySearch);
- }
- *outArray = certArray;
- return ortn;
-}
-
-static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore)
-{
- // Search the user keychain list for all identities. Identities are a certificate/private key association that
- // can be chosen for a purpose such as signing or an SSL connection.
- SecIdentitySearchRef identitySearch = NULL;
- // Pass 0 if you want all identities returned by this search
- OSStatus err = SecIdentitySearchCreate(NULL, 0, &identitySearch);
- SecIdentityRef theIdentity = NULL;
- OSErr searchResult = noErr;
-
- do {
- searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity);
-
- if (searchResult == noErr) {
- // Get the cert from the identity, then generate a chain.
- SecCertificateRef certificate;
- SecIdentityCopyCertificate(theIdentity, &certificate);
- CFArrayRef certChain = NULL;
-
- // *** Should do something with this error...
- err = completeCertChain(theIdentity, NULL, TRUE, &certChain);
-
- CFIndex i, certCount = CFArrayGetCount(certChain);
-
- // Make a java array of certificate data from the chain.
- jclass byteArrayClass = (*env)->FindClass(env, "[B");
- if (byteArrayClass == NULL) {
- goto errOut;
- }
- jobjectArray javaCertArray = (*env)->NewObjectArray(env, certCount, byteArrayClass, NULL);
- // Cleanup first then check for a NULL return code
- (*env)->DeleteLocalRef(env, byteArrayClass);
- if (javaCertArray == NULL) {
- goto errOut;
- }
-
- // And, make an array of the certificate refs.
- jlongArray certRefArray = (*env)->NewLongArray(env, certCount);
- if (certRefArray == NULL) {
- goto errOut;
- }
-
- SecCertificateRef currCertRef = NULL;
-
- for (i = 0; i < certCount; i++) {
- CSSM_DATA currCertData;
-
- if (i == 0)
- currCertRef = certificate;
- else
- currCertRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
-
- bzero(&currCertData, sizeof(CSSM_DATA));
- err = SecCertificateGetData(currCertRef, &currCertData);
- jbyteArray encodedCertData = (*env)->NewByteArray(env, currCertData.Length);
- if (encodedCertData == NULL) {
- goto errOut;
- }
- (*env)->SetByteArrayRegion(env, encodedCertData, 0, currCertData.Length, (jbyte *)currCertData.Data);
- (*env)->SetObjectArrayElement(env, javaCertArray, i, encodedCertData);
- jlong certRefElement = ptr_to_jlong(currCertRef);
- (*env)->SetLongArrayRegion(env, certRefArray, i, 1, &certRefElement);
- }
-
- // Get the private key. When needed we'll export the data from it later.
- SecKeyRef privateKeyRef;
- err = SecIdentityCopyPrivateKey(theIdentity, &privateKeyRef);
-
- // Find the label. It's a 'blob', but we interpret as characters.
- jstring alias = getLabelFromItem(env, (SecKeychainItemRef)certificate);
- if (alias == NULL) {
- goto errOut;
- }
-
- // Find the creation date.
- jlong creationDate = getModDateFromItem(env, (SecKeychainItemRef)certificate);
-
- // Call back to the Java object to create Java objects corresponding to this security object.
- jlong nativeKeyRef = ptr_to_jlong(privateKeyRef);
- JNFCallVoidMethod(env, keyStore, jm_createKeyEntry, alias, creationDate, nativeKeyRef, certRefArray, javaCertArray);
- }
- } while (searchResult == noErr);
-
-errOut:
- if (identitySearch != NULL) {
- CFRelease(identitySearch);
- }
-}
-
-static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
-{
- // Search the user keychain list for all X509 certificates.
- SecKeychainSearchRef keychainItemSearch = NULL;
- OSStatus err = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass, NULL, &keychainItemSearch);
- SecKeychainItemRef theItem = NULL;
- OSErr searchResult = noErr;
-
- do {
- searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem);
-
- if (searchResult == noErr) {
- // Make a byte array with the DER-encoded contents of the certificate.
- SecCertificateRef certRef = (SecCertificateRef)theItem;
- CSSM_DATA currCertificate;
- err = SecCertificateGetData(certRef, &currCertificate);
- jbyteArray certData = (*env)->NewByteArray(env, currCertificate.Length);
- if (certData == NULL) {
- goto errOut;
- }
- (*env)->SetByteArrayRegion(env, certData, 0, currCertificate.Length, (jbyte *)currCertificate.Data);
-
- // Find the label. It's a 'blob', but we interpret as characters.
- jstring alias = getLabelFromItem(env, theItem);
- if (alias == NULL) {
- goto errOut;
- }
-
- // Find the creation date.
- jlong creationDate = getModDateFromItem(env, theItem);
-
- // Call back to the Java object to create Java objects corresponding to this security object.
- jlong nativeRef = ptr_to_jlong(certRef);
- JNFCallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData);
- }
- } while (searchResult == noErr);
-
-errOut:
- if (keychainItemSearch != NULL) {
- CFRelease(keychainItemSearch);
- }
-}
-
-/*
- * Class: apple_security_KeychainStore
- * Method: _getEncodedKeyData
- * Signature: (J)[B
- */
-JNIEXPORT jbyteArray JNICALL Java_apple_security_KeychainStore__1getEncodedKeyData
-(JNIEnv *env, jobject this, jlong keyRefLong, jcharArray passwordObj)
-{
- SecKeyRef keyRef = (SecKeyRef)jlong_to_ptr(keyRefLong);
- SecKeyImportExportParameters paramBlock;
- OSStatus err = noErr;
- CFDataRef exportedData = NULL;
- jbyteArray returnValue = NULL;
- CFStringRef passwordStrRef = NULL;
-
- jsize passwordLen = 0;
- jchar *passwordChars = NULL;
-
- if (passwordObj) {
- passwordLen = (*env)->GetArrayLength(env, passwordObj);
-
- if (passwordLen > 0) {
- passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
- if (passwordChars == NULL) {
- goto errOut;
- }
- passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen);
- }
- }
-
- paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
- // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.
- paramBlock.flags = 0;
- paramBlock.passphrase = passwordStrRef;
- paramBlock.alertTitle = NULL;
- paramBlock.alertPrompt = NULL;
- paramBlock.accessRef = NULL;
- paramBlock.keyUsage = CSSM_KEYUSE_ANY;
- paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
-
- err = SecKeychainItemExport(keyRef, kSecFormatPKCS12, 0, ¶mBlock, &exportedData);
-
- if (err == noErr) {
- CFIndex size = CFDataGetLength(exportedData);
- returnValue = (*env)->NewByteArray(env, size);
- if (returnValue == NULL) {
- goto errOut;
- }
- (*env)->SetByteArrayRegion(env, returnValue, 0, size, (jbyte *)CFDataGetBytePtr(exportedData));
- }
-
-errOut:
- if (exportedData) CFRelease(exportedData);
- if (passwordStrRef) CFRelease(passwordStrRef);
-
- return returnValue;
-}
-
-
-/*
- * Class: apple_security_KeychainStore
- * Method: _scanKeychain
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1scanKeychain
-(JNIEnv *env, jobject this)
-{
- // Look for 'identities' -- private key and certificate chain pairs -- and add those.
- // Search for these first, because a certificate that's found here as part of an identity will show up
- // again later as a certificate.
- addIdentitiesToKeystore(env, this);
-
- // Scan current keychain for trusted certificates.
- addCertificatesToKeystore(env, this);
-
-}
-
-/*
- * Class: apple_security_KeychainStore
- * Method: _addItemToKeychain
- * Signature: (Ljava/lang/String;[B)I
-*/
-JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain
-(JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj)
-{
- OSStatus err;
- jlong returnValue = 0;
-
-JNF_COCOA_ENTER(env);
-
- jsize dataSize = (*env)->GetArrayLength(env, rawDataObj);
- jbyte *rawData = (*env)->GetByteArrayElements(env, rawDataObj, NULL);
- if (rawData == NULL) {
- goto errOut;
- }
-
- CFDataRef cfDataToImport = CFDataCreate(kCFAllocatorDefault, (UInt8 *)rawData, dataSize);
- CFArrayRef createdItems = NULL;
-
- SecKeychainRef defaultKeychain = NULL;
- SecKeychainCopyDefault(&defaultKeychain);
-
- SecExternalItemType dataType = (isCertificate == JNI_TRUE ? kSecFormatX509Cert : kSecFormatWrappedPKCS8);
-
- // Convert the password obj into a CFStringRef that the keychain importer can use for encryption.
- SecKeyImportExportParameters paramBlock;
- CFStringRef passwordStrRef = NULL;
-
- jsize passwordLen = 0;
- jchar *passwordChars = NULL;
-
- if (passwordObj) {
- passwordLen = (*env)->GetArrayLength(env, passwordObj);
- passwordChars = (*env)->GetCharArrayElements(env, passwordObj, NULL);
- passwordStrRef = CFStringCreateWithCharacters(kCFAllocatorDefault, passwordChars, passwordLen);
- }
-
- paramBlock.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
- // Note that setting the flags field **requires** you to pass in a password of some kind. The keychain will not prompt you.
- paramBlock.flags = 0;
- paramBlock.passphrase = passwordStrRef;
- paramBlock.alertTitle = NULL;
- paramBlock.alertPrompt = NULL;
- paramBlock.accessRef = NULL;
- paramBlock.keyUsage = CSSM_KEYUSE_ANY;
- paramBlock.keyAttributes = CSSM_KEYATTR_RETURN_DEFAULT;
-
- err = SecKeychainItemImport(cfDataToImport, NULL, &dataType, NULL,
- 0, ¶mBlock, defaultKeychain, &createdItems);
-
- if (err == noErr) {
- SecKeychainItemRef anItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(createdItems, 0);
-
- // Don't bother labeling keys. They become part of an identity, and are not an accessible part of the keychain.
- if (CFGetTypeID(anItem) == SecCertificateGetTypeID()) {
- setLabelForItem(JNFJavaToNSString(env, alias), anItem);
- }
-
- // Retain the item, since it will be released once when the array holding it gets released.
- CFRetain(anItem);
- returnValue = ptr_to_jlong(anItem);
- } else {
- cssmPerror("_addItemToKeychain: SecKeychainItemImport", err);
- }
-
- (*env)->ReleaseByteArrayElements(env, rawDataObj, rawData, JNI_ABORT);
-
- if (createdItems != NULL) {
- CFRelease(createdItems);
- }
-
-errOut: ;
-
-JNF_COCOA_EXIT(env);
-
- return returnValue;
-}
-
-/*
- * Class: apple_security_KeychainStore
- * Method: _removeItemFromKeychain
- * Signature: (J)I
-*/
-JNIEXPORT jint JNICALL Java_apple_security_KeychainStore__1removeItemFromKeychain
-(JNIEnv *env, jobject this, jlong keychainItem)
-{
- SecKeychainItemRef itemToRemove = jlong_to_ptr(keychainItem);
- return SecKeychainItemDelete(itemToRemove);
-}
-
-/*
- * Class: apple_security_KeychainStore
- * Method: _releaseKeychainItemRef
- * Signature: (J)V
- */
-JNIEXPORT void JNICALL Java_apple_security_KeychainStore__1releaseKeychainItemRef
-(JNIEnv *env, jobject this, jlong keychainItem)
-{
- SecKeychainItemRef itemToFree = jlong_to_ptr(keychainItem);
- CFRelease(itemToFree);
-}