diff -r 13967da712ff -r ba2c9c7773b6 jdk/src/jdk.deploy.osx/macosx/classes/apple/security/KeychainStore.java --- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/security/KeychainStore.java Thu Aug 20 11:38:20 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 deletedEntries = new Hashtable<>(); - - /** - * Entries that have been added. When something calls engineStore we'll - * add them to the keychain. - */ - private Hashtable addedEntries = new Hashtable<>(); - - /** - * Private keys and certificates are stored in a hashtable. - * Hash entries are keyed by alias names. - */ - private Hashtable 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() { - 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 key entry. - * - * @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 trusted certificate entry or a - * key entry 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. - * - *

If the given alias name identifies a - * trusted certificate entry, the certificate associated with that - * entry is returned. If the given alias name identifies a - * key entry, 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. - * - *

If the given key is of type java.security.PrivateKey, - * it must be accompanied by a certificate chain certifying the - * corresponding public key. - * - *

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 - * java.security.PrivateKey). - * - * @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. - * - *

If the protected key is of type - * java.security.PrivateKey, it must be accompanied by a - * certificate chain certifying the corresponding public key. If the - * underlying keystore implementation is of type jks, - * key must be encoded as an - * EncryptedPrivateKeyInfo as defined in the PKCS #8 standard. - * - *

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 - * java.security.PrivateKey). - * - * @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. - * - *

If the given alias already exists in this keystore and identifies a - * trusted certificate entry, 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 trusted certificate entry, 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 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 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 - * key entry, and false otherwise. - * - * @return true if the entry identified by the given alias is a - * key entry, 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 - * trusted certificate entry, and false otherwise. - * - * @return true if the entry identified by the given alias is a - * trusted certificate entry, 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. - * - *

This method attempts to match the given certificate with each - * keystore entry. If the entry being considered - * is a trusted certificate entry, the given certificate is - * compared to that entry's certificate. If the entry being considered is - * a key entry, 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 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 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 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 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 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; - } - - -} -