diff -r c0b3077af726 -r e5d5f0f2d40d jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java --- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java Tue Dec 06 14:54:11 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2683 +0,0 @@ -/* - * Copyright (c) 2003, 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 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.Collections; -import java.util.Date; -import java.util.Enumeration; -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.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.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.util.DerValue; -import sun.security.util.ECUtil; - -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 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 = 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 setKeyEntry, - * or by a call to setEntry with a - * PrivateKeyEntry or SecretKeyEntry. - * - * @param alias the alias name - * @param password the password, which must be null - * - * @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 | KeyStoreException e) { - throw new ProviderException(e); - } 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 setKeyEntry, - * or by a call to setEntry with a - * PrivateKeyEntry. - * - * @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. - * - *

If the given alias name identifies an entry - * created by a call to setCertificateEntry, - * or created by a call to setEntry with a - * TrustedCertificateEntry, - * then the trusted certificate contained in that entry is returned. - * - *

If the given alias name identifies an entry - * created by a call to setKeyEntry, - * or created by a call to setEntry with a - * PrivateKeyEntry, - * 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. - * - *

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 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 | IllegalArgumentException e) { - throw new KeyStoreException(e); - } - engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password)); - } - - /** - * 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 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 { - token.ensureValid(); - throw new ProviderException(new UnsupportedOperationException()); - } - - /** - * Assigns the given certificate to the given alias. - * - *

If the given alias identifies an existing entry - * created by a call to setCertificateEntry, - * or created by a call to setEntry with a - * TrustedCertificateEntry, - * the trusted certificate in the existing entry - * 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 an entry containing a trusted certificate, - * or this operation fails for some other reason. - */ - public synchronized void engineSetCertificateEntry - (String alias, Certificate cert) throws KeyStoreException { - - token.ensureValid(); - checkWrite(); - - if (cert == null) { - throw new KeyStoreException("invalid null certificate"); - } - - KeyStore.Entry entry = null; - entry = new KeyStore.TrustedCertificateEntry(cert); - engineSetEntry(alias, entry, null); - } - - /** - * 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 synchronized void engineDeleteEntry(String alias) - throws KeyStoreException { - token.ensureValid(); - - if (token.isWriteProtected()) { - throw new KeyStoreException("token write-protected"); - } - checkWrite(); - deleteEntry(alias); - } - - /** - * XXX - not sure whether to keep this - */ - private boolean deleteEntry(String alias) throws KeyStoreException { - AliasInfo aliasInfo = aliasMap.get(alias); - if (aliasInfo != null) { - - aliasMap.remove(alias); - - try { - if (aliasInfo.type == ATTR_CLASS_CERT) { - // trusted certificate entry - return destroyCert(aliasInfo.id); - } else if (aliasInfo.type == ATTR_CLASS_PKEY) { - // private key entry - return destroyPkey(aliasInfo.id) && - destroyChain(aliasInfo.id); - } else if (aliasInfo.type == ATTR_CLASS_SKEY) { - // secret key entry - return destroySkey(alias); - } else { - throw new KeyStoreException("unexpected entry type"); - } - } catch (PKCS11Exception | CertificateException e) { - throw new KeyStoreException(e); - } - } - return false; - } - - /** - * Lists all the alias names of this keystore. - * - * @return enumeration of the alias names - */ - public synchronized Enumeration engineAliases() { - token.ensureValid(); - - // don't want returned enumeration to iterate off actual keySet - - // otherwise applications that iterate and modify the keystore - // may run into concurrent modification problems - return Collections.enumeration(new HashSet(aliasMap.keySet())); - } - - /** - * Checks if the given alias exists in this keystore. - * - * @param alias the alias name - * - * @return true if the alias exists, false otherwise - */ - public synchronized boolean engineContainsAlias(String alias) { - token.ensureValid(); - return aliasMap.containsKey(alias); - } - - /** - * Retrieves the number of entries in this keystore. - * - * @return the number of entries in this keystore - */ - public synchronized int engineSize() { - token.ensureValid(); - return aliasMap.size(); - } - - /** - * Returns true if the entry identified by the given alias - * was created by a call to setKeyEntry, - * or created by a call to setEntry with a - * PrivateKeyEntry or a SecretKeyEntry. - * - * @param alias the alias for the keystore entry to be checked - * - * @return true if the entry identified by the given alias is a - * key-related, false otherwise. - */ - public synchronized boolean engineIsKeyEntry(String alias) { - token.ensureValid(); - - AliasInfo aliasInfo = aliasMap.get(alias); - if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) { - return false; - } - return true; - } - - /** - * Returns true if the entry identified by the given alias - * was created by a call to setCertificateEntry, - * or created by a call to setEntry with a - * TrustedCertificateEntry. - * - * @param alias the alias for the keystore entry to be checked - * - * @return true if the entry identified by the given alias contains a - * trusted certificate, false otherwise. - */ - public synchronized boolean engineIsCertificateEntry(String alias) { - token.ensureValid(); - - AliasInfo aliasInfo = aliasMap.get(alias); - if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) { - return false; - } - return true; - } - - /** - * 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 was - * created by a call to setCertificateEntry, - * or created by a call to setEntry with a - * TrustedCertificateEntry, - * then the given certificate is compared to that entry's certificate. - * - *

If the entry being considered was - * created by a call to setKeyEntry, - * or created by a call to setEntry with a - * PrivateKeyEntry, - * then the given certificate is compared to the first - * element of that entry's certificate chain. - * - * @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 synchronized String engineGetCertificateAlias(Certificate cert) { - token.ensureValid(); - Enumeration e = engineAliases(); - while (e.hasMoreElements()) { - String alias = e.nextElement(); - Certificate tokenCert = engineGetCertificate(alias); - if (tokenCert != null && tokenCert.equals(cert)) { - return alias; - } - } - return null; - } - - /** - * engineStore currently is a No-op. - * Entries are stored to the token during engineSetEntry - * - * @param stream this must be null - * @param password this must be null - */ - public synchronized void engineStore(OutputStream stream, char[] password) - throws IOException, NoSuchAlgorithmException, CertificateException { - token.ensureValid(); - if (stream != null && !token.config.getKeyStoreCompatibilityMode()) { - throw new IOException("output stream must be null"); - } - - if (password != null && !token.config.getKeyStoreCompatibilityMode()) { - throw new IOException("password must be null"); - } - } - - /** - * engineStore currently is a No-op. - * Entries are stored to the token during engineSetEntry - * - * @param param this must be null - * - * @exception IllegalArgumentException if the given - * KeyStore.LoadStoreParameter - * input is not null - */ - public synchronized void engineStore(KeyStore.LoadStoreParameter param) - throws IOException, NoSuchAlgorithmException, CertificateException { - token.ensureValid(); - if (param != null) { - throw new IllegalArgumentException - ("LoadStoreParameter must be null"); - } - } - - /** - * Loads the keystore. - * - * @param stream the input stream, which must be null - * @param password the password used to unlock the keystore, - * or null if the token supports a - * CKF_PROTECTED_AUTHENTICATION_PATH - * - * @exception IOException if the given stream is not - * null, if the token supports a - * CKF_PROTECTED_AUTHENTICATION_PATH and a non-null - * password is given, of if the token login operation failed - */ - public synchronized void engineLoad(InputStream stream, char[] password) - throws IOException, NoSuchAlgorithmException, CertificateException { - - token.ensureValid(); - - if (NSS_TEST) { - ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false); - } - - if (stream != null && !token.config.getKeyStoreCompatibilityMode()) { - throw new IOException("input stream must be null"); - } - - if (useSecmodTrust) { - nssTrustType = Secmod.TrustType.ALL; - } - - try { - if (password == null) { - login(null); - } else { - login(new PasswordCallbackHandler(password)); - } - } catch(LoginException e) { - Throwable cause = e.getCause(); - if (cause instanceof PKCS11Exception) { - PKCS11Exception pe = (PKCS11Exception) cause; - if (pe.getErrorCode() == CKR_PIN_INCORRECT) { - // if password is wrong, the cause of the IOException - // should be an UnrecoverableKeyException - throw new IOException("load failed", - new UnrecoverableKeyException().initCause(e)); - } - } - throw new IOException("load failed", e); - } - - try { - if (mapLabels() == true) { - // CKA_LABELs are shared by multiple certs - writeDisabled = true; - } - if (debug != null) { - dumpTokenMap(); - } - } catch (KeyStoreException | PKCS11Exception e) { - throw new IOException("load failed", e); - } - } - - /** - * Loads the keystore using the given - * KeyStore.LoadStoreParameter. - * - *

The LoadStoreParameter.getProtectionParameter() - * method is expected to return a KeyStore.PasswordProtection - * object. The password is retrieved from that object and used - * to unlock the PKCS#11 token. - * - *

If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH - * then the provided password must be null. - * - * @param param the KeyStore.LoadStoreParameter - * - * @exception IllegalArgumentException if the given - * KeyStore.LoadStoreParameter is null, - * or if that parameter returns a null - * ProtectionParameter object. - * input is not recognized - * @exception IOException if the token supports a - * CKF_PROTECTED_AUTHENTICATION_PATH and the provided password - * is non-null, or if the token login operation fails - */ - public synchronized void engineLoad(KeyStore.LoadStoreParameter param) - throws IOException, NoSuchAlgorithmException, - CertificateException { - - token.ensureValid(); - - if (NSS_TEST) { - ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false); - } - - // if caller wants to pass a NULL password, - // force it to pass a non-NULL PasswordProtection that returns - // a NULL password - - if (param == null) { - throw new IllegalArgumentException - ("invalid null LoadStoreParameter"); - } - if (useSecmodTrust) { - if (param instanceof Secmod.KeyStoreLoadParameter) { - nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType(); - } else { - nssTrustType = Secmod.TrustType.ALL; - } - } - - CallbackHandler handler; - KeyStore.ProtectionParameter pp = param.getProtectionParameter(); - if (pp instanceof PasswordProtection) { - char[] password = ((PasswordProtection)pp).getPassword(); - if (password == null) { - handler = null; - } else { - handler = new PasswordCallbackHandler(password); - } - } else if (pp instanceof CallbackHandlerProtection) { - handler = ((CallbackHandlerProtection)pp).getCallbackHandler(); - } else { - throw new IllegalArgumentException - ("ProtectionParameter must be either " + - "PasswordProtection or CallbackHandlerProtection"); - } - - try { - login(handler); - if (mapLabels() == true) { - // CKA_LABELs are shared by multiple certs - writeDisabled = true; - } - if (debug != null) { - dumpTokenMap(); - } - } catch (LoginException | KeyStoreException | PKCS11Exception e) { - throw new IOException("load failed", e); - } - } - - private void login(CallbackHandler handler) throws LoginException { - if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) { - token.provider.login(null, handler); - } else { - // token supports protected authentication path - // (external pin-pad, for example) - if (handler != null && - !token.config.getKeyStoreCompatibilityMode()) { - throw new LoginException("can not specify password if token " + - "supports protected authentication path"); - } - - // must rely on application-set or default handler - // if one is necessary - token.provider.login(null, null); - } - } - - /** - * Get a KeyStore.Entry for the specified alias - * - * @param alias get the KeyStore.Entry for this alias - * @param protParam this must be null - * - * @return the KeyStore.Entry for the specified alias, - * or null if there is no such entry - * - * @exception KeyStoreException if the operation failed - * @exception NoSuchAlgorithmException if the algorithm for recovering the - * entry cannot be found - * @exception UnrecoverableEntryException if the specified - * protParam were insufficient or invalid - * - * @since 1.5 - */ - public synchronized KeyStore.Entry engineGetEntry(String alias, - KeyStore.ProtectionParameter protParam) - throws KeyStoreException, NoSuchAlgorithmException, - UnrecoverableEntryException { - - token.ensureValid(); - - if (protParam != null && - protParam instanceof KeyStore.PasswordProtection && - ((KeyStore.PasswordProtection)protParam).getPassword() != null && - !token.config.getKeyStoreCompatibilityMode()) { - throw new KeyStoreException("ProtectionParameter must be null"); - } - - AliasInfo aliasInfo = aliasMap.get(alias); - if (aliasInfo == null) { - if (debug != null) { - debug.println("engineGetEntry did not find alias [" + - alias + - "] in map"); - } - return null; - } - - Session session = null; - try { - session = token.getOpSession(); - - if (aliasInfo.type == ATTR_CLASS_CERT) { - // trusted certificate entry - if (debug != null) { - debug.println("engineGetEntry found trusted cert entry"); - } - return new KeyStore.TrustedCertificateEntry(aliasInfo.cert); - } else if (aliasInfo.type == ATTR_CLASS_SKEY) { - // secret key entry - if (debug != null) { - debug.println("engineGetEntry found secret key entry"); - } - - THandle h = getTokenObject - (session, ATTR_CLASS_SKEY, null, aliasInfo.label); - if (h.type != ATTR_CLASS_SKEY) { - throw new KeyStoreException - ("expected but could not find secret key"); - } else { - SecretKey skey = loadSkey(session, h.handle); - return new KeyStore.SecretKeyEntry(skey); - } - } else { - // private key entry - if (debug != null) { - debug.println("engineGetEntry found private key entry"); - } - - THandle h = getTokenObject - (session, ATTR_CLASS_PKEY, aliasInfo.id, null); - if (h.type != ATTR_CLASS_PKEY) { - throw new KeyStoreException - ("expected but could not find private key"); - } else { - PrivateKey pkey = loadPkey(session, h.handle); - Certificate[] chain = aliasInfo.chain; - if ((pkey != null) && (chain != null)) { - return new KeyStore.PrivateKeyEntry(pkey, chain); - } else { - if (debug != null) { - debug.println - ("engineGetEntry got null cert chain or private key"); - } - } - } - } - return null; - } catch (PKCS11Exception pe) { - throw new KeyStoreException(pe); - } finally { - token.releaseSession(session); - } - } - - /** - * Save a KeyStore.Entry under the specified alias. - * - *

If an entry already exists for the specified alias, - * it is overridden. - * - *

This KeyStore implementation only supports the standard - * entry types, and only supports X509Certificates in - * TrustedCertificateEntries. Also, this implementation does not support - * protecting entries using a different password - * from the one used for token login. - * - *

Entries are immediately stored on the token. - * - * @param alias save the KeyStore.Entry under this alias - * @param entry the Entry to save - * @param protParam this must be null - * - * @exception KeyStoreException if this operation fails - * - * @since 1.5 - */ - public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, - KeyStore.ProtectionParameter protParam) - throws KeyStoreException { - - token.ensureValid(); - checkWrite(); - - if (protParam != null && - protParam instanceof KeyStore.PasswordProtection && - ((KeyStore.PasswordProtection)protParam).getPassword() != null && - !token.config.getKeyStoreCompatibilityMode()) { - throw new KeyStoreException(new UnsupportedOperationException - ("ProtectionParameter must be null")); - } - - if (token.isWriteProtected()) { - throw new KeyStoreException("token write-protected"); - } - - if (entry instanceof KeyStore.TrustedCertificateEntry) { - - if (useSecmodTrust == false) { - // PKCS #11 does not allow app to modify trusted certs - - throw new KeyStoreException(new UnsupportedOperationException - ("trusted certificates may only be set by " + - "token initialization application")); - } - Module module = token.provider.nssModule; - if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) { - // XXX allow TRUSTANCHOR module - throw new KeyStoreException("Trusted certificates can only be " - + "added to the NSS KeyStore module"); - } - Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate(); - if (cert instanceof X509Certificate == false) { - throw new KeyStoreException("Certificate must be an X509Certificate"); - } - X509Certificate xcert = (X509Certificate)cert; - AliasInfo info = aliasMap.get(alias); - if (info != null) { - // XXX try to update - deleteEntry(alias); - } - try { - storeCert(alias, xcert); - module.setTrust(token, xcert); - mapLabels(); - } catch (PKCS11Exception | CertificateException e) { - throw new KeyStoreException(e); - } - - } else { - - if (entry instanceof KeyStore.PrivateKeyEntry) { - - PrivateKey key = - ((KeyStore.PrivateKeyEntry)entry).getPrivateKey(); - if (!(key instanceof P11Key) && - !(key instanceof RSAPrivateKey) && - !(key instanceof DSAPrivateKey) && - !(key instanceof DHPrivateKey) && - !(key instanceof ECPrivateKey)) { - throw new KeyStoreException("unsupported key type: " + - key.getClass().getName()); - } - - // only support X509Certificate chains - Certificate[] chain = - ((KeyStore.PrivateKeyEntry)entry).getCertificateChain(); - if (!(chain instanceof X509Certificate[])) { - throw new KeyStoreException - (new UnsupportedOperationException - ("unsupported certificate array type: " + - chain.getClass().getName())); - } - - try { - boolean updatedAlias = false; - Set aliases = aliasMap.keySet(); - for (String oldAlias : aliases) { - - // see if there's an existing entry with the same info - - AliasInfo aliasInfo = aliasMap.get(oldAlias); - if (aliasInfo.type == ATTR_CLASS_PKEY && - aliasInfo.cert.getPublicKey().equals - (chain[0].getPublicKey())) { - - // found existing entry - - // caller is renaming entry or updating cert chain - // - // set new CKA_LABEL/CKA_ID - // and update certs if necessary - - updatePkey(alias, - aliasInfo.id, - (X509Certificate[])chain, - !aliasInfo.cert.equals(chain[0])); - updatedAlias = true; - break; - } - } - - if (!updatedAlias) { - // caller adding new entry - engineDeleteEntry(alias); - storePkey(alias, (KeyStore.PrivateKeyEntry)entry); - } - - } catch (PKCS11Exception | CertificateException pe) { - throw new KeyStoreException(pe); - } - - } else if (entry instanceof KeyStore.SecretKeyEntry) { - - KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; - SecretKey skey = ske.getSecretKey(); - - try { - // first check if the key already exists - AliasInfo aliasInfo = aliasMap.get(alias); - - if (aliasInfo != null) { - engineDeleteEntry(alias); - } - storeSkey(alias, ske); - - } catch (PKCS11Exception pe) { - throw new KeyStoreException(pe); - } - - } else { - throw new KeyStoreException(new UnsupportedOperationException - ("unsupported entry type: " + entry.getClass().getName())); - } - - try { - - // XXX NSS does not write out the CKA_ID we pass to them - // - // therefore we must re-map labels - // (can not simply update aliasMap) - - mapLabels(); - if (debug != null) { - dumpTokenMap(); - } - } catch (PKCS11Exception | CertificateException pe) { - throw new KeyStoreException(pe); - } - } - - if (debug != null) { - debug.println - ("engineSetEntry added new entry for [" + - alias + - "] to token"); - } - } - - /** - * Determines if the keystore Entry for the specified - * alias is an instance or subclass of the specified - * entryClass. - * - * @param alias the alias name - * @param entryClass the entry class - * - * @return true if the keystore Entry for the specified - * alias is an instance or subclass of the - * specified entryClass, false otherwise - */ - public synchronized boolean engineEntryInstanceOf - (String alias, Class entryClass) { - token.ensureValid(); - return super.engineEntryInstanceOf(alias, entryClass); - } - - private X509Certificate loadCert(Session session, long oHandle) - throws PKCS11Exception, CertificateException { - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] - { new CK_ATTRIBUTE(CKA_VALUE) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - - byte[] bytes = attrs[0].getByteArray(); - if (bytes == null) { - throw new CertificateException - ("unexpectedly retrieved null byte array"); - } - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return (X509Certificate)cf.generateCertificate - (new ByteArrayInputStream(bytes)); - } - - private X509Certificate[] loadChain(Session session, - X509Certificate endCert) - throws PKCS11Exception, CertificateException { - - ArrayList lChain = null; - - if (endCert.getSubjectX500Principal().equals - (endCert.getIssuerX500Principal())) { - // self signed - return new X509Certificate[] { endCert }; - } else { - lChain = new ArrayList(); - lChain.add(endCert); - } - - // try loading remaining certs in chain by following - // issuer->subject links - - X509Certificate next = endCert; - while (true) { - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_CERT, - new CK_ATTRIBUTE(CKA_SUBJECT, - next.getIssuerX500Principal().getEncoded()) }; - long[] ch = findObjects(session, attrs); - - if (ch == null || ch.length == 0) { - // done - break; - } else { - // if more than one found, use first - if (debug != null && ch.length > 1) { - debug.println("engineGetEntry found " + - ch.length + - " certificate entries for subject [" + - next.getIssuerX500Principal().toString() + - "] in token - using first entry"); - } - - next = loadCert(session, ch[0]); - lChain.add(next); - if (next.getSubjectX500Principal().equals - (next.getIssuerX500Principal())) { - // self signed - break; - } - } - } - - return lChain.toArray(new X509Certificate[lChain.size()]); - } - - private SecretKey loadSkey(Session session, long oHandle) - throws PKCS11Exception { - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_KEY_TYPE) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - long kType = attrs[0].getLong(); - - String keyType = null; - int keyLength = -1; - - // XXX NSS mangles the stored key type for secret key token objects - - if (kType == CKK_DES || kType == CKK_DES3) { - if (kType == CKK_DES) { - keyType = "DES"; - keyLength = 64; - } else if (kType == CKK_DES3) { - keyType = "DESede"; - keyLength = 192; - } - } else { - if (kType == CKK_AES) { - keyType = "AES"; - } else if (kType == CKK_BLOWFISH) { - keyType = "Blowfish"; - } else if (kType == CKK_RC4) { - keyType = "ARCFOUR"; - } else { - if (debug != null) { - debug.println("unknown key type [" + - kType + - "] - using 'Generic Secret'"); - } - keyType = "Generic Secret"; - } - - // XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID? - if (NSS_TEST) { - keyLength = 128; - } else { - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - keyLength = (int)attrs[0].getLong(); - } - } - - return P11Key.secretKey(session, oHandle, keyType, keyLength, null); - } - - private PrivateKey loadPkey(Session session, long oHandle) - throws PKCS11Exception, KeyStoreException { - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_KEY_TYPE) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - long kType = attrs[0].getLong(); - String keyType = null; - int keyLength = 0; - - if (kType == CKK_RSA) { - - keyType = "RSA"; - - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - BigInteger modulus = attrs[0].getBigInteger(); - keyLength = modulus.bitLength(); - - // This check will combine our "don't care" values here - // with the system-wide min/max values. - try { - RSAKeyFactory.checkKeyLengths(keyLength, null, - -1, Integer.MAX_VALUE); - } catch (InvalidKeyException e) { - throw new KeyStoreException(e.getMessage()); - } - - return P11Key.privateKey(session, - oHandle, - keyType, - keyLength, - null); - - } else if (kType == CKK_DSA) { - - keyType = "DSA"; - - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - BigInteger prime = attrs[0].getBigInteger(); - keyLength = prime.bitLength(); - - return P11Key.privateKey(session, - oHandle, - keyType, - keyLength, - null); - - } else if (kType == CKK_DH) { - - keyType = "DH"; - - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - BigInteger prime = attrs[0].getBigInteger(); - keyLength = prime.bitLength(); - - return P11Key.privateKey(session, - oHandle, - keyType, - keyLength, - null); - - } else if (kType == CKK_EC) { - - attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_EC_PARAMS), - }; - token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); - byte[] encodedParams = attrs[0].getByteArray(); - try { - ECParameterSpec params = - ECUtil.getECParameterSpec(null, encodedParams); - keyLength = params.getCurve().getField().getFieldSize(); - } catch (IOException e) { - // we do not want to accept key with unsupported parameters - throw new KeyStoreException("Unsupported parameters", e); - } - - return P11Key.privateKey(session, oHandle, "EC", keyLength, null); - - } else { - if (debug != null) { - debug.println("unknown key type [" + kType + "]"); - } - throw new KeyStoreException("unknown key type"); - } - } - - - /** - * XXX On ibutton, when you C_SetAttribute(CKA_ID) for a private key - * it not only changes the CKA_ID of the private key, - * it changes the CKA_ID of the corresponding cert too. - * And vice versa. - * - * XXX On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID) - * for a private key, and then try to delete the corresponding cert. - * So this code reverses the order. - * After the cert is first destroyed (if necessary), - * then the CKA_ID of the private key can be changed successfully. - * - * @param replaceCert if true, then caller is updating alias info for - * existing cert (only update CKA_ID/CKA_LABEL). - * if false, then caller is updating cert chain - * (delete old end cert and add new chain). - */ - private void updatePkey(String alias, - byte[] cka_id, - X509Certificate[] chain, - boolean replaceCert) throws - KeyStoreException, CertificateException, PKCS11Exception { - - // XXX - // - // always set replaceCert to true - // - // NSS does not allow resetting of CKA_LABEL on an existing cert - // (C_SetAttribute call succeeds, but is ignored) - - replaceCert = true; - - Session session = null; - try { - session = token.getOpSession(); - - // first get private key object handle and hang onto it - - THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null); - long pKeyHandle; - if (h.type == ATTR_CLASS_PKEY) { - pKeyHandle = h.handle; - } else { - throw new KeyStoreException - ("expected but could not find private key " + - "with CKA_ID " + - getID(cka_id)); - } - - // next find existing end entity cert - - h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); - if (h.type != ATTR_CLASS_CERT) { - throw new KeyStoreException - ("expected but could not find certificate " + - "with CKA_ID " + - getID(cka_id)); - } else { - if (replaceCert) { - // replacing existing cert and chain - destroyChain(cka_id); - } else { - // renaming alias for existing cert - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_LABEL, alias), - new CK_ATTRIBUTE(CKA_ID, alias) }; - token.p11.C_SetAttributeValue - (session.id(), h.handle, attrs); - } - } - - // add new chain - - if (replaceCert) { - // add all certs in chain - storeChain(alias, chain); - } else { - // already updated alias info for existing end cert - - // just update CA certs - storeCaCerts(chain, 1); - } - - // finally update CKA_ID for private key - // - // ibutton may have already done this (that is ok) - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_ID, alias) }; - token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs); - - if (debug != null) { - debug.println("updatePkey set new alias [" + - alias + - "] for private key entry"); - } - } finally { - token.releaseSession(session); - } - } - - private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key) - throws PKCS11Exception { - - // if token key, update alias. - // if session key, convert to token key. - - Session session = null; - try { - session = token.getOpSession(); - if (key.tokenObject == true) { - - // token key - set new CKA_ID - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_ID, alias) }; - token.p11.C_SetAttributeValue - (session.id(), key.keyID, attrs); - if (debug != null) { - debug.println("updateP11Pkey set new alias [" + - alias + - "] for key entry"); - } - } else { - - // session key - convert to token key and set CKA_ID - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - new CK_ATTRIBUTE(CKA_ID, alias), - }; - if (attribute != null) { - attrs = addAttribute(attrs, attribute); - } - token.p11.C_CopyObject(session.id(), key.keyID, attrs); - if (debug != null) { - debug.println("updateP11Pkey copied private session key " + - "for [" + - alias + - "] to token entry"); - } - } - } finally { - token.releaseSession(session); - } - } - - private void storeCert(String alias, X509Certificate cert) - throws PKCS11Exception, CertificateException { - - ArrayList attrList = new ArrayList(); - attrList.add(ATTR_TOKEN_TRUE); - attrList.add(ATTR_CLASS_CERT); - attrList.add(ATTR_X509_CERT_TYPE); - attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT, - cert.getSubjectX500Principal().getEncoded())); - attrList.add(new CK_ATTRIBUTE(CKA_ISSUER, - cert.getIssuerX500Principal().getEncoded())); - attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER, - cert.getSerialNumber().toByteArray())); - attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded())); - - if (alias != null) { - attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias)); - attrList.add(new CK_ATTRIBUTE(CKA_ID, alias)); - } else { - // ibutton requires something to be set - // - alias must be unique - attrList.add(new CK_ATTRIBUTE(CKA_ID, - getID(cert.getSubjectX500Principal().getName - (X500Principal.CANONICAL), cert))); - } - - Session session = null; - try { - session = token.getOpSession(); - token.p11.C_CreateObject(session.id(), - attrList.toArray(new CK_ATTRIBUTE[attrList.size()])); - } finally { - token.releaseSession(session); - } - } - - private void storeChain(String alias, X509Certificate[] chain) - throws PKCS11Exception, CertificateException { - - // add new chain - // - // end cert has CKA_LABEL and CKA_ID set to alias. - // other certs in chain have neither set. - - storeCert(alias, chain[0]); - storeCaCerts(chain, 1); - } - - private void storeCaCerts(X509Certificate[] chain, int start) - throws PKCS11Exception, CertificateException { - - // do not add duplicate CA cert if already in token - // - // XXX ibutton stores duplicate CA certs, NSS does not - - Session session = null; - HashSet cacerts = new HashSet(); - try { - session = token.getOpSession(); - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_CERT }; - long[] handles = findObjects(session, attrs); - - // load certs currently on the token - for (long handle : handles) { - cacerts.add(loadCert(session, handle)); - } - } finally { - token.releaseSession(session); - } - - for (int i = start; i < chain.length; i++) { - if (!cacerts.contains(chain[i])) { - storeCert(null, chain[i]); - } else if (debug != null) { - debug.println("ignoring duplicate CA cert for [" + - chain[i].getSubjectX500Principal() + - "]"); - } - } - } - - private void storeSkey(String alias, KeyStore.SecretKeyEntry ske) - throws PKCS11Exception, KeyStoreException { - - SecretKey skey = ske.getSecretKey(); - // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since - // they are handled in P11SecretKeyFactory.createKey() method. - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_SKEY_TOKEN_TRUE, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_LABEL, alias), - }; - try { - P11SecretKeyFactory.convertKey(token, skey, null, attrs); - } catch (InvalidKeyException ike) { - // re-throw KeyStoreException to match javadoc - throw new KeyStoreException("Cannot convert to PKCS11 keys", ike); - } - - // update global alias map - aliasMap.put(alias, new AliasInfo(alias)); - - if (debug != null) { - debug.println("storeSkey created token secret key for [" + - alias + "]"); - } - } - - private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) { - int n = attrs.length; - CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1]; - System.arraycopy(attrs, 0, newAttrs, 0, n); - newAttrs[n] = attr; - return newAttrs; - } - - private void storePkey(String alias, KeyStore.PrivateKeyEntry pke) - throws PKCS11Exception, CertificateException, KeyStoreException { - - PrivateKey key = pke.getPrivateKey(); - CK_ATTRIBUTE[] attrs = null; - - // If the key is a token object on this token, update it instead - // of creating a duplicate key object. - // Otherwise, treat a P11Key like any other key, if it is extractable. - if (key instanceof P11Key) { - P11Key p11Key = (P11Key)key; - if (p11Key.tokenObject && (p11Key.token == this.token)) { - updateP11Pkey(alias, null, p11Key); - storeChain(alias, (X509Certificate[])pke.getCertificateChain()); - return; - } - } - - boolean useNDB = token.config.getNssNetscapeDbWorkaround(); - PublicKey publicKey = pke.getCertificate().getPublicKey(); - - if (key instanceof RSAPrivateKey) { - - X509Certificate cert = (X509Certificate)pke.getCertificate(); - attrs = getRsaPrivKeyAttrs - (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal()); - - } else if (key instanceof DSAPrivateKey) { - - DSAPrivateKey dsaKey = (DSAPrivateKey)key; - - CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); - if (idAttrs[0] == null) { - idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); - } - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA), - idAttrs[0], - new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()), - new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()), - new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()), - new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()), - }; - if (idAttrs[1] != null) { - attrs = addAttribute(attrs, idAttrs[1]); - } - - attrs = token.getAttributes - (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs); - - if (debug != null) { - debug.println("storePkey created DSA template"); - } - - } else if (key instanceof DHPrivateKey) { - - DHPrivateKey dhKey = (DHPrivateKey)key; - - CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); - if (idAttrs[0] == null) { - idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); - } - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH), - idAttrs[0], - new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()), - new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()), - new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()), - }; - if (idAttrs[1] != null) { - attrs = addAttribute(attrs, idAttrs[1]); - } - - attrs = token.getAttributes - (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs); - - } else if (key instanceof ECPrivateKey) { - - ECPrivateKey ecKey = (ECPrivateKey)key; - - CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); - if (idAttrs[0] == null) { - idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); - } - - byte[] encodedParams = - ECUtil.encodeECParameterSpec(null, ecKey.getParams()); - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC), - idAttrs[0], - new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()), - new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), - }; - if (idAttrs[1] != null) { - attrs = addAttribute(attrs, idAttrs[1]); - } - - attrs = token.getAttributes - (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs); - - if (debug != null) { - debug.println("storePkey created EC template"); - } - - } else if (key instanceof P11Key) { - // sensitive/non-extractable P11Key - P11Key p11Key = (P11Key)key; - if (p11Key.token != this.token) { - throw new KeyStoreException - ("Cannot move sensitive keys across tokens"); - } - CK_ATTRIBUTE netscapeDB = null; - if (useNDB) { - // Note that this currently fails due to an NSS bug. - // They do not allow the CKA_NETSCAPE_DB attribute to be - // specified during C_CopyObject() and fail with - // CKR_ATTRIBUTE_READ_ONLY. - // But if we did not specify it, they would fail with - // CKA_TEMPLATE_INCOMPLETE, so leave this code in here. - CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true); - netscapeDB = idAttrs[1]; - } - // Update the key object. - updateP11Pkey(alias, netscapeDB, p11Key); - storeChain(alias, (X509Certificate[])pke.getCertificateChain()); - return; - - } else { - throw new KeyStoreException("unsupported key type: " + key); - } - - Session session = null; - try { - session = token.getOpSession(); - - // create private key entry - token.p11.C_CreateObject(session.id(), attrs); - if (debug != null) { - debug.println("storePkey created token key for [" + - alias + - "]"); - } - } finally { - token.releaseSession(session); - } - - storeChain(alias, (X509Certificate[])pke.getCertificateChain()); - } - - private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias, - RSAPrivateKey key, - X500Principal subject) throws PKCS11Exception { - - // subject is currently ignored - could be used to set CKA_SUBJECT - - CK_ATTRIBUTE[] attrs = null; - if (key instanceof RSAPrivateCrtKey) { - - if (debug != null) { - debug.println("creating RSAPrivateCrtKey attrs"); - } - - RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key; - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), - new CK_ATTRIBUTE(CKA_ID, alias), - new CK_ATTRIBUTE(CKA_MODULUS, - rsaKey.getModulus()), - new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, - rsaKey.getPrivateExponent()), - new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, - rsaKey.getPublicExponent()), - new CK_ATTRIBUTE(CKA_PRIME_1, - rsaKey.getPrimeP()), - new CK_ATTRIBUTE(CKA_PRIME_2, - rsaKey.getPrimeQ()), - new CK_ATTRIBUTE(CKA_EXPONENT_1, - rsaKey.getPrimeExponentP()), - new CK_ATTRIBUTE(CKA_EXPONENT_2, - rsaKey.getPrimeExponentQ()), - new CK_ATTRIBUTE(CKA_COEFFICIENT, - rsaKey.getCrtCoefficient()) }; - attrs = token.getAttributes - (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs); - - } else { - - if (debug != null) { - debug.println("creating RSAPrivateKey attrs"); - } - - RSAPrivateKey rsaKey = key; - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - ATTR_PRIVATE_TRUE, - new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), - new CK_ATTRIBUTE(CKA_ID, alias), - new CK_ATTRIBUTE(CKA_MODULUS, - rsaKey.getModulus()), - new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, - rsaKey.getPrivateExponent()) }; - attrs = token.getAttributes - (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs); - } - - return attrs; - } - - /** - * Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be - * used for this private key. It uses the same algorithm to calculate the - * values as NSS. The public and private keys MUST match for the result to - * be correct. - * - * It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB - * at index 1. The boolean flags determine what is to be calculated. - * If false or if we could not calculate the value, that element is null. - * - * NOTE that we currently do not use the CKA_ID value calculated by this - * method. - */ - private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey, - PublicKey publicKey, boolean id, boolean netscapeDb) { - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2]; - if ((id || netscapeDb) == false) { - return attrs; - } - String alg = privateKey.getAlgorithm(); - if (id && alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) { - // CKA_NETSCAPE_DB not needed for RSA public keys - BigInteger n = ((RSAPublicKey)publicKey).getModulus(); - attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n))); - } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) { - BigInteger y = ((DSAPublicKey)publicKey).getY(); - if (id) { - attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y))); - } - if (netscapeDb) { - attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y); - } - } else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) { - BigInteger y = ((DHPublicKey)publicKey).getY(); - if (id) { - attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y))); - } - if (netscapeDb) { - attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y); - } - } else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) { - ECPublicKey ecPub = (ECPublicKey)publicKey; - ECPoint point = ecPub.getW(); - ECParameterSpec params = ecPub.getParams(); - byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve()); - if (id) { - attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint)); - } - if (netscapeDb) { - attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint); - } - } else { - throw new RuntimeException("Unknown key algorithm " + alg); - } - return attrs; - } - - /** - * return true if cert destroyed - */ - private boolean destroyCert(byte[] cka_id) - throws PKCS11Exception, KeyStoreException { - Session session = null; - try { - session = token.getOpSession(); - THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); - if (h.type != ATTR_CLASS_CERT) { - return false; - } - - token.p11.C_DestroyObject(session.id(), h.handle); - if (debug != null) { - debug.println("destroyCert destroyed cert with CKA_ID [" + - getID(cka_id) + - "]"); - } - return true; - } finally { - token.releaseSession(session); - } - } - - /** - * return true if chain destroyed - */ - private boolean destroyChain(byte[] cka_id) - throws PKCS11Exception, CertificateException, KeyStoreException { - - Session session = null; - try { - session = token.getOpSession(); - - THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); - if (h.type != ATTR_CLASS_CERT) { - if (debug != null) { - debug.println("destroyChain could not find " + - "end entity cert with CKA_ID [0x" + - Functions.toHexString(cka_id) + - "]"); - } - return false; - } - - X509Certificate endCert = loadCert(session, h.handle); - token.p11.C_DestroyObject(session.id(), h.handle); - if (debug != null) { - debug.println("destroyChain destroyed end entity cert " + - "with CKA_ID [" + - getID(cka_id) + - "]"); - } - - // build chain following issuer->subject links - - X509Certificate next = endCert; - while (true) { - - if (next.getSubjectX500Principal().equals - (next.getIssuerX500Principal())) { - // self signed - done - break; - } - - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_CERT, - new CK_ATTRIBUTE(CKA_SUBJECT, - next.getIssuerX500Principal().getEncoded()) }; - long[] ch = findObjects(session, attrs); - - if (ch == null || ch.length == 0) { - // done - break; - } else { - // if more than one found, use first - if (debug != null && ch.length > 1) { - debug.println("destroyChain found " + - ch.length + - " certificate entries for subject [" + - next.getIssuerX500Principal() + - "] in token - using first entry"); - } - - next = loadCert(session, ch[0]); - - // only delete if not part of any other chain - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_CERT, - new CK_ATTRIBUTE(CKA_ISSUER, - next.getSubjectX500Principal().getEncoded()) }; - long[] issuers = findObjects(session, attrs); - - boolean destroyIt = false; - if (issuers == null || issuers.length == 0) { - // no other certs with this issuer - - // destroy it - destroyIt = true; - } else if (issuers.length == 1) { - X509Certificate iCert = loadCert(session, issuers[0]); - if (next.equals(iCert)) { - // only cert with issuer is itself (self-signed) - - // destroy it - destroyIt = true; - } - } - - if (destroyIt) { - token.p11.C_DestroyObject(session.id(), ch[0]); - if (debug != null) { - debug.println - ("destroyChain destroyed cert in chain " + - "with subject [" + - next.getSubjectX500Principal() + "]"); - } - } else { - if (debug != null) { - debug.println("destroyChain did not destroy " + - "shared cert in chain with subject [" + - next.getSubjectX500Principal() + "]"); - } - } - } - } - - return true; - - } finally { - token.releaseSession(session); - } - } - - /** - * return true if secret key destroyed - */ - private boolean destroySkey(String alias) - throws PKCS11Exception, KeyStoreException { - Session session = null; - try { - session = token.getOpSession(); - - THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias); - if (h.type != ATTR_CLASS_SKEY) { - if (debug != null) { - debug.println("destroySkey did not find secret key " + - "with CKA_LABEL [" + - alias + - "]"); - } - return false; - } - token.p11.C_DestroyObject(session.id(), h.handle); - return true; - } finally { - token.releaseSession(session); - } - } - - /** - * return true if private key destroyed - */ - private boolean destroyPkey(byte[] cka_id) - throws PKCS11Exception, KeyStoreException { - Session session = null; - try { - session = token.getOpSession(); - - THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null); - if (h.type != ATTR_CLASS_PKEY) { - if (debug != null) { - debug.println - ("destroyPkey did not find private key with CKA_ID [" + - getID(cka_id) + - "]"); - } - return false; - } - token.p11.C_DestroyObject(session.id(), h.handle); - return true; - } finally { - token.releaseSession(session); - } - } - - /** - * build [alias + issuer + serialNumber] string from a cert - */ - private String getID(String alias, X509Certificate cert) { - X500Principal issuer = cert.getIssuerX500Principal(); - BigInteger serialNum = cert.getSerialNumber(); - - return alias + - ALIAS_SEP + - issuer.getName(X500Principal.CANONICAL) + - ALIAS_SEP + - serialNum.toString(); - } - - /** - * build CKA_ID string from bytes - */ - private static String getID(byte[] bytes) { - boolean printable = true; - for (int i = 0; i < bytes.length; i++) { - if (!DerValue.isPrintableStringChar((char)bytes[i])) { - printable = false; - break; - } - } - - if (!printable) { - return "0x" + Functions.toHexString(bytes); - } else { - try { - return new String(bytes, "UTF-8"); - } catch (UnsupportedEncodingException uee) { - return "0x" + Functions.toHexString(bytes); - } - } - } - - /** - * find an object on the token - * - * @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY - * @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY - * @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY - */ - private THandle getTokenObject(Session session, - CK_ATTRIBUTE type, - byte[] cka_id, - String cka_label) - throws PKCS11Exception, KeyStoreException { - - CK_ATTRIBUTE[] attrs; - if (type == ATTR_CLASS_SKEY) { - attrs = new CK_ATTRIBUTE[] { - ATTR_SKEY_TOKEN_TRUE, - new CK_ATTRIBUTE(CKA_LABEL, cka_label), - type }; - } else { - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - new CK_ATTRIBUTE(CKA_ID, cka_id), - type }; - } - long[] h = findObjects(session, attrs); - if (h.length == 0) { - if (debug != null) { - if (type == ATTR_CLASS_SKEY) { - debug.println("getTokenObject did not find secret key " + - "with CKA_LABEL [" + - cka_label + - "]"); - } else if (type == ATTR_CLASS_CERT) { - debug.println - ("getTokenObject did not find cert with CKA_ID [" + - getID(cka_id) + - "]"); - } else { - debug.println("getTokenObject did not find private key " + - "with CKA_ID [" + - getID(cka_id) + - "]"); - } - } - } else if (h.length == 1) { - - // found object handle - return it - return new THandle(h[0], type); - - } else { - - // found multiple object handles - - // see if token ignored CKA_LABEL during search (e.g. NSS) - - if (type == ATTR_CLASS_SKEY) { - - ArrayList list = new ArrayList(h.length); - for (int i = 0; i < h.length; i++) { - - CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[] - { new CK_ATTRIBUTE(CKA_LABEL) }; - token.p11.C_GetAttributeValue(session.id(), h[i], label); - if (label[0].pValue != null && - cka_label.equals(new String(label[0].getCharArray()))) { - list.add(new THandle(h[i], ATTR_CLASS_SKEY)); - } - } - if (list.size() == 1) { - // yes, there was only one CKA_LABEL that matched - return list.get(0); - } else { - throw new KeyStoreException("invalid KeyStore state: " + - "found " + - list.size() + - " secret keys sharing CKA_LABEL [" + - cka_label + - "]"); - } - } else if (type == ATTR_CLASS_CERT) { - throw new KeyStoreException("invalid KeyStore state: " + - "found " + - h.length + - " certificates sharing CKA_ID " + - getID(cka_id)); - } else { - throw new KeyStoreException("invalid KeyStore state: " + - "found " + - h.length + - " private keys sharing CKA_ID " + - getID(cka_id)); - } - } - return new THandle(NO_HANDLE, null); - } - - /** - * Create a mapping of all key pairs, trusted certs, and secret keys - * on the token into logical KeyStore entries unambiguously - * accessible via an alias. - * - * If the token is removed, the map may contain stale values. - * KeyStore.load should be called to re-create the map. - * - * Assume all private keys and matching certs share a unique CKA_ID. - * - * Assume all secret keys have a unique CKA_LABEL. - * - * @return true if multiple certs found sharing the same CKA_LABEL - * (if so, write capabilities are disabled) - */ - private boolean mapLabels() throws - PKCS11Exception, CertificateException, KeyStoreException { - - CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] { - new CK_ATTRIBUTE(CKA_TRUSTED) }; - - Session session = null; - try { - session = token.getOpSession(); - - // get all private key CKA_IDs - - ArrayList pkeyIDs = new ArrayList(); - CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_PKEY, - }; - long[] handles = findObjects(session, attrs); - - for (long handle : handles) { - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) }; - token.p11.C_GetAttributeValue(session.id(), handle, attrs); - - if (attrs[0].pValue != null) { - pkeyIDs.add(attrs[0].getByteArray()); - } - } - - // Get all certificates - // - // If cert does not have a CKA_LABEL nor CKA_ID, it is ignored. - // - // Get the CKA_LABEL for each cert - // (if the cert does not have a CKA_LABEL, use the CKA_ID). - // - // Map each cert to the its CKA_LABEL - // (multiple certs may be mapped to a single CKA_LABEL) - - HashMap> certMap = - new HashMap>(); - - attrs = new CK_ATTRIBUTE[] { - ATTR_TOKEN_TRUE, - ATTR_CLASS_CERT, - }; - handles = findObjects(session, attrs); - - for (long handle : handles) { - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) }; - - String cka_label = null; - byte[] cka_id = null; - try { - token.p11.C_GetAttributeValue(session.id(), handle, attrs); - if (attrs[0].pValue != null) { - // there is a CKA_LABEL - cka_label = new String(attrs[0].getCharArray()); - } - } catch (PKCS11Exception pe) { - if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) { - throw pe; - } - - // GetAttributeValue for CKA_LABEL not supported - // - // XXX SCA1000 - } - - // get CKA_ID - - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) }; - token.p11.C_GetAttributeValue(session.id(), handle, attrs); - if (attrs[0].pValue == null) { - if (cka_label == null) { - // no cka_label nor cka_id - ignore - continue; - } - } else { - if (cka_label == null) { - // use CKA_ID as CKA_LABEL - cka_label = getID(attrs[0].getByteArray()); - } - cka_id = attrs[0].getByteArray(); - } - - X509Certificate cert = loadCert(session, handle); - - // get CKA_TRUSTED - - boolean cka_trusted = false; - - if (useSecmodTrust) { - cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType); - } else { - if (CKA_TRUSTED_SUPPORTED) { - try { - token.p11.C_GetAttributeValue - (session.id(), handle, trustedAttr); - cka_trusted = trustedAttr[0].getBoolean(); - } catch (PKCS11Exception pe) { - if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) { - // XXX NSS, ibutton, sca1000 - CKA_TRUSTED_SUPPORTED = false; - if (debug != null) { - debug.println - ("CKA_TRUSTED attribute not supported"); - } - } - } - } - } - - HashSet infoSet = certMap.get(cka_label); - if (infoSet == null) { - infoSet = new HashSet(2); - certMap.put(cka_label, infoSet); - } - - // initially create private key entry AliasInfo entries - - // these entries will get resolved into their true - // entry types later - - infoSet.add(new AliasInfo - (cka_label, - cka_id, - cka_trusted, - cert)); - } - - // create list secret key CKA_LABELS - - // if there are duplicates (either between secret keys, - // or between a secret key and another object), - // throw an exception - HashMap sKeyMap = - new HashMap(); - - attrs = new CK_ATTRIBUTE[] { - ATTR_SKEY_TOKEN_TRUE, - ATTR_CLASS_SKEY, - }; - handles = findObjects(session, attrs); - - for (long handle : handles) { - attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) }; - token.p11.C_GetAttributeValue(session.id(), handle, attrs); - if (attrs[0].pValue != null) { - - // there is a CKA_LABEL - String cka_label = new String(attrs[0].getCharArray()); - if (sKeyMap.get(cka_label) == null) { - sKeyMap.put(cka_label, new AliasInfo(cka_label)); - } else { - throw new KeyStoreException("invalid KeyStore state: " + - "found multiple secret keys sharing same " + - "CKA_LABEL [" + - cka_label + - "]"); - } - } - } - - // update global aliasMap with alias mappings - ArrayList matchedCerts = - mapPrivateKeys(pkeyIDs, certMap); - boolean sharedLabel = mapCerts(matchedCerts, certMap); - mapSecretKeys(sKeyMap); - - return sharedLabel; - - } finally { - token.releaseSession(session); - } - } - - /** - * for each private key CKA_ID, find corresponding cert with same CKA_ID. - * if found cert, see if cert CKA_LABEL is unique. - * if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL. - * if CKA_LABEL not unique, map private key/cert alias to: - * CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL - * if cert not found, ignore private key - * (don't support private key entries without a cert chain yet) - * - * @return a list of AliasInfo entries that represents all matches - */ - private ArrayList mapPrivateKeys(ArrayList pkeyIDs, - HashMap> certMap) - throws PKCS11Exception, CertificateException { - - // reset global alias map - aliasMap = new HashMap(); - - // list of matched certs that we will return - ArrayList matchedCerts = new ArrayList(); - - for (byte[] pkeyID : pkeyIDs) { - - // try to find a matching CKA_ID in a certificate - - boolean foundMatch = false; - Set certLabels = certMap.keySet(); - for (String certLabel : certLabels) { - - // get cert CKA_IDs (if present) for each cert - - HashSet infoSet = certMap.get(certLabel); - for (AliasInfo aliasInfo : infoSet) { - if (Arrays.equals(pkeyID, aliasInfo.id)) { - - // found private key with matching cert - - if (infoSet.size() == 1) { - // unique CKA_LABEL - use certLabel as alias - aliasInfo.matched = true; - aliasMap.put(certLabel, aliasInfo); - } else { - // create new alias - aliasInfo.matched = true; - aliasMap.put(getID(certLabel, aliasInfo.cert), - aliasInfo); - } - matchedCerts.add(aliasInfo); - foundMatch = true; - break; - } - } - if (foundMatch) { - break; - } - } - - if (!foundMatch) { - if (debug != null) { - debug.println - ("did not find match for private key with CKA_ID [" + - getID(pkeyID) + - "] (ignoring entry)"); - } - } - } - - return matchedCerts; - } - - /** - * for each cert not matched with a private key but is CKA_TRUSTED: - * if CKA_LABEL unique, map cert to CKA_LABEL. - * if CKA_LABEL not unique, map cert to [label+issuer+serialNum] - * - * if CKA_TRUSTED not supported, treat all certs not part of a chain - * as trusted - * - * @return true if multiple certs found sharing the same CKA_LABEL - */ - private boolean mapCerts(ArrayList matchedCerts, - HashMap> certMap) - throws PKCS11Exception, CertificateException { - - // load all cert chains - for (AliasInfo aliasInfo : matchedCerts) { - Session session = null; - try { - session = token.getOpSession(); - aliasInfo.chain = loadChain(session, aliasInfo.cert); - } finally { - token.releaseSession(session); - } - } - - // find all certs in certMap not part of a cert chain - // - these are trusted - - boolean sharedLabel = false; - - Set certLabels = certMap.keySet(); - for (String certLabel : certLabels) { - HashSet infoSet = certMap.get(certLabel); - for (AliasInfo aliasInfo : infoSet) { - - if (aliasInfo.matched == true) { - // already found a private key match for this cert - - // just continue - aliasInfo.trusted = false; - continue; - } - - // cert in this aliasInfo is not matched yet - // - // if CKA_TRUSTED_SUPPORTED == true, - // then check if cert is trusted - - if (CKA_TRUSTED_SUPPORTED) { - if (aliasInfo.trusted) { - // trusted certificate - if (mapTrustedCert - (certLabel, aliasInfo, infoSet) == true) { - sharedLabel = true; - } - } - continue; - } - - // CKA_TRUSTED_SUPPORTED == false - // - // XXX treat all certs not part of a chain as trusted - // XXX - // XXX Unsupported - // - // boolean partOfChain = false; - // for (AliasInfo matchedInfo : matchedCerts) { - // for (int i = 0; i < matchedInfo.chain.length; i++) { - // if (matchedInfo.chain[i].equals(aliasInfo.cert)) { - // partOfChain = true; - // break; - // } - // } - // if (partOfChain) { - // break; - // } - // } - // - // if (!partOfChain) { - // if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){ - // sharedLabel = true; - // } - // } else { - // if (debug != null) { - // debug.println("ignoring unmatched/untrusted cert " + - // "that is part of cert chain - cert subject is [" + - // aliasInfo.cert.getSubjectX500Principal().getName - // (X500Principal.CANONICAL) + - // "]"); - // } - // } - } - } - - return sharedLabel; - } - - private boolean mapTrustedCert(String certLabel, - AliasInfo aliasInfo, - HashSet infoSet) { - - boolean sharedLabel = false; - - aliasInfo.type = ATTR_CLASS_CERT; - aliasInfo.trusted = true; - if (infoSet.size() == 1) { - // unique CKA_LABEL - use certLabel as alias - aliasMap.put(certLabel, aliasInfo); - } else { - // create new alias - sharedLabel = true; - aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo); - } - - return sharedLabel; - } - - /** - * If the secret key shares a CKA_LABEL with another entry, - * throw an exception - */ - private void mapSecretKeys(HashMap sKeyMap) - throws KeyStoreException { - for (String label : sKeyMap.keySet()) { - if (aliasMap.containsKey(label)) { - throw new KeyStoreException("invalid KeyStore state: " + - "found secret key sharing CKA_LABEL [" + - label + - "] with another token object"); - } - } - aliasMap.putAll(sKeyMap); - } - - private void dumpTokenMap() { - Set aliases = aliasMap.keySet(); - System.out.println("Token Alias Map:"); - if (aliases.isEmpty()) { - System.out.println(" [empty]"); - } else { - for (String s : aliases) { - System.out.println(" " + s + aliasMap.get(s)); - } - } - } - - private void checkWrite() throws KeyStoreException { - if (writeDisabled) { - throw new KeyStoreException - ("This PKCS11KeyStore does not support write capabilities"); - } - } - - private final static long[] LONG0 = new long[0]; - - private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs) - throws PKCS11Exception { - Token token = session.token; - long[] handles = LONG0; - token.p11.C_FindObjectsInit(session.id(), attrs); - while (true) { - long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX); - if (h.length == 0) { - break; - } - handles = P11Util.concat(handles, h); - } - token.p11.C_FindObjectsFinal(session.id()); - return handles; - } - -}