2699 lines · 98.9 KB · Java
RawDownload
/* * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.pkcs11; import java.math.BigInteger; import java.io.InputStream;import java.io.OutputStream;import java.io.IOException;import java.io.ByteArrayInputStream;import java.io.UnsupportedEncodingException; import java.util.Arrays;import java.util.Collection;import java.util.Collections;import java.util.Date;import java.util.Enumeration;import java.util.Iterator;import java.util.List;import java.util.ArrayList;import java.util.HashSet;import java.util.HashMap;import java.util.Set; import java.security.*;import java.security.KeyStore.*; import java.security.cert.CertPath;import java.security.cert.Certificate;import java.security.cert.X509Certificate;import java.security.cert.CertificateFactory;import java.security.cert.CertificateException; import java.security.interfaces.*;import java.security.spec.*; import javax.crypto.SecretKey;import javax.crypto.interfaces.*; import javax.security.auth.Subject;import javax.security.auth.x500.X500Principal;import javax.security.auth.login.LoginException;import javax.security.auth.callback.Callback;import javax.security.auth.callback.PasswordCallback;import javax.security.auth.callback.CallbackHandler;import javax.security.auth.callback.UnsupportedCallbackException; import sun.security.util.Debug;import sun.security.x509.SerialNumber;import sun.security.util.DerValue; import sun.security.ec.ECParameters; import sun.security.pkcs11.Secmod.*;import static sun.security.pkcs11.P11Util.*; import sun.security.pkcs11.wrapper.*;import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import sun.security.rsa.RSAKeyFactory; final class P11KeyStore extends KeyStoreSpi {     private static final CK_ATTRIBUTE ATTR_CLASS_CERT =                        new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE);    private static final CK_ATTRIBUTE ATTR_CLASS_PKEY =                        new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY);    private static final CK_ATTRIBUTE ATTR_CLASS_SKEY =                        new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);     private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE =                        new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509);     private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE =                        new CK_ATTRIBUTE(CKA_TOKEN, true);     // XXX for testing purposes only    //  - NSS doesn't support persistent secret keys    //    (key type gets mangled if secret key is a token key)    //  - if debug is turned on, then this is set to false    private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE;     private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE =                        new CK_ATTRIBUTE(CKA_TRUSTED, true);    private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE =                        new CK_ATTRIBUTE(CKA_PRIVATE, true);     private static final long NO_HANDLE = -1;    private static final long FINDOBJECTS_MAX = 100;    private static final String ALIAS_SEP = "/";     private static final boolean NSS_TEST = false;    private static final Debug debug =                        Debug.getInstance("pkcs11keystore");    private static boolean CKA_TRUSTED_SUPPORTED = true;     private final Token token;     // If multiple certs are found to share the same CKA_LABEL    // at load time (NSS-style keystore), then the keystore is read    // and the unique keystore aliases are mapped to the entries.    // However, write capabilities are disabled.    private boolean writeDisabled = false;     // Map of unique keystore aliases to entries in the token    private HashMap<String, AliasInfo> aliasMap;     // whether to use NSS Secmod info for trust attributes    private final boolean useSecmodTrust;     // if useSecmodTrust == true, which type of trust we are interested in    private Secmod.TrustType nssTrustType;     /**     * The underlying token may contain multiple certs belonging to the     * same "personality" (for example, a signing cert and encryption cert),     * all sharing the same CKA_LABEL.  These must be resolved     * into unique keystore aliases.     *     * In addition, private keys and certs may not have a CKA_LABEL.     * It is assumed that a private key and corresponding certificate     * share the same CKA_ID, and that the CKA_ID is unique across the token.     * The CKA_ID may not be human-readable.     * These pairs must be resolved into unique keystore aliases.     *     * Furthermore, secret keys are assumed to have a CKA_LABEL     * unique across the entire token.     *     * When the KeyStore is loaded, instances of this class are     * created to represent the private keys/secret keys/certs     * that reside on the token.     */    private static class AliasInfo {         // CKA_CLASS - entry type        private CK_ATTRIBUTE type = null;         // CKA_LABEL of cert and secret key        private String label = null;         // CKA_ID of the private key/cert pair        private byte[] id = null;         // CKA_TRUSTED - true if cert is trusted        private boolean trusted = false;         // either end-entity cert or trusted cert depending on 'type'        private X509Certificate cert = null;         // chain        private X509Certificate chain[] = null;         // true if CKA_ID for private key and cert match up        private boolean matched = false;         // SecretKeyEntry        public AliasInfo(String label) {            this.type = ATTR_CLASS_SKEY;            this.label = label;        }         // PrivateKeyEntry        public AliasInfo(String label,                        byte[] id,                        boolean trusted,                        X509Certificate cert) {            this.type = ATTR_CLASS_PKEY;            this.label = label;            this.id = id;            this.trusted = trusted;            this.cert = cert;        }         public String toString() {            StringBuilder sb = new StringBuilder();            if (type == ATTR_CLASS_PKEY) {                sb.append("\ttype=[private key]\n");            } else if (type == ATTR_CLASS_SKEY) {                sb.append("\ttype=[secret key]\n");            } else if (type == ATTR_CLASS_CERT) {                sb.append("\ttype=[trusted cert]\n");            }            sb.append("\tlabel=[" + label + "]\n");            if (id == null) {                sb.append("\tid=[null]\n");            } else {                sb.append("\tid=" + P11KeyStore.getID(id) + "\n");            }            sb.append("\ttrusted=[" + trusted + "]\n");            sb.append("\tmatched=[" + matched + "]\n");            if (cert == null) {                sb.append("\tcert=[null]\n");            } else {                sb.append("\tcert=[\tsubject: " +                        cert.getSubjectX500Principal() +                        "\n\t\tissuer: " +                        cert.getIssuerX500Principal() +                        "\n\t\tserialNum: " +                        cert.getSerialNumber().toString() +                        "]");            }            return sb.toString();        }    }     /**     * callback handler for passing password to Provider.login method     */    private static class PasswordCallbackHandler implements CallbackHandler {         private char[] password;         private PasswordCallbackHandler(char[] password) {            if (password != null) {                this.password = (char[])password.clone();            }        }         public void handle(Callback[] callbacks)                throws IOException, UnsupportedCallbackException {            if (!(callbacks[0] instanceof PasswordCallback)) {                throw new UnsupportedCallbackException(callbacks[0]);            }            PasswordCallback pc = (PasswordCallback)callbacks[0];            pc.setPassword(password);  // this clones the password if not null        }         protected void finalize() throws Throwable {            if (password != null) {                Arrays.fill(password, ' ');            }            super.finalize();        }    }     /**     * getTokenObject return value.     *     * if object is not found, type is set to null.     * otherwise, type is set to the requested type.     */    private static class THandle {        private final long handle;              // token object handle        private final CK_ATTRIBUTE type;        // CKA_CLASS         private THandle(long handle, CK_ATTRIBUTE type) {            this.handle = handle;            this.type = type;        }    }     P11KeyStore(Token token) {        this.token = token;        this.useSecmodTrust = token.provider.nssUseSecmodTrust;    }     /**     * Returns the key associated with the given alias.     * The key must have been associated with     * the alias by a call to <code>setKeyEntry</code>,     * or by a call to <code>setEntry</code> with a     * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>.     *     * @param alias the alias name     * @param password the password, which must be <code>null</code>     *     * @return the requested key, or null if the given alias does not exist     * or does not identify a key-related entry.     *     * @exception NoSuchAlgorithmException if the algorithm for recovering the     * key cannot be found     * @exception UnrecoverableKeyException if the key cannot be recovered     */    public synchronized Key engineGetKey(String alias, char[] password)                throws NoSuchAlgorithmException, UnrecoverableKeyException {         token.ensureValid();        if (password != null && !token.config.getKeyStoreCompatibilityMode()) {            throw new NoSuchAlgorithmException("password must be null");        }         AliasInfo aliasInfo = aliasMap.get(alias);        if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {            return null;        }         Session session = null;        try {            session = token.getOpSession();             if (aliasInfo.type == ATTR_CLASS_PKEY) {                THandle h = getTokenObject(session,                                        aliasInfo.type,                                        aliasInfo.id,                                        null);                if (h.type == ATTR_CLASS_PKEY) {                    return loadPkey(session, h.handle);                }            } else {                THandle h = getTokenObject(session,                                        ATTR_CLASS_SKEY,                                        null,                                        alias);                if (h.type == ATTR_CLASS_SKEY) {                    return loadSkey(session, h.handle);                }            }             // did not find anything            return null;        } catch (PKCS11Exception pe) {            throw new ProviderException(pe);        } catch (KeyStoreException ke) {            throw new ProviderException(ke);        } finally {            token.releaseSession(session);        }    }     /**     * Returns the certificate chain associated with the given alias.     * The certificate chain must have been associated with the alias     * by a call to <code>setKeyEntry</code>,     * or by a call to <code>setEntry</code> with a     * <code>PrivateKeyEntry</code>.     *     * @param alias the alias name     *     * @return the certificate chain (ordered with the user's certificate first     * and the root certificate authority last), or null if the given alias     * does not exist or does not contain a certificate chain     */    public synchronized Certificate[] engineGetCertificateChain(String alias) {         token.ensureValid();         AliasInfo aliasInfo = aliasMap.get(alias);        if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) {            return null;        }        return aliasInfo.chain;    }     /**     * Returns the certificate associated with the given alias.     *     * <p> If the given alias name identifies an entry     * created by a call to <code>setCertificateEntry</code>,     * or created by a call to <code>setEntry</code> with a     * <code>TrustedCertificateEntry</code>,     * then the trusted certificate contained in that entry is returned.     *     * <p> If the given alias name identifies an entry     * created by a call to <code>setKeyEntry</code>,     * or created by a call to <code>setEntry</code> with a     * <code>PrivateKeyEntry</code>,     * then the first element of the certificate chain in that entry     * (if a chain exists) is returned.     *     * @param alias the alias name     *     * @return the certificate, or null if the given alias does not exist or     * does not contain a certificate.     */    public synchronized Certificate engineGetCertificate(String alias) {        token.ensureValid();         AliasInfo aliasInfo = aliasMap.get(alias);        if (aliasInfo == null) {            return null;        }        return aliasInfo.cert;    }     /**     * Returns the creation date of the entry identified by the given alias.     *     * @param alias the alias name     *     * @return the creation date of this entry, or null if the given alias does     * not exist     */    public Date engineGetCreationDate(String alias) {        token.ensureValid();        throw new ProviderException(new UnsupportedOperationException());    }     /**     * Assigns the given key to the given alias, protecting it with the given     * password.     *     * <p>If the given key is of type <code>java.security.PrivateKey</code>,     * it must be accompanied by a certificate chain certifying the     * corresponding public key.     *     * <p>If the given alias already exists, the keystore information     * associated with it is overridden by the given key (and possibly     * certificate chain).     *     * @param alias the alias name     * @param key the key to be associated with the alias     * @param password the password to protect the key     * @param chain the certificate chain for the corresponding public     * key (only required if the given key is of type     * <code>java.security.PrivateKey</code>).     *     * @exception KeyStoreException if the given key cannot be protected, or     * this operation fails for some other reason     */    public synchronized void engineSetKeyEntry(String alias, Key key,                                   char[] password,                                   Certificate[] chain)                throws KeyStoreException {         token.ensureValid();        checkWrite();         if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) {            throw new KeyStoreException("key must be PrivateKey or SecretKey");        } else if (key instanceof PrivateKey && chain == null) {            throw new KeyStoreException                ("PrivateKey must be accompanied by non-null chain");        } else if (key instanceof SecretKey && chain != null) {            throw new KeyStoreException                ("SecretKey must be accompanied by null chain");        } else if (password != null &&                    !token.config.getKeyStoreCompatibilityMode()) {            throw new KeyStoreException("Password must be null");        }         KeyStore.Entry entry = null;        try {            if (key instanceof PrivateKey) {                entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, chain);            } else if (key instanceof SecretKey) {                entry = new KeyStore.SecretKeyEntry((SecretKey)key);            }        } catch (NullPointerException npe) {            throw new KeyStoreException(npe);        } catch (IllegalArgumentException iae) {            throw new KeyStoreException(iae);        }        engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password));    }     /**     * Assigns the given key (that has already been protected) to the given     * alias.     *     * <p>If the protected key is of type     * <code>java.security.PrivateKey</code>,     * it must be accompanied by a certificate chain certifying the     * corresponding public key.     *     * <p>If the given alias already exists, the keystore information     * associated with it is overridden by the given key (and possibly     * certificate chain).     *     * @param alias the alias name     * @param key the key (in protected format) to be associated with the alias     * @param chain the certificate chain for the corresponding public     * key (only useful if the protected key is of type     * <code>java.security.PrivateKey</code>).     *     * @exception KeyStoreException if this operation fails.     */    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)                throws KeyStoreException {        token.ensureValid();        throw new ProviderException(new UnsupportedOperationException());    }     /**     * Assigns the given certificate to the given alias.     *     * <p> If the given alias identifies an existing entry     * created by a call to <code>setCertificateEntry</code>,

Lines 1–500 of 2699.