1536 lines · 56.6 KB · Java
RawDownload
/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.pkcs12; import java.io.*;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.security.Key;import java.security.KeyFactory;import java.security.PrivateKey;import java.security.KeyStoreSpi;import java.security.KeyStoreException;import java.security.UnrecoverableKeyException;import java.security.SecureRandom;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import java.security.cert.CertificateException;import java.security.spec.PKCS8EncodedKeySpec;import java.util.*;import java.math.*; import java.security.AlgorithmParameters;import java.security.spec.AlgorithmParameterSpec;import javax.crypto.spec.PBEParameterSpec;import javax.crypto.spec.PBEKeySpec;import javax.crypto.spec.SecretKeySpec;import javax.crypto.SecretKeyFactory;import javax.crypto.SecretKey;import javax.crypto.Cipher;import javax.crypto.Mac;import javax.security.auth.x500.X500Principal; import sun.security.util.DerInputStream;import sun.security.util.DerOutputStream;import sun.security.util.DerValue;import sun.security.util.ObjectIdentifier;import sun.security.pkcs.ContentInfo;import sun.security.x509.AlgorithmId;import sun.security.pkcs.EncryptedPrivateKeyInfo;  /** * This class provides the keystore implementation referred to as "PKCS12". * Implements the PKCS#12 PFX protected using the Password privacy mode. * The contents are protected using Password integrity mode. * * Currently we support following PBE algorithms: *  - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys *  - pbeWithSHAAnd40BitRC2CBC to encrypt certificates * * Supported encryption of various implementations : * * Software and mode.     Certificate encryption  Private key encryption * --------------------------------------------------------------------- * MSIE4 (domestic            40 bit RC2.            40 bit RC2 * and xport versions) * PKCS#12 export. * * MSIE4, 5 (domestic         40 bit RC2,            40 bit RC2, * and export versions)       3 key triple DES       3 key triple DES * PKCS#12 import. * * MSIE5                      40 bit RC2             3 key triple DES, * PKCS#12 export.                                   with SHA1 (168 bits) * * Netscape Communicator      40 bit RC2             3 key triple DES, * (domestic and export                              with SHA1 (168 bits) * versions) PKCS#12 export * * Netscape Communicator      40 bit ciphers only    All. * (export version) * PKCS#12 import. * * Netscape Communicator      All.                   All. * (domestic or fortified * version) PKCS#12 import. * * OpenSSL PKCS#12 code.      All.                   All. * --------------------------------------------------------------------- * * NOTE: Currently PKCS12 KeyStore does not support TrustedCertEntries. * PKCS#12 is mainly used to deliver private keys with their associated * certificate chain and aliases. In a PKCS12 keystore, entries are * identified by the alias, and a localKeyId is required to match the * private key with the certificate. * * @author Seema Malkani * @author Jeff Nisewanger * @author Jan Luehe * * @see KeyProtector * @see java.security.KeyStoreSpi * @see KeyTool * * */public final class PKCS12KeyStore extends KeyStoreSpi {     public static final int VERSION_3 = 3;     private static final int keyBag[]  = {1, 2, 840, 113549, 1, 12, 10, 1, 2};    private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};     private static final int pkcs9Name[]  = {1, 2, 840, 113549, 1, 9, 20};    private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};     private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};     private static final int pbeWithSHAAnd40BitRC2CBC[] =                                        {1, 2, 840, 113549, 1, 12, 1, 6};    private static final int pbeWithSHAAnd3KeyTripleDESCBC[] =                                        {1, 2, 840, 113549, 1, 12, 1, 3};     private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;    private static ObjectIdentifier CertBag_OID;    private static ObjectIdentifier PKCS9FriendlyName_OID;    private static ObjectIdentifier PKCS9LocalKeyId_OID;    private static ObjectIdentifier PKCS9CertType_OID;    private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID;    private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;     private int counter = 0;    private static final int iterationCount = 1024;    private static final int SALT_LEN = 20;     // private key count    // Note: This is a workaround to allow null localKeyID attribute    // in pkcs12 with one private key entry and associated cert-chain    private int privateKeyCount = 0;     // the source of randomness    private SecureRandom random;     static {        try {            PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);            CertBag_OID = new ObjectIdentifier(certBag);            PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);            PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);            PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);            pbeWithSHAAnd40BitRC2CBC_OID =                        new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC);            pbeWithSHAAnd3KeyTripleDESCBC_OID =                        new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);        } catch (IOException ioe) {            // should not happen        }    }     // Private keys and their supporting certificate chains    private static class KeyEntry {        Date date; // the creation date of this entry        byte[] protectedPrivKey;        Certificate chain[];        byte[] keyId;        String alias;    };     // A certificate with its PKCS #9 attributes    private static class CertEntry {        final X509Certificate cert;        final byte[] keyId;        final String alias;        CertEntry(X509Certificate cert, byte[] keyId, String alias) {            this.cert = cert;            this.keyId = keyId;            this.alias = alias;        }    }     /**     * Private keys and certificates are stored in a hashtable.     * Hash entries are keyed by alias names.     */    private Hashtable<String, KeyEntry> entries =                                new Hashtable<String, KeyEntry>();     private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();    private LinkedHashMap<X500Principal, X509Certificate> certsMap =            new LinkedHashMap<X500Principal, X509Certificate>();    private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>();     /**     * 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     *     * @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    {        KeyEntry entry = entries.get(alias.toLowerCase());        Key key = null;         if (entry == null) {            return null;        }         // get the encoded private key        byte[] encrBytes = entry.protectedPrivKey;         byte[] encryptedKey;        AlgorithmParameters algParams;        ObjectIdentifier algOid;        try {            // get the encrypted private key            EncryptedPrivateKeyInfo encrInfo =                        new EncryptedPrivateKeyInfo(encrBytes);            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;        }         try {            // Use JCE            SecretKey skey = getPBEKey(password);            Cipher cipher = Cipher.getInstance(algOid.toString());            cipher.init(Cipher.DECRYPT_MODE, skey, algParams);            byte[] privateKeyInfo = cipher.doFinal(encryptedKey);             PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyInfo);             /*             * Parse the key algorithm and then use a JCA key factory             * to create the private key.             */            DerValue val = new DerValue(privateKeyInfo);            DerInputStream in = val.toDerInputStream();            int i = in.getInteger();            DerValue[] value = in.getSequence(2);            AlgorithmId algId = new AlgorithmId(value[0].getOID());            String algName = algId.getName();             KeyFactory kfac = KeyFactory.getInstance(algName);            key =  kfac.generatePrivate(kspec);        } catch (Exception e) {            UnrecoverableKeyException uke =                new UnrecoverableKeyException("Get Key failed: " +                                        e.getMessage());            uke.initCause(e);            throw uke;        }        return key;    }     /**     * 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) {        KeyEntry entry = entries.get(alias.toLowerCase());        if (entry != null) {            if (entry.chain == null) {                return null;            } else {                return 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) {        KeyEntry entry = entries.get(alias.toLowerCase());        if (entry != null) {            if (entry.chain == null) {                return null;            } else {                return entry.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) {        KeyEntry entry = entries.get(alias.toLowerCase());        if (entry != null) {            return new Date(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 synchronized void engineSetKeyEntry(String alias, Key key,                        char[] password, Certificate[] chain)        throws KeyStoreException    {        try {            KeyEntry entry = new KeyEntry();            entry.date = new Date();             if (key instanceof PrivateKey) {                if ((key.getFormat().equals("PKCS#8")) ||                    (key.getFormat().equals("PKCS8"))) {                    // Encrypt the private key                    entry.protectedPrivKey =                        encryptPrivateKey(key.getEncoded(), password);                } 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) {                // validate cert-chain                if ((chain.length > 1) && (!validateChain(chain)))                   throw new KeyStoreException("Certificate chain is " +                                                "not validate");                entry.chain = chain.clone();            }             // set the keyId to current date            entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");            // set the alias            entry.alias = alias.toLowerCase();             // add the entry            entries.put(alias.toLowerCase(), 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 synchronized void engineSetKeyEntry(String alias, byte[] key,                                  Certificate[] chain)        throws KeyStoreException    {        // key must be encoded as EncryptedPrivateKeyInfo        // as defined in PKCS#8        try {            new EncryptedPrivateKeyInfo(key);        } catch (IOException ioe) {            KeyStoreException ke = new KeyStoreException("Private key is not"                        + " stored as PKCS#8 EncryptedPrivateKeyInfo: " + ioe);            ke.initCause(ioe);            throw ke;        }         KeyEntry entry = new KeyEntry();        entry.date = new Date();         try {            // set the keyId to current date            entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");        } catch (UnsupportedEncodingException ex) {            // Won't happen        }        // set the alias        entry.alias = alias.toLowerCase();         entry.protectedPrivKey = key.clone();        if (chain != null) {           entry.chain = chain.clone();        }         // add the entry        entries.put(alias.toLowerCase(), entry);    }      /*     * Generate random salt     */    private byte[] getSalt()    {        // Generate a random salt.        byte[] salt = new byte[SALT_LEN];        if (random == null) {           random = new SecureRandom();        }        random.nextBytes(salt);        return salt;    } 

Lines 1–500 of 1536.