802 lines · 28.6 KB · Java
RawDownload
/* * Copyright (c) 1997, 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.provider; import java.io.*;import java.security.*;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.cert.CertificateException;import java.util.*;import sun.misc.IOUtils; import sun.security.pkcs.EncryptedPrivateKeyInfo; /** * This class provides the keystore implementation referred to as "JKS". * * @author Jan Luehe * @author David Brownell * * * @see KeyProtector * @see java.security.KeyStoreSpi * @see KeyTool * * @since 1.2 */ abstract class JavaKeyStore extends KeyStoreSpi {     // regular JKS    public static final class JKS extends JavaKeyStore {        String convertAlias(String alias) {            return alias.toLowerCase();        }    }     // special JKS that uses case sensitive aliases    public static final class CaseExactJKS extends JavaKeyStore {        String convertAlias(String alias) {            return alias;        }    }     private static final int MAGIC = 0xfeedfeed;    private static final int VERSION_1 = 0x01;    private static final int VERSION_2 = 0x02;     // Private keys and their supporting certificate chains    private static class KeyEntry {        Date date; // the creation date of this entry        byte[] protectedPrivKey;        Certificate chain[];    };     // Trusted certificates    private static class TrustedCertEntry {        Date date; // the creation date of this entry        Certificate cert;    };     /**     * Private keys and certificates are stored in a hashtable.     * Hash entries are keyed by alias names.     */    private final Hashtable<String, Object> entries;     JavaKeyStore() {        entries = new Hashtable<String, Object>();    }     // convert an alias to internal form, overridden in subclasses:    // lower case for regular JKS    // original string for CaseExactJKS    abstract String convertAlias(String alias);     /**     * 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    {        Object entry = entries.get(convertAlias(alias));         if (entry == null || !(entry instanceof KeyEntry)) {            return null;        }        if (password == null) {            throw new UnrecoverableKeyException("Password must not be null");        }         KeyProtector keyProtector = new KeyProtector(password);        byte[] encrBytes = ((KeyEntry)entry).protectedPrivKey;        EncryptedPrivateKeyInfo encrInfo;        byte[] plain;        try {            encrInfo = new EncryptedPrivateKeyInfo(encrBytes);        } catch (IOException ioe) {            throw new UnrecoverableKeyException("Private key not stored as "                                                + "PKCS #8 "                                                + "EncryptedPrivateKeyInfo");        }        return keyProtector.recover(encrInfo);    }     /**     * 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) {        Object entry = entries.get(convertAlias(alias));         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) {        Object entry = entries.get(convertAlias(alias));         if (entry != null) {            if (entry instanceof TrustedCertEntry) {                return ((TrustedCertEntry)entry).cert;            } else {                if (((KeyEntry)entry).chain == null) {                    return null;                } else {                    return ((KeyEntry)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) {        Object entry = entries.get(convertAlias(alias));         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 private key to the given alias, protecting     * it with the given password as defined in PKCS8.     *     * <p>The given java.security.PrivateKey <code>key</code> 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 certificate     * chain.     *     * @param alias the alias name     * @param key the private 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 is not a private 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    {        KeyProtector keyProtector = null;         if (!(key instanceof java.security.PrivateKey)) {            throw new KeyStoreException("Cannot store non-PrivateKeys");        }        try {            synchronized(entries) {                KeyEntry entry = new KeyEntry();                entry.date = new Date();                 // Protect the encoding of the key                keyProtector = new KeyProtector(password);                entry.protectedPrivKey = keyProtector.protect(key);                 // clone the chain                if ((chain != null) &&                    (chain.length != 0)) {                    entry.chain = chain.clone();                } else {                    entry.chain = null;                }                 entries.put(convertAlias(alias), entry);            }        } catch (NoSuchAlgorithmException nsae) {            throw new KeyStoreException("Key protection algorithm not found");        } finally {            keyProtector = null;        }    }     /**     * 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    {        synchronized(entries) {            // key must be encoded as EncryptedPrivateKeyInfo as defined in            // PKCS#8            try {                new EncryptedPrivateKeyInfo(key);            } catch (IOException ioe) {                throw new KeyStoreException("key is not encoded as "                                            + "EncryptedPrivateKeyInfo");            }             KeyEntry entry = new KeyEntry();            entry.date = new Date();             entry.protectedPrivKey = key.clone();            if ((chain != null) &&                (chain.length != 0)) {                entry.chain = chain.clone();            } else {                entry.chain = null;            }             entries.put(convertAlias(alias), 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    {        synchronized(entries) {             Object entry = entries.get(convertAlias(alias));            if ((entry != null) && (entry instanceof KeyEntry)) {                throw new KeyStoreException                    ("Cannot overwrite own certificate");            }             TrustedCertEntry trustedCertEntry = new TrustedCertEntry();            trustedCertEntry.cert = cert;            trustedCertEntry.date = new Date();            entries.put(convertAlias(alias), 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    {        synchronized(entries) {            entries.remove(convertAlias(alias));        }    }     /**     * Lists all the alias names of this keystore.     *     * @return enumeration of the alias names     */    public Enumeration<String> engineAliases() {        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) {        return entries.containsKey(convertAlias(alias));    }     /**     * Retrieves the number of entries in this keystore.     *     * @return the number of entries in this keystore     */    public int engineSize() {        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) {        Object entry = entries.get(convertAlias(alias));        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) {        Object entry = entries.get(convertAlias(alias));        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) {        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 if (((KeyEntry)entry).chain != null) {                certElem = ((KeyEntry)entry).chain[0];            } else {                continue;            }            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 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    {        synchronized(entries) {            /*             * KEYSTORE FORMAT:             *             * Magic number (big-endian integer),             * Version of this file format (big-endian integer),             *             * Count (big-endian integer),             * followed by "count" instances of either:             *             *     {

Lines 1–500 of 802.