Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java
41154 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs11;2627import java.math.BigInteger;2829import java.io.InputStream;30import java.io.OutputStream;31import java.io.IOException;32import java.io.ByteArrayInputStream;3334import static java.nio.charset.StandardCharsets.UTF_8;3536import java.util.Arrays;37import java.util.Collections;38import java.util.Date;39import java.util.Enumeration;40import java.util.ArrayList;41import java.util.HashSet;42import java.util.HashMap;43import java.util.Set;4445import java.security.*;46import java.security.KeyStore.*;4748import java.security.cert.Certificate;49import java.security.cert.X509Certificate;50import java.security.cert.CertificateFactory;51import java.security.cert.CertificateException;5253import java.security.interfaces.*;54import java.security.spec.*;5556import javax.crypto.SecretKey;57import javax.crypto.interfaces.*;5859import javax.security.auth.x500.X500Principal;60import javax.security.auth.login.LoginException;61import javax.security.auth.callback.Callback;62import javax.security.auth.callback.PasswordCallback;63import javax.security.auth.callback.CallbackHandler;64import javax.security.auth.callback.UnsupportedCallbackException;6566import sun.security.util.Debug;67import sun.security.util.DerValue;68import sun.security.util.ECUtil;6970import sun.security.pkcs11.Secmod.*;71import static sun.security.pkcs11.P11Util.*;7273import sun.security.pkcs11.wrapper.*;74import static sun.security.pkcs11.wrapper.PKCS11Constants.*;75import static sun.security.pkcs11.wrapper.PKCS11Exception.*;7677import sun.security.rsa.RSAKeyFactory;7879final class P11KeyStore extends KeyStoreSpi {8081private static final CK_ATTRIBUTE ATTR_CLASS_CERT =82new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE);83private static final CK_ATTRIBUTE ATTR_CLASS_PKEY =84new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY);85private static final CK_ATTRIBUTE ATTR_CLASS_SKEY =86new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);8788private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE =89new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509);9091private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE =92new CK_ATTRIBUTE(CKA_TOKEN, true);9394// XXX for testing purposes only95// - NSS doesn't support persistent secret keys96// (key type gets mangled if secret key is a token key)97// - if debug is turned on, then this is set to false98private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE;99100private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE =101new CK_ATTRIBUTE(CKA_TRUSTED, true);102private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE =103new CK_ATTRIBUTE(CKA_PRIVATE, true);104105private static final long NO_HANDLE = -1;106private static final long FINDOBJECTS_MAX = 100;107private static final String ALIAS_SEP = "/";108109private static final boolean NSS_TEST = false;110private static final Debug debug =111Debug.getInstance("pkcs11keystore");112private static boolean CKA_TRUSTED_SUPPORTED = true;113114private final Token token;115116// If multiple certs are found to share the same CKA_LABEL117// at load time (NSS-style keystore), then the keystore is read118// and the unique keystore aliases are mapped to the entries.119// However, write capabilities are disabled.120private boolean writeDisabled = false;121122// Map of unique keystore aliases to entries in the token123private HashMap<String, AliasInfo> aliasMap;124125// whether to use NSS Secmod info for trust attributes126private final boolean useSecmodTrust;127128// if useSecmodTrust == true, which type of trust we are interested in129private Secmod.TrustType nssTrustType;130131/**132* The underlying token may contain multiple certs belonging to the133* same "personality" (for example, a signing cert and encryption cert),134* all sharing the same CKA_LABEL. These must be resolved135* into unique keystore aliases.136*137* In addition, private keys and certs may not have a CKA_LABEL.138* It is assumed that a private key and corresponding certificate139* share the same CKA_ID, and that the CKA_ID is unique across the token.140* The CKA_ID may not be human-readable.141* These pairs must be resolved into unique keystore aliases.142*143* Furthermore, secret keys are assumed to have a CKA_LABEL144* unique across the entire token.145*146* When the KeyStore is loaded, instances of this class are147* created to represent the private keys/secret keys/certs148* that reside on the token.149*/150private static class AliasInfo {151152// CKA_CLASS - entry type153private CK_ATTRIBUTE type = null;154155// CKA_LABEL of cert and secret key156private String label = null;157158// CKA_ID of the private key/cert pair159private byte[] id = null;160161// CKA_TRUSTED - true if cert is trusted162private boolean trusted = false;163164// either end-entity cert or trusted cert depending on 'type'165private X509Certificate cert = null;166167// chain168private X509Certificate[] chain = null;169170// true if CKA_ID for private key and cert match up171private boolean matched = false;172173// SecretKeyEntry174public AliasInfo(String label) {175this.type = ATTR_CLASS_SKEY;176this.label = label;177}178179// PrivateKeyEntry180public AliasInfo(String label,181byte[] id,182boolean trusted,183X509Certificate cert) {184this.type = ATTR_CLASS_PKEY;185this.label = label;186this.id = id;187this.trusted = trusted;188this.cert = cert;189}190191public String toString() {192StringBuilder sb = new StringBuilder();193if (type == ATTR_CLASS_PKEY) {194sb.append("\ttype=[private key]\n");195} else if (type == ATTR_CLASS_SKEY) {196sb.append("\ttype=[secret key]\n");197} else if (type == ATTR_CLASS_CERT) {198sb.append("\ttype=[trusted cert]\n");199}200sb.append("\tlabel=[" + label + "]\n");201if (id == null) {202sb.append("\tid=[null]\n");203} else {204sb.append("\tid=" + P11KeyStore.getID(id) + "\n");205}206sb.append("\ttrusted=[" + trusted + "]\n");207sb.append("\tmatched=[" + matched + "]\n");208if (cert == null) {209sb.append("\tcert=[null]\n");210} else {211sb.append("\tcert=[\tsubject: " +212cert.getSubjectX500Principal() +213"\n\t\tissuer: " +214cert.getIssuerX500Principal() +215"\n\t\tserialNum: " +216cert.getSerialNumber().toString() +217"]");218}219return sb.toString();220}221}222223/**224* callback handler for passing password to Provider.login method225*/226private static class PasswordCallbackHandler implements CallbackHandler {227228private char[] password;229230private PasswordCallbackHandler(char[] password) {231if (password != null) {232this.password = password.clone();233}234}235236public void handle(Callback[] callbacks)237throws IOException, UnsupportedCallbackException {238if (!(callbacks[0] instanceof PasswordCallback)) {239throw new UnsupportedCallbackException(callbacks[0]);240}241PasswordCallback pc = (PasswordCallback)callbacks[0];242pc.setPassword(password); // this clones the password if not null243}244245@SuppressWarnings("deprecation")246protected void finalize() throws Throwable {247if (password != null) {248Arrays.fill(password, ' ');249}250super.finalize();251}252}253254/**255* getTokenObject return value.256*257* if object is not found, type is set to null.258* otherwise, type is set to the requested type.259*/260private static class THandle {261private final long handle; // token object handle262private final CK_ATTRIBUTE type; // CKA_CLASS263264private THandle(long handle, CK_ATTRIBUTE type) {265this.handle = handle;266this.type = type;267}268}269270P11KeyStore(Token token) {271this.token = token;272this.useSecmodTrust = token.provider.nssUseSecmodTrust;273}274275/**276* Returns the key associated with the given alias.277* The key must have been associated with278* the alias by a call to <code>setKeyEntry</code>,279* or by a call to <code>setEntry</code> with a280* <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>.281*282* @param alias the alias name283* @param password the password, which must be <code>null</code>284*285* @return the requested key, or null if the given alias does not exist286* or does not identify a key-related entry.287*288* @exception NoSuchAlgorithmException if the algorithm for recovering the289* key cannot be found290* @exception UnrecoverableKeyException if the key cannot be recovered291*/292public synchronized Key engineGetKey(String alias, char[] password)293throws NoSuchAlgorithmException, UnrecoverableKeyException {294295token.ensureValid();296if (password != null && !token.config.getKeyStoreCompatibilityMode()) {297throw new NoSuchAlgorithmException("password must be null");298}299300AliasInfo aliasInfo = aliasMap.get(alias);301if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {302return null;303}304305Session session = null;306try {307session = token.getOpSession();308309if (aliasInfo.type == ATTR_CLASS_PKEY) {310THandle h = getTokenObject(session,311aliasInfo.type,312aliasInfo.id,313null);314if (h.type == ATTR_CLASS_PKEY) {315return loadPkey(session, h.handle);316}317} else {318THandle h = getTokenObject(session,319ATTR_CLASS_SKEY,320null,321alias);322if (h.type == ATTR_CLASS_SKEY) {323return loadSkey(session, h.handle);324}325}326327// did not find anything328return null;329} catch (PKCS11Exception | KeyStoreException e) {330throw new ProviderException(e);331} finally {332token.releaseSession(session);333}334}335336/**337* Returns the certificate chain associated with the given alias.338* The certificate chain must have been associated with the alias339* by a call to <code>setKeyEntry</code>,340* or by a call to <code>setEntry</code> with a341* <code>PrivateKeyEntry</code>.342*343* @param alias the alias name344*345* @return the certificate chain (ordered with the user's certificate first346* and the root certificate authority last), or null if the given alias347* does not exist or does not contain a certificate chain348*/349public synchronized Certificate[] engineGetCertificateChain(String alias) {350351token.ensureValid();352353AliasInfo aliasInfo = aliasMap.get(alias);354if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) {355return null;356}357return aliasInfo.chain;358}359360/**361* Returns the certificate associated with the given alias.362*363* <p> If the given alias name identifies an entry364* created by a call to <code>setCertificateEntry</code>,365* or created by a call to <code>setEntry</code> with a366* <code>TrustedCertificateEntry</code>,367* then the trusted certificate contained in that entry is returned.368*369* <p> If the given alias name identifies an entry370* created by a call to <code>setKeyEntry</code>,371* or created by a call to <code>setEntry</code> with a372* <code>PrivateKeyEntry</code>,373* then the first element of the certificate chain in that entry374* (if a chain exists) is returned.375*376* @param alias the alias name377*378* @return the certificate, or null if the given alias does not exist or379* does not contain a certificate.380*/381public synchronized Certificate engineGetCertificate(String alias) {382token.ensureValid();383384AliasInfo aliasInfo = aliasMap.get(alias);385if (aliasInfo == null) {386return null;387}388return aliasInfo.cert;389}390391/**392* Returns the creation date of the entry identified by the given alias.393*394* @param alias the alias name395*396* @return the creation date of this entry, or null if the given alias does397* not exist398*/399public Date engineGetCreationDate(String alias) {400token.ensureValid();401throw new ProviderException(new UnsupportedOperationException());402}403404/**405* Assigns the given key to the given alias, protecting it with the given406* password.407*408* <p>If the given key is of type <code>java.security.PrivateKey</code>,409* it must be accompanied by a certificate chain certifying the410* corresponding public key.411*412* <p>If the given alias already exists, the keystore information413* associated with it is overridden by the given key (and possibly414* certificate chain).415*416* @param alias the alias name417* @param key the key to be associated with the alias418* @param password the password to protect the key419* @param chain the certificate chain for the corresponding public420* key (only required if the given key is of type421* <code>java.security.PrivateKey</code>).422*423* @exception KeyStoreException if the given key cannot be protected, or424* this operation fails for some other reason425*/426public synchronized void engineSetKeyEntry(String alias, Key key,427char[] password,428Certificate[] chain)429throws KeyStoreException {430431token.ensureValid();432checkWrite();433434if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) {435throw new KeyStoreException("key must be PrivateKey or SecretKey");436} else if (key instanceof PrivateKey && chain == null) {437throw new KeyStoreException438("PrivateKey must be accompanied by non-null chain");439} else if (key instanceof SecretKey && chain != null) {440throw new KeyStoreException441("SecretKey must be accompanied by null chain");442} else if (password != null &&443!token.config.getKeyStoreCompatibilityMode()) {444throw new KeyStoreException("Password must be null");445}446447KeyStore.Entry entry = null;448try {449if (key instanceof PrivateKey) {450entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, chain);451} else if (key instanceof SecretKey) {452entry = new KeyStore.SecretKeyEntry((SecretKey)key);453}454} catch (NullPointerException | IllegalArgumentException e) {455throw new KeyStoreException(e);456}457engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password));458}459460/**461* Assigns the given key (that has already been protected) to the given462* alias.463*464* <p>If the protected key is of type465* <code>java.security.PrivateKey</code>,466* it must be accompanied by a certificate chain certifying the467* corresponding public key.468*469* <p>If the given alias already exists, the keystore information470* associated with it is overridden by the given key (and possibly471* certificate chain).472*473* @param alias the alias name474* @param key the key (in protected format) to be associated with the alias475* @param chain the certificate chain for the corresponding public476* key (only useful if the protected key is of type477* <code>java.security.PrivateKey</code>).478*479* @exception KeyStoreException if this operation fails.480*/481public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)482throws KeyStoreException {483token.ensureValid();484throw new ProviderException(new UnsupportedOperationException());485}486487/**488* Assigns the given certificate to the given alias.489*490* <p> If the given alias identifies an existing entry491* created by a call to <code>setCertificateEntry</code>,492* or created by a call to <code>setEntry</code> with a493* <code>TrustedCertificateEntry</code>,494* the trusted certificate in the existing entry495* is overridden by the given certificate.496*497* @param alias the alias name498* @param cert the certificate499*500* @exception KeyStoreException if the given alias already exists and does501* not identify an entry containing a trusted certificate,502* or this operation fails for some other reason.503*/504public synchronized void engineSetCertificateEntry505(String alias, Certificate cert) throws KeyStoreException {506507token.ensureValid();508checkWrite();509510if (cert == null) {511throw new KeyStoreException("invalid null certificate");512}513514KeyStore.Entry entry = null;515entry = new KeyStore.TrustedCertificateEntry(cert);516engineSetEntry(alias, entry, null);517}518519/**520* Deletes the entry identified by the given alias from this keystore.521*522* @param alias the alias name523*524* @exception KeyStoreException if the entry cannot be removed.525*/526public synchronized void engineDeleteEntry(String alias)527throws KeyStoreException {528token.ensureValid();529530if (token.isWriteProtected()) {531throw new KeyStoreException("token write-protected");532}533checkWrite();534deleteEntry(alias);535}536537/**538* XXX - not sure whether to keep this539*/540private boolean deleteEntry(String alias) throws KeyStoreException {541AliasInfo aliasInfo = aliasMap.get(alias);542if (aliasInfo != null) {543544aliasMap.remove(alias);545546try {547if (aliasInfo.type == ATTR_CLASS_CERT) {548// trusted certificate entry549return destroyCert(aliasInfo.id);550} else if (aliasInfo.type == ATTR_CLASS_PKEY) {551// private key entry552return destroyPkey(aliasInfo.id) &&553destroyChain(aliasInfo.id);554} else if (aliasInfo.type == ATTR_CLASS_SKEY) {555// secret key entry556return destroySkey(alias);557} else {558throw new KeyStoreException("unexpected entry type");559}560} catch (PKCS11Exception | CertificateException e) {561throw new KeyStoreException(e);562}563}564return false;565}566567/**568* Lists all the alias names of this keystore.569*570* @return enumeration of the alias names571*/572public synchronized Enumeration<String> engineAliases() {573token.ensureValid();574575// don't want returned enumeration to iterate off actual keySet -576// otherwise applications that iterate and modify the keystore577// may run into concurrent modification problems578return Collections.enumeration(new HashSet<String>(aliasMap.keySet()));579}580581/**582* Checks if the given alias exists in this keystore.583*584* @param alias the alias name585*586* @return true if the alias exists, false otherwise587*/588public synchronized boolean engineContainsAlias(String alias) {589token.ensureValid();590return aliasMap.containsKey(alias);591}592593/**594* Retrieves the number of entries in this keystore.595*596* @return the number of entries in this keystore597*/598public synchronized int engineSize() {599token.ensureValid();600return aliasMap.size();601}602603/**604* Returns true if the entry identified by the given alias605* was created by a call to <code>setKeyEntry</code>,606* or created by a call to <code>setEntry</code> with a607* <code>PrivateKeyEntry</code> or a <code>SecretKeyEntry</code>.608*609* @param alias the alias for the keystore entry to be checked610*611* @return true if the entry identified by the given alias is a612* key-related, false otherwise.613*/614public synchronized boolean engineIsKeyEntry(String alias) {615token.ensureValid();616617AliasInfo aliasInfo = aliasMap.get(alias);618if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {619return false;620}621return true;622}623624/**625* Returns true if the entry identified by the given alias626* was created by a call to <code>setCertificateEntry</code>,627* or created by a call to <code>setEntry</code> with a628* <code>TrustedCertificateEntry</code>.629*630* @param alias the alias for the keystore entry to be checked631*632* @return true if the entry identified by the given alias contains a633* trusted certificate, false otherwise.634*/635public synchronized boolean engineIsCertificateEntry(String alias) {636token.ensureValid();637638AliasInfo aliasInfo = aliasMap.get(alias);639if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) {640return false;641}642return true;643}644645/**646* Returns the (alias) name of the first keystore entry whose certificate647* matches the given certificate.648*649* <p>This method attempts to match the given certificate with each650* keystore entry. If the entry being considered was651* created by a call to <code>setCertificateEntry</code>,652* or created by a call to <code>setEntry</code> with a653* <code>TrustedCertificateEntry</code>,654* then the given certificate is compared to that entry's certificate.655*656* <p> If the entry being considered was657* created by a call to <code>setKeyEntry</code>,658* or created by a call to <code>setEntry</code> with a659* <code>PrivateKeyEntry</code>,660* then the given certificate is compared to the first661* element of that entry's certificate chain.662*663* @param cert the certificate to match with.664*665* @return the alias name of the first entry with matching certificate,666* or null if no such entry exists in this keystore.667*/668public synchronized String engineGetCertificateAlias(Certificate cert) {669token.ensureValid();670Enumeration<String> e = engineAliases();671while (e.hasMoreElements()) {672String alias = e.nextElement();673Certificate tokenCert = engineGetCertificate(alias);674if (tokenCert != null && tokenCert.equals(cert)) {675return alias;676}677}678return null;679}680681/**682* engineStore currently is a No-op.683* Entries are stored to the token during engineSetEntry684*685* @param stream this must be <code>null</code>686* @param password this must be <code>null</code>687*/688public synchronized void engineStore(OutputStream stream, char[] password)689throws IOException, NoSuchAlgorithmException, CertificateException {690token.ensureValid();691if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {692throw new IOException("output stream must be null");693}694695if (password != null && !token.config.getKeyStoreCompatibilityMode()) {696throw new IOException("password must be null");697}698}699700/**701* engineStore currently is a No-op.702* Entries are stored to the token during engineSetEntry703*704* @param param this must be <code>null</code>705*706* @exception IllegalArgumentException if the given707* <code>KeyStore.LoadStoreParameter</code>708* input is not <code>null</code>709*/710public synchronized void engineStore(KeyStore.LoadStoreParameter param)711throws IOException, NoSuchAlgorithmException, CertificateException {712token.ensureValid();713if (param != null) {714throw new IllegalArgumentException715("LoadStoreParameter must be null");716}717}718719/**720* Loads the keystore.721*722* @param stream the input stream, which must be <code>null</code>723* @param password the password used to unlock the keystore,724* or <code>null</code> if the token supports a725* CKF_PROTECTED_AUTHENTICATION_PATH726*727* @exception IOException if the given <code>stream</code> is not728* <code>null</code>, if the token supports a729* CKF_PROTECTED_AUTHENTICATION_PATH and a non-null730* password is given, of if the token login operation failed731*/732public synchronized void engineLoad(InputStream stream, char[] password)733throws IOException, NoSuchAlgorithmException, CertificateException {734735token.ensureValid();736737if (NSS_TEST) {738ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);739}740741if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {742throw new IOException("input stream must be null");743}744745if (useSecmodTrust) {746nssTrustType = Secmod.TrustType.ALL;747}748749try {750if (password == null) {751login(null);752} else {753login(new PasswordCallbackHandler(password));754}755} catch(LoginException e) {756Throwable cause = e.getCause();757if (cause instanceof PKCS11Exception) {758PKCS11Exception pe = (PKCS11Exception) cause;759if (pe.getErrorCode() == CKR_PIN_INCORRECT) {760// if password is wrong, the cause of the IOException761// should be an UnrecoverableKeyException762throw new IOException("load failed",763new UnrecoverableKeyException().initCause(e));764}765}766throw new IOException("load failed", e);767}768769try {770if (mapLabels() == true) {771// CKA_LABELs are shared by multiple certs772writeDisabled = true;773}774if (debug != null) {775dumpTokenMap();776debug.println("P11KeyStore load. Entry count: " +777aliasMap.size());778}779} catch (KeyStoreException | PKCS11Exception e) {780throw new IOException("load failed", e);781}782}783784/**785* Loads the keystore using the given786* <code>KeyStore.LoadStoreParameter</code>.787*788* <p> The <code>LoadStoreParameter.getProtectionParameter()</code>789* method is expected to return a <code>KeyStore.PasswordProtection</code>790* object. The password is retrieved from that object and used791* to unlock the PKCS#11 token.792*793* <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH794* then the provided password must be <code>null</code>.795*796* @param param the <code>KeyStore.LoadStoreParameter</code>797*798* @exception IllegalArgumentException if the given799* <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,800* or if that parameter returns a <code>null</code>801* <code>ProtectionParameter</code> object.802* input is not recognized803* @exception IOException if the token supports a804* CKF_PROTECTED_AUTHENTICATION_PATH and the provided password805* is non-null, or if the token login operation fails806*/807public synchronized void engineLoad(KeyStore.LoadStoreParameter param)808throws IOException, NoSuchAlgorithmException,809CertificateException {810811token.ensureValid();812813if (NSS_TEST) {814ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);815}816817// if caller wants to pass a NULL password,818// force it to pass a non-NULL PasswordProtection that returns819// a NULL password820821if (param == null) {822throw new IllegalArgumentException823("invalid null LoadStoreParameter");824}825if (useSecmodTrust) {826if (param instanceof Secmod.KeyStoreLoadParameter) {827nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();828} else {829nssTrustType = Secmod.TrustType.ALL;830}831}832833CallbackHandler handler;834KeyStore.ProtectionParameter pp = param.getProtectionParameter();835if (pp instanceof PasswordProtection) {836char[] password = ((PasswordProtection)pp).getPassword();837if (password == null) {838handler = null;839} else {840handler = new PasswordCallbackHandler(password);841}842} else if (pp instanceof CallbackHandlerProtection) {843handler = ((CallbackHandlerProtection)pp).getCallbackHandler();844} else {845throw new IllegalArgumentException846("ProtectionParameter must be either " +847"PasswordProtection or CallbackHandlerProtection");848}849850try {851login(handler);852if (mapLabels() == true) {853// CKA_LABELs are shared by multiple certs854writeDisabled = true;855}856if (debug != null) {857dumpTokenMap();858}859} catch (LoginException | KeyStoreException | PKCS11Exception e) {860throw new IOException("load failed", e);861}862}863864private void login(CallbackHandler handler) throws LoginException {865if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {866token.provider.login(null, handler);867} else {868// token supports protected authentication path869// (external pin-pad, for example)870if (handler != null &&871!token.config.getKeyStoreCompatibilityMode()) {872throw new LoginException("can not specify password if token " +873"supports protected authentication path");874}875876// must rely on application-set or default handler877// if one is necessary878token.provider.login(null, null);879}880}881882/**883* Get a <code>KeyStore.Entry</code> for the specified alias884*885* @param alias get the <code>KeyStore.Entry</code> for this alias886* @param protParam this must be <code>null</code>887*888* @return the <code>KeyStore.Entry</code> for the specified alias,889* or <code>null</code> if there is no such entry890*891* @exception KeyStoreException if the operation failed892* @exception NoSuchAlgorithmException if the algorithm for recovering the893* entry cannot be found894* @exception UnrecoverableEntryException if the specified895* <code>protParam</code> were insufficient or invalid896*897* @since 1.5898*/899public synchronized KeyStore.Entry engineGetEntry(String alias,900KeyStore.ProtectionParameter protParam)901throws KeyStoreException, NoSuchAlgorithmException,902UnrecoverableEntryException {903904token.ensureValid();905906if (protParam != null &&907protParam instanceof KeyStore.PasswordProtection &&908((KeyStore.PasswordProtection)protParam).getPassword() != null &&909!token.config.getKeyStoreCompatibilityMode()) {910throw new KeyStoreException("ProtectionParameter must be null");911}912913AliasInfo aliasInfo = aliasMap.get(alias);914if (aliasInfo == null) {915if (debug != null) {916debug.println("engineGetEntry did not find alias [" +917alias +918"] in map");919}920return null;921}922923Session session = null;924try {925session = token.getOpSession();926927if (aliasInfo.type == ATTR_CLASS_CERT) {928// trusted certificate entry929if (debug != null) {930debug.println("engineGetEntry found trusted cert entry");931}932return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);933} else if (aliasInfo.type == ATTR_CLASS_SKEY) {934// secret key entry935if (debug != null) {936debug.println("engineGetEntry found secret key entry");937}938939THandle h = getTokenObject940(session, ATTR_CLASS_SKEY, null, aliasInfo.label);941if (h.type != ATTR_CLASS_SKEY) {942throw new KeyStoreException943("expected but could not find secret key");944} else {945SecretKey skey = loadSkey(session, h.handle);946return new KeyStore.SecretKeyEntry(skey);947}948} else {949// private key entry950if (debug != null) {951debug.println("engineGetEntry found private key entry");952}953954THandle h = getTokenObject955(session, ATTR_CLASS_PKEY, aliasInfo.id, null);956if (h.type != ATTR_CLASS_PKEY) {957throw new KeyStoreException958("expected but could not find private key");959} else {960PrivateKey pkey = loadPkey(session, h.handle);961Certificate[] chain = aliasInfo.chain;962if ((pkey != null) && (chain != null)) {963return new KeyStore.PrivateKeyEntry(pkey, chain);964} else {965if (debug != null) {966debug.println967("engineGetEntry got null cert chain or private key");968}969}970}971}972return null;973} catch (PKCS11Exception pe) {974throw new KeyStoreException(pe);975} finally {976token.releaseSession(session);977}978}979980/**981* Save a <code>KeyStore.Entry</code> under the specified alias.982*983* <p> If an entry already exists for the specified alias,984* it is overridden.985*986* <p> This KeyStore implementation only supports the standard987* entry types, and only supports X509Certificates in988* TrustedCertificateEntries. Also, this implementation does not support989* protecting entries using a different password990* from the one used for token login.991*992* <p> Entries are immediately stored on the token.993*994* @param alias save the <code>KeyStore.Entry</code> under this alias995* @param entry the <code>Entry</code> to save996* @param protParam this must be <code>null</code>997*998* @exception KeyStoreException if this operation fails999*1000* @since 1.51001*/1002public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,1003KeyStore.ProtectionParameter protParam)1004throws KeyStoreException {10051006token.ensureValid();1007checkWrite();10081009if (protParam != null &&1010protParam instanceof KeyStore.PasswordProtection &&1011((KeyStore.PasswordProtection)protParam).getPassword() != null &&1012!token.config.getKeyStoreCompatibilityMode()) {1013throw new KeyStoreException(new UnsupportedOperationException1014("ProtectionParameter must be null"));1015}10161017if (token.isWriteProtected()) {1018throw new KeyStoreException("token write-protected");1019}10201021if (entry instanceof KeyStore.TrustedCertificateEntry) {10221023if (useSecmodTrust == false) {1024// PKCS #11 does not allow app to modify trusted certs -1025throw new KeyStoreException(new UnsupportedOperationException1026("trusted certificates may only be set by " +1027"token initialization application"));1028}1029Secmod.Module module = token.provider.nssModule;1030if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {1031// XXX allow TRUSTANCHOR module1032throw new KeyStoreException("Trusted certificates can only be "1033+ "added to the NSS KeyStore module");1034}1035Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();1036if (cert instanceof X509Certificate == false) {1037throw new KeyStoreException("Certificate must be an X509Certificate");1038}1039X509Certificate xcert = (X509Certificate)cert;1040AliasInfo info = aliasMap.get(alias);1041if (info != null) {1042// XXX try to update1043deleteEntry(alias);1044}1045try {1046storeCert(alias, xcert);1047module.setTrust(token, xcert);1048mapLabels();1049} catch (PKCS11Exception | CertificateException e) {1050throw new KeyStoreException(e);1051}10521053} else {10541055if (entry instanceof KeyStore.PrivateKeyEntry) {10561057PrivateKey key =1058((KeyStore.PrivateKeyEntry)entry).getPrivateKey();1059if (!(key instanceof P11Key) &&1060!(key instanceof RSAPrivateKey) &&1061!(key instanceof DSAPrivateKey) &&1062!(key instanceof DHPrivateKey) &&1063!(key instanceof ECPrivateKey)) {1064throw new KeyStoreException("unsupported key type: " +1065key.getClass().getName());1066}10671068// only support X509Certificate chains1069Certificate[] chain =1070((KeyStore.PrivateKeyEntry)entry).getCertificateChain();1071if (!(chain instanceof X509Certificate[])) {1072throw new KeyStoreException1073(new UnsupportedOperationException1074("unsupported certificate array type: " +1075chain.getClass().getName()));1076}10771078try {1079boolean updatedAlias = false;1080Set<String> aliases = aliasMap.keySet();1081for (String oldAlias : aliases) {10821083// see if there's an existing entry with the same info10841085AliasInfo aliasInfo = aliasMap.get(oldAlias);1086if (aliasInfo.type == ATTR_CLASS_PKEY &&1087aliasInfo.cert.getPublicKey().equals1088(chain[0].getPublicKey())) {10891090// found existing entry -1091// caller is renaming entry or updating cert chain1092//1093// set new CKA_LABEL/CKA_ID1094// and update certs if necessary10951096updatePkey(alias,1097aliasInfo.id,1098(X509Certificate[])chain,1099!aliasInfo.cert.equals(chain[0]));1100updatedAlias = true;1101break;1102}1103}11041105if (!updatedAlias) {1106// caller adding new entry1107engineDeleteEntry(alias);1108storePkey(alias, (KeyStore.PrivateKeyEntry)entry);1109}11101111} catch (PKCS11Exception | CertificateException pe) {1112throw new KeyStoreException(pe);1113}11141115} else if (entry instanceof KeyStore.SecretKeyEntry) {11161117KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;1118SecretKey skey = ske.getSecretKey();11191120try {1121// first check if the key already exists1122AliasInfo aliasInfo = aliasMap.get(alias);11231124if (aliasInfo != null) {1125engineDeleteEntry(alias);1126}1127storeSkey(alias, ske);11281129} catch (PKCS11Exception pe) {1130throw new KeyStoreException(pe);1131}11321133} else {1134throw new KeyStoreException(new UnsupportedOperationException1135("unsupported entry type: " + entry.getClass().getName()));1136}11371138try {11391140// XXX NSS does not write out the CKA_ID we pass to them1141//1142// therefore we must re-map labels1143// (can not simply update aliasMap)11441145mapLabels();1146if (debug != null) {1147dumpTokenMap();1148}1149} catch (PKCS11Exception | CertificateException pe) {1150throw new KeyStoreException(pe);1151}1152}11531154if (debug != null) {1155debug.println1156("engineSetEntry added new entry for [" +1157alias +1158"] to token");1159}1160}11611162/**1163* Determines if the keystore <code>Entry</code> for the specified1164* <code>alias</code> is an instance or subclass of the specified1165* <code>entryClass</code>.1166*1167* @param alias the alias name1168* @param entryClass the entry class1169*1170* @return true if the keystore <code>Entry</code> for the specified1171* <code>alias</code> is an instance or subclass of the1172* specified <code>entryClass</code>, false otherwise1173*/1174public synchronized boolean engineEntryInstanceOf1175(String alias, Class<? extends KeyStore.Entry> entryClass) {1176token.ensureValid();1177return super.engineEntryInstanceOf(alias, entryClass);1178}11791180private X509Certificate loadCert(Session session, long oHandle)1181throws PKCS11Exception, CertificateException {11821183CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]1184{ new CK_ATTRIBUTE(CKA_VALUE) };1185token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);11861187byte[] bytes = attrs[0].getByteArray();1188if (bytes == null) {1189throw new CertificateException1190("unexpectedly retrieved null byte array");1191}1192CertificateFactory cf = CertificateFactory.getInstance("X.509");1193return (X509Certificate)cf.generateCertificate1194(new ByteArrayInputStream(bytes));1195}11961197private X509Certificate[] loadChain(Session session,1198X509Certificate endCert)1199throws PKCS11Exception, CertificateException {12001201ArrayList<X509Certificate> lChain = null;12021203if (endCert.getSubjectX500Principal().equals1204(endCert.getIssuerX500Principal())) {1205// self signed1206return new X509Certificate[] { endCert };1207} else {1208lChain = new ArrayList<X509Certificate>();1209lChain.add(endCert);1210}12111212// try loading remaining certs in chain by following1213// issuer->subject links12141215X509Certificate next = endCert;1216while (true) {1217CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1218ATTR_TOKEN_TRUE,1219ATTR_CLASS_CERT,1220new CK_ATTRIBUTE(CKA_SUBJECT,1221next.getIssuerX500Principal().getEncoded()) };1222long[] ch = findObjects(session, attrs);12231224if (ch == null || ch.length == 0) {1225// done1226break;1227} else {1228// if more than one found, use first1229if (debug != null && ch.length > 1) {1230debug.println("engineGetEntry found " +1231ch.length +1232" certificate entries for subject [" +1233next.getIssuerX500Principal().toString() +1234"] in token - using first entry");1235}12361237next = loadCert(session, ch[0]);1238lChain.add(next);1239if (next.getSubjectX500Principal().equals1240(next.getIssuerX500Principal())) {1241// self signed1242break;1243}1244}1245}12461247return lChain.toArray(new X509Certificate[lChain.size()]);1248}12491250private SecretKey loadSkey(Session session, long oHandle)1251throws PKCS11Exception {12521253CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1254new CK_ATTRIBUTE(CKA_KEY_TYPE) };1255token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1256long kType = attrs[0].getLong();12571258String keyType = null;1259int keyLength = -1;12601261// XXX NSS mangles the stored key type for secret key token objects12621263if (kType == CKK_DES || kType == CKK_DES3) {1264if (kType == CKK_DES) {1265keyType = "DES";1266keyLength = 64;1267} else if (kType == CKK_DES3) {1268keyType = "DESede";1269keyLength = 192;1270}1271} else {1272if (kType == CKK_AES) {1273keyType = "AES";1274} else if (kType == CKK_BLOWFISH) {1275keyType = "Blowfish";1276} else if (kType == CKK_RC4) {1277keyType = "ARCFOUR";1278} else {1279if (debug != null) {1280debug.println("unknown key type [" +1281kType +1282"] - using 'Generic Secret'");1283}1284keyType = "Generic Secret";1285}12861287// XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?1288if (NSS_TEST) {1289keyLength = 128;1290} else {1291attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };1292token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1293keyLength = (int)attrs[0].getLong();1294}1295}12961297return P11Key.secretKey(session, oHandle, keyType, keyLength, null);1298}12991300private PrivateKey loadPkey(Session session, long oHandle)1301throws PKCS11Exception, KeyStoreException {13021303CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1304new CK_ATTRIBUTE(CKA_KEY_TYPE) };1305token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1306long kType = attrs[0].getLong();1307String keyType = null;1308int keyLength = 0;13091310if (kType == CKK_RSA) {13111312keyType = "RSA";13131314attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };1315token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1316BigInteger modulus = attrs[0].getBigInteger();1317keyLength = modulus.bitLength();13181319// This check will combine our "don't care" values here1320// with the system-wide min/max values.1321try {1322RSAKeyFactory.checkKeyLengths(keyLength, null,1323-1, Integer.MAX_VALUE);1324} catch (InvalidKeyException e) {1325throw new KeyStoreException(e.getMessage());1326}13271328return P11Key.privateKey(session,1329oHandle,1330keyType,1331keyLength,1332null);13331334} else if (kType == CKK_DSA) {13351336keyType = "DSA";13371338attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };1339token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1340BigInteger prime = attrs[0].getBigInteger();1341keyLength = prime.bitLength();13421343return P11Key.privateKey(session,1344oHandle,1345keyType,1346keyLength,1347null);13481349} else if (kType == CKK_DH) {13501351keyType = "DH";13521353attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };1354token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1355BigInteger prime = attrs[0].getBigInteger();1356keyLength = prime.bitLength();13571358return P11Key.privateKey(session,1359oHandle,1360keyType,1361keyLength,1362null);13631364} else if (kType == CKK_EC) {13651366attrs = new CK_ATTRIBUTE[] {1367new CK_ATTRIBUTE(CKA_EC_PARAMS),1368};1369token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1370byte[] encodedParams = attrs[0].getByteArray();1371try {1372ECParameterSpec params =1373ECUtil.getECParameterSpec(null, encodedParams);1374keyLength = params.getCurve().getField().getFieldSize();1375} catch (IOException e) {1376// we do not want to accept key with unsupported parameters1377throw new KeyStoreException("Unsupported parameters", e);1378}13791380return P11Key.privateKey(session, oHandle, "EC", keyLength, null);13811382} else {1383if (debug != null) {1384debug.println("unknown key type [" + kType + "]");1385}1386throw new KeyStoreException("unknown key type");1387}1388}138913901391/**1392* XXX On ibutton, when you C_SetAttribute(CKA_ID) for a private key1393* it not only changes the CKA_ID of the private key,1394* it changes the CKA_ID of the corresponding cert too.1395* And vice versa.1396*1397* XXX On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)1398* for a private key, and then try to delete the corresponding cert.1399* So this code reverses the order.1400* After the cert is first destroyed (if necessary),1401* then the CKA_ID of the private key can be changed successfully.1402*1403* @param replaceCert if true, then caller is updating alias info for1404* existing cert (only update CKA_ID/CKA_LABEL).1405* if false, then caller is updating cert chain1406* (delete old end cert and add new chain).1407*/1408private void updatePkey(String alias,1409byte[] cka_id,1410X509Certificate[] chain,1411boolean replaceCert) throws1412KeyStoreException, CertificateException, PKCS11Exception {14131414// XXX1415//1416// always set replaceCert to true1417//1418// NSS does not allow resetting of CKA_LABEL on an existing cert1419// (C_SetAttribute call succeeds, but is ignored)14201421replaceCert = true;14221423Session session = null;1424try {1425session = token.getOpSession();14261427// first get private key object handle and hang onto it14281429THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);1430long pKeyHandle;1431if (h.type == ATTR_CLASS_PKEY) {1432pKeyHandle = h.handle;1433} else {1434throw new KeyStoreException1435("expected but could not find private key " +1436"with CKA_ID " +1437getID(cka_id));1438}14391440// next find existing end entity cert14411442h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1443if (h.type != ATTR_CLASS_CERT) {1444throw new KeyStoreException1445("expected but could not find certificate " +1446"with CKA_ID " +1447getID(cka_id));1448} else {1449if (replaceCert) {1450// replacing existing cert and chain1451destroyChain(cka_id);1452} else {1453// renaming alias for existing cert1454CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1455new CK_ATTRIBUTE(CKA_LABEL, alias),1456new CK_ATTRIBUTE(CKA_ID, alias) };1457token.p11.C_SetAttributeValue1458(session.id(), h.handle, attrs);1459}1460}14611462// add new chain14631464if (replaceCert) {1465// add all certs in chain1466storeChain(alias, chain);1467} else {1468// already updated alias info for existing end cert -1469// just update CA certs1470storeCaCerts(chain, 1);1471}14721473// finally update CKA_ID for private key1474//1475// ibutton may have already done this (that is ok)14761477CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1478new CK_ATTRIBUTE(CKA_ID, alias) };1479token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);14801481if (debug != null) {1482debug.println("updatePkey set new alias [" +1483alias +1484"] for private key entry");1485}1486} finally {1487token.releaseSession(session);1488}1489}14901491// retrieves the native key handle and either update it directly or make a copy1492private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)1493throws PKCS11Exception {14941495// if token key, update alias.1496// if session key, convert to token key.14971498Session session = null;1499long keyID = key.getKeyID();1500try {1501session = token.getOpSession();1502if (key.tokenObject == true) {1503// token key - set new CKA_ID15041505CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1506new CK_ATTRIBUTE(CKA_ID, alias) };1507token.p11.C_SetAttributeValue1508(session.id(), keyID, attrs);1509if (debug != null) {1510debug.println("updateP11Pkey set new alias [" +1511alias +1512"] for key entry");1513}1514} else {1515// session key - convert to token key and set CKA_ID15161517CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1518ATTR_TOKEN_TRUE,1519new CK_ATTRIBUTE(CKA_ID, alias),1520};1521if (attribute != null) {1522attrs = addAttribute(attrs, attribute);1523}1524// creates a new token key with the desired CKA_ID1525token.p11.C_CopyObject(session.id(), keyID, attrs);1526if (debug != null) {1527debug.println("updateP11Pkey copied private session key " +1528"for [" +1529alias +1530"] to token entry");1531}1532}1533} finally {1534token.releaseSession(session);1535key.releaseKeyID();1536}1537}15381539private void storeCert(String alias, X509Certificate cert)1540throws PKCS11Exception, CertificateException {15411542ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();1543attrList.add(ATTR_TOKEN_TRUE);1544attrList.add(ATTR_CLASS_CERT);1545attrList.add(ATTR_X509_CERT_TYPE);1546attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,1547cert.getSubjectX500Principal().getEncoded()));1548attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,1549cert.getIssuerX500Principal().getEncoded()));1550attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,1551cert.getSerialNumber().toByteArray()));1552attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));15531554if (alias != null) {1555attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));1556attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));1557} else {1558// ibutton requires something to be set1559// - alias must be unique1560attrList.add(new CK_ATTRIBUTE(CKA_ID,1561getID(cert.getSubjectX500Principal().getName1562(X500Principal.CANONICAL), cert)));1563}15641565Session session = null;1566try {1567session = token.getOpSession();1568token.p11.C_CreateObject(session.id(),1569attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));1570} finally {1571token.releaseSession(session);1572}1573}15741575private void storeChain(String alias, X509Certificate[] chain)1576throws PKCS11Exception, CertificateException {15771578// add new chain1579//1580// end cert has CKA_LABEL and CKA_ID set to alias.1581// other certs in chain have neither set.15821583storeCert(alias, chain[0]);1584storeCaCerts(chain, 1);1585}15861587private void storeCaCerts(X509Certificate[] chain, int start)1588throws PKCS11Exception, CertificateException {15891590// do not add duplicate CA cert if already in token1591//1592// XXX ibutton stores duplicate CA certs, NSS does not15931594Session session = null;1595HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();1596try {1597session = token.getOpSession();1598CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1599ATTR_TOKEN_TRUE,1600ATTR_CLASS_CERT };1601long[] handles = findObjects(session, attrs);16021603// load certs currently on the token1604for (long handle : handles) {1605cacerts.add(loadCert(session, handle));1606}1607} finally {1608token.releaseSession(session);1609}16101611for (int i = start; i < chain.length; i++) {1612if (!cacerts.contains(chain[i])) {1613storeCert(null, chain[i]);1614} else if (debug != null) {1615debug.println("ignoring duplicate CA cert for [" +1616chain[i].getSubjectX500Principal() +1617"]");1618}1619}1620}16211622private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)1623throws PKCS11Exception, KeyStoreException {16241625SecretKey skey = ske.getSecretKey();1626// No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since1627// they are handled in P11SecretKeyFactory.createKey() method.1628CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1629ATTR_SKEY_TOKEN_TRUE,1630ATTR_PRIVATE_TRUE,1631new CK_ATTRIBUTE(CKA_LABEL, alias),1632};1633try {1634P11SecretKeyFactory.convertKey(token, skey, null, attrs);1635} catch (InvalidKeyException ike) {1636// re-throw KeyStoreException to match javadoc1637throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);1638}16391640// update global alias map1641aliasMap.put(alias, new AliasInfo(alias));16421643if (debug != null) {1644debug.println("storeSkey created token secret key for [" +1645alias + "]");1646}1647}16481649private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {1650int n = attrs.length;1651CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];1652System.arraycopy(attrs, 0, newAttrs, 0, n);1653newAttrs[n] = attr;1654return newAttrs;1655}16561657private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)1658throws PKCS11Exception, CertificateException, KeyStoreException {16591660PrivateKey key = pke.getPrivateKey();1661CK_ATTRIBUTE[] attrs = null;16621663// If the key is a token object on this token, update it instead1664// of creating a duplicate key object.1665// Otherwise, treat a P11Key like any other key, if it is extractable.1666if (key instanceof P11Key) {1667P11Key p11Key = (P11Key)key;1668if (p11Key.tokenObject && (p11Key.token == this.token)) {1669updateP11Pkey(alias, null, p11Key);1670storeChain(alias, (X509Certificate[])pke.getCertificateChain());1671return;1672}1673}16741675boolean useNDB = token.config.getNssNetscapeDbWorkaround();1676PublicKey publicKey = pke.getCertificate().getPublicKey();16771678if (key instanceof RSAPrivateKey) {16791680X509Certificate cert = (X509Certificate)pke.getCertificate();1681attrs = getRsaPrivKeyAttrs1682(alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());16831684} else if (key instanceof DSAPrivateKey) {16851686DSAPrivateKey dsaKey = (DSAPrivateKey)key;16871688CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1689if (idAttrs[0] == null) {1690idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1691}16921693attrs = new CK_ATTRIBUTE[] {1694ATTR_TOKEN_TRUE,1695ATTR_CLASS_PKEY,1696ATTR_PRIVATE_TRUE,1697new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),1698idAttrs[0],1699new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),1700new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),1701new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),1702new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),1703};1704if (idAttrs[1] != null) {1705attrs = addAttribute(attrs, idAttrs[1]);1706}17071708attrs = token.getAttributes1709(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);17101711if (debug != null) {1712debug.println("storePkey created DSA template");1713}17141715} else if (key instanceof DHPrivateKey) {17161717DHPrivateKey dhKey = (DHPrivateKey)key;17181719CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1720if (idAttrs[0] == null) {1721idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1722}17231724attrs = new CK_ATTRIBUTE[] {1725ATTR_TOKEN_TRUE,1726ATTR_CLASS_PKEY,1727ATTR_PRIVATE_TRUE,1728new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),1729idAttrs[0],1730new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),1731new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),1732new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),1733};1734if (idAttrs[1] != null) {1735attrs = addAttribute(attrs, idAttrs[1]);1736}17371738attrs = token.getAttributes1739(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);17401741} else if (key instanceof ECPrivateKey) {17421743ECPrivateKey ecKey = (ECPrivateKey)key;17441745CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1746if (idAttrs[0] == null) {1747idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1748}17491750byte[] encodedParams =1751ECUtil.encodeECParameterSpec(null, ecKey.getParams());1752attrs = new CK_ATTRIBUTE[] {1753ATTR_TOKEN_TRUE,1754ATTR_CLASS_PKEY,1755ATTR_PRIVATE_TRUE,1756new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),1757idAttrs[0],1758new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),1759new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),1760};1761if (idAttrs[1] != null) {1762attrs = addAttribute(attrs, idAttrs[1]);1763}17641765attrs = token.getAttributes1766(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);17671768if (debug != null) {1769debug.println("storePkey created EC template");1770}17711772} else if (key instanceof P11Key) {1773// sensitive/non-extractable P11Key1774P11Key p11Key = (P11Key)key;1775if (p11Key.token != this.token) {1776throw new KeyStoreException1777("Cannot move sensitive keys across tokens");1778}1779CK_ATTRIBUTE netscapeDB = null;1780if (useNDB) {1781// Note that this currently fails due to an NSS bug.1782// They do not allow the CKA_NETSCAPE_DB attribute to be1783// specified during C_CopyObject() and fail with1784// CKR_ATTRIBUTE_READ_ONLY.1785// But if we did not specify it, they would fail with1786// CKA_TEMPLATE_INCOMPLETE, so leave this code in here.1787CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);1788netscapeDB = idAttrs[1];1789}1790// Update the key object.1791updateP11Pkey(alias, netscapeDB, p11Key);1792storeChain(alias, (X509Certificate[])pke.getCertificateChain());1793return;17941795} else {1796throw new KeyStoreException("unsupported key type: " + key);1797}17981799Session session = null;1800try {1801session = token.getOpSession();18021803// create private key entry1804token.p11.C_CreateObject(session.id(), attrs);1805if (debug != null) {1806debug.println("storePkey created token key for [" +1807alias +1808"]");1809}1810} finally {1811token.releaseSession(session);1812}18131814storeChain(alias, (X509Certificate[])pke.getCertificateChain());1815}18161817private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,1818RSAPrivateKey key,1819X500Principal subject) throws PKCS11Exception {18201821// subject is currently ignored - could be used to set CKA_SUBJECT18221823CK_ATTRIBUTE[] attrs = null;1824if (key instanceof RSAPrivateCrtKey) {18251826if (debug != null) {1827debug.println("creating RSAPrivateCrtKey attrs");1828}18291830RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;18311832attrs = new CK_ATTRIBUTE[] {1833ATTR_TOKEN_TRUE,1834ATTR_CLASS_PKEY,1835ATTR_PRIVATE_TRUE,1836new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),1837new CK_ATTRIBUTE(CKA_ID, alias),1838new CK_ATTRIBUTE(CKA_MODULUS,1839rsaKey.getModulus()),1840new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,1841rsaKey.getPrivateExponent()),1842new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,1843rsaKey.getPublicExponent()),1844new CK_ATTRIBUTE(CKA_PRIME_1,1845rsaKey.getPrimeP()),1846new CK_ATTRIBUTE(CKA_PRIME_2,1847rsaKey.getPrimeQ()),1848new CK_ATTRIBUTE(CKA_EXPONENT_1,1849rsaKey.getPrimeExponentP()),1850new CK_ATTRIBUTE(CKA_EXPONENT_2,1851rsaKey.getPrimeExponentQ()),1852new CK_ATTRIBUTE(CKA_COEFFICIENT,1853rsaKey.getCrtCoefficient()) };1854attrs = token.getAttributes1855(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);18561857} else {18581859if (debug != null) {1860debug.println("creating RSAPrivateKey attrs");1861}18621863RSAPrivateKey rsaKey = key;18641865attrs = new CK_ATTRIBUTE[] {1866ATTR_TOKEN_TRUE,1867ATTR_CLASS_PKEY,1868ATTR_PRIVATE_TRUE,1869new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),1870new CK_ATTRIBUTE(CKA_ID, alias),1871new CK_ATTRIBUTE(CKA_MODULUS,1872rsaKey.getModulus()),1873new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,1874rsaKey.getPrivateExponent()) };1875attrs = token.getAttributes1876(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);1877}18781879return attrs;1880}18811882/**1883* Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be1884* used for this private key. It uses the same algorithm to calculate the1885* values as NSS. The public and private keys MUST match for the result to1886* be correct.1887*1888* It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB1889* at index 1. The boolean flags determine what is to be calculated.1890* If false or if we could not calculate the value, that element is null.1891*1892* NOTE that we currently do not use the CKA_ID value calculated by this1893* method.1894*/1895private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,1896PublicKey publicKey, boolean id, boolean netscapeDb) {1897CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];1898if ((id || netscapeDb) == false) {1899return attrs;1900}1901String alg = privateKey.getAlgorithm();1902if (alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {1903if (id) {1904BigInteger n = ((RSAPublicKey)publicKey).getModulus();1905attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));1906}1907// CKA_NETSCAPE_DB not needed for RSA public keys1908} else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {1909BigInteger y = ((DSAPublicKey)publicKey).getY();1910if (id) {1911attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));1912}1913if (netscapeDb) {1914attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);1915}1916} else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {1917BigInteger y = ((DHPublicKey)publicKey).getY();1918if (id) {1919attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));1920}1921if (netscapeDb) {1922attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);1923}1924} else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {1925ECPublicKey ecPub = (ECPublicKey)publicKey;1926ECPoint point = ecPub.getW();1927ECParameterSpec params = ecPub.getParams();1928byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());1929if (id) {1930attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));1931}1932if (netscapeDb) {1933attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);1934}1935} else {1936throw new RuntimeException("Unknown key algorithm " + alg);1937}1938return attrs;1939}19401941/**1942* return true if cert destroyed1943*/1944private boolean destroyCert(byte[] cka_id)1945throws PKCS11Exception, KeyStoreException {1946Session session = null;1947try {1948session = token.getOpSession();1949THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1950if (h.type != ATTR_CLASS_CERT) {1951return false;1952}19531954token.p11.C_DestroyObject(session.id(), h.handle);1955if (debug != null) {1956debug.println("destroyCert destroyed cert with CKA_ID [" +1957getID(cka_id) +1958"]");1959}1960return true;1961} finally {1962token.releaseSession(session);1963}1964}19651966/**1967* return true if chain destroyed1968*/1969private boolean destroyChain(byte[] cka_id)1970throws PKCS11Exception, CertificateException, KeyStoreException {19711972Session session = null;1973try {1974session = token.getOpSession();19751976THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1977if (h.type != ATTR_CLASS_CERT) {1978if (debug != null) {1979debug.println("destroyChain could not find " +1980"end entity cert with CKA_ID [0x" +1981Functions.toHexString(cka_id) +1982"]");1983}1984return false;1985}19861987X509Certificate endCert = loadCert(session, h.handle);1988token.p11.C_DestroyObject(session.id(), h.handle);1989if (debug != null) {1990debug.println("destroyChain destroyed end entity cert " +1991"with CKA_ID [" +1992getID(cka_id) +1993"]");1994}19951996// build chain following issuer->subject links19971998X509Certificate next = endCert;1999while (true) {20002001if (next.getSubjectX500Principal().equals2002(next.getIssuerX500Principal())) {2003// self signed - done2004break;2005}20062007CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {2008ATTR_TOKEN_TRUE,2009ATTR_CLASS_CERT,2010new CK_ATTRIBUTE(CKA_SUBJECT,2011next.getIssuerX500Principal().getEncoded()) };2012long[] ch = findObjects(session, attrs);20132014if (ch == null || ch.length == 0) {2015// done2016break;2017} else {2018// if more than one found, use first2019if (debug != null && ch.length > 1) {2020debug.println("destroyChain found " +2021ch.length +2022" certificate entries for subject [" +2023next.getIssuerX500Principal() +2024"] in token - using first entry");2025}20262027next = loadCert(session, ch[0]);20282029// only delete if not part of any other chain20302031attrs = new CK_ATTRIBUTE[] {2032ATTR_TOKEN_TRUE,2033ATTR_CLASS_CERT,2034new CK_ATTRIBUTE(CKA_ISSUER,2035next.getSubjectX500Principal().getEncoded()) };2036long[] issuers = findObjects(session, attrs);20372038boolean destroyIt = false;2039if (issuers == null || issuers.length == 0) {2040// no other certs with this issuer -2041// destroy it2042destroyIt = true;2043} else if (issuers.length == 1) {2044X509Certificate iCert = loadCert(session, issuers[0]);2045if (next.equals(iCert)) {2046// only cert with issuer is itself (self-signed) -2047// destroy it2048destroyIt = true;2049}2050}20512052if (destroyIt) {2053token.p11.C_DestroyObject(session.id(), ch[0]);2054if (debug != null) {2055debug.println2056("destroyChain destroyed cert in chain " +2057"with subject [" +2058next.getSubjectX500Principal() + "]");2059}2060} else {2061if (debug != null) {2062debug.println("destroyChain did not destroy " +2063"shared cert in chain with subject [" +2064next.getSubjectX500Principal() + "]");2065}2066}2067}2068}20692070return true;20712072} finally {2073token.releaseSession(session);2074}2075}20762077/**2078* return true if secret key destroyed2079*/2080private boolean destroySkey(String alias)2081throws PKCS11Exception, KeyStoreException {2082Session session = null;2083try {2084session = token.getOpSession();20852086THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);2087if (h.type != ATTR_CLASS_SKEY) {2088if (debug != null) {2089debug.println("destroySkey did not find secret key " +2090"with CKA_LABEL [" +2091alias +2092"]");2093}2094return false;2095}2096token.p11.C_DestroyObject(session.id(), h.handle);2097return true;2098} finally {2099token.releaseSession(session);2100}2101}21022103/**2104* return true if private key destroyed2105*/2106private boolean destroyPkey(byte[] cka_id)2107throws PKCS11Exception, KeyStoreException {2108Session session = null;2109try {2110session = token.getOpSession();21112112THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);2113if (h.type != ATTR_CLASS_PKEY) {2114if (debug != null) {2115debug.println2116("destroyPkey did not find private key with CKA_ID [" +2117getID(cka_id) +2118"]");2119}2120return false;2121}2122token.p11.C_DestroyObject(session.id(), h.handle);2123return true;2124} finally {2125token.releaseSession(session);2126}2127}21282129/**2130* build [alias + issuer + serialNumber] string from a cert2131*/2132private String getID(String alias, X509Certificate cert) {2133X500Principal issuer = cert.getIssuerX500Principal();2134BigInteger serialNum = cert.getSerialNumber();21352136return alias +2137ALIAS_SEP +2138issuer.getName(X500Principal.CANONICAL) +2139ALIAS_SEP +2140serialNum.toString();2141}21422143/**2144* build CKA_ID string from bytes2145*/2146private static String getID(byte[] bytes) {2147boolean printable = true;2148for (int i = 0; i < bytes.length; i++) {2149if (!DerValue.isPrintableStringChar((char)bytes[i])) {2150printable = false;2151break;2152}2153}21542155if (!printable) {2156return "0x" + Functions.toHexString(bytes);2157} else {2158return new String(bytes, UTF_8);2159}2160}21612162/**2163* find an object on the token2164*2165* @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY2166* @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY2167* @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY2168*/2169private THandle getTokenObject(Session session,2170CK_ATTRIBUTE type,2171byte[] cka_id,2172String cka_label)2173throws PKCS11Exception, KeyStoreException {21742175CK_ATTRIBUTE[] attrs;2176if (type == ATTR_CLASS_SKEY) {2177attrs = new CK_ATTRIBUTE[] {2178ATTR_SKEY_TOKEN_TRUE,2179new CK_ATTRIBUTE(CKA_LABEL, cka_label),2180type };2181} else {2182attrs = new CK_ATTRIBUTE[] {2183ATTR_TOKEN_TRUE,2184new CK_ATTRIBUTE(CKA_ID, cka_id),2185type };2186}2187long[] h = findObjects(session, attrs);2188if (h.length == 0) {2189if (debug != null) {2190if (type == ATTR_CLASS_SKEY) {2191debug.println("getTokenObject did not find secret key " +2192"with CKA_LABEL [" +2193cka_label +2194"]");2195} else if (type == ATTR_CLASS_CERT) {2196debug.println2197("getTokenObject did not find cert with CKA_ID [" +2198getID(cka_id) +2199"]");2200} else {2201debug.println("getTokenObject did not find private key " +2202"with CKA_ID [" +2203getID(cka_id) +2204"]");2205}2206}2207} else if (h.length == 1) {22082209// found object handle - return it2210return new THandle(h[0], type);22112212} else {22132214// found multiple object handles -2215// see if token ignored CKA_LABEL during search (e.g. NSS)22162217if (type == ATTR_CLASS_SKEY) {22182219ArrayList<THandle> list = new ArrayList<THandle>(h.length);2220for (int i = 0; i < h.length; i++) {22212222CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]2223{ new CK_ATTRIBUTE(CKA_LABEL) };2224token.p11.C_GetAttributeValue(session.id(), h[i], label);2225if (label[0].pValue != null &&2226cka_label.equals(new String(label[0].getCharArray()))) {2227list.add(new THandle(h[i], ATTR_CLASS_SKEY));2228}2229}2230if (list.size() == 1) {2231// yes, there was only one CKA_LABEL that matched2232return list.get(0);2233} else {2234throw new KeyStoreException("invalid KeyStore state: " +2235"found " +2236list.size() +2237" secret keys sharing CKA_LABEL [" +2238cka_label +2239"]");2240}2241} else if (type == ATTR_CLASS_CERT) {2242throw new KeyStoreException("invalid KeyStore state: " +2243"found " +2244h.length +2245" certificates sharing CKA_ID " +2246getID(cka_id));2247} else {2248throw new KeyStoreException("invalid KeyStore state: " +2249"found " +2250h.length +2251" private keys sharing CKA_ID " +2252getID(cka_id));2253}2254}2255return new THandle(NO_HANDLE, null);2256}22572258/**2259* Create a mapping of all key pairs, trusted certs, and secret keys2260* on the token into logical KeyStore entries unambiguously2261* accessible via an alias.2262*2263* If the token is removed, the map may contain stale values.2264* KeyStore.load should be called to re-create the map.2265*2266* Assume all private keys and matching certs share a unique CKA_ID.2267*2268* Assume all secret keys have a unique CKA_LABEL.2269*2270* @return true if multiple certs found sharing the same CKA_LABEL2271* (if so, write capabilities are disabled)2272*/2273private boolean mapLabels() throws2274PKCS11Exception, CertificateException, KeyStoreException {22752276CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {2277new CK_ATTRIBUTE(CKA_TRUSTED) };22782279Session session = null;2280try {2281session = token.getOpSession();22822283// get all private key CKA_IDs22842285ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();2286CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {2287ATTR_TOKEN_TRUE,2288ATTR_CLASS_PKEY,2289};2290long[] handles = findObjects(session, attrs);22912292for (long handle : handles) {2293attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };2294token.p11.C_GetAttributeValue(session.id(), handle, attrs);22952296if (attrs[0].pValue != null) {2297pkeyIDs.add(attrs[0].getByteArray());2298}2299}23002301// Get all certificates2302//2303// If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.2304//2305// Get the CKA_LABEL for each cert2306// (if the cert does not have a CKA_LABEL, use the CKA_ID).2307//2308// Map each cert to the its CKA_LABEL2309// (multiple certs may be mapped to a single CKA_LABEL)23102311HashMap<String, HashSet<AliasInfo>> certMap =2312new HashMap<String, HashSet<AliasInfo>>();23132314attrs = new CK_ATTRIBUTE[] {2315ATTR_TOKEN_TRUE,2316ATTR_CLASS_CERT,2317};2318handles = findObjects(session, attrs);23192320for (long handle : handles) {2321attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };23222323String cka_label = null;2324byte[] cka_id = null;2325try {2326token.p11.C_GetAttributeValue(session.id(), handle, attrs);2327if (attrs[0].pValue != null) {2328// there is a CKA_LABEL2329cka_label = new String(attrs[0].getCharArray());2330}2331} catch (PKCS11Exception pe) {2332if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {2333throw pe;2334}23352336// GetAttributeValue for CKA_LABEL not supported2337//2338// XXX SCA10002339}23402341// get CKA_ID23422343attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };2344token.p11.C_GetAttributeValue(session.id(), handle, attrs);2345if (attrs[0].pValue == null) {2346if (cka_label == null) {2347// no cka_label nor cka_id - ignore2348continue;2349}2350} else {2351if (cka_label == null) {2352// use CKA_ID as CKA_LABEL2353cka_label = getID(attrs[0].getByteArray());2354}2355cka_id = attrs[0].getByteArray();2356}23572358X509Certificate cert = loadCert(session, handle);23592360// get CKA_TRUSTED23612362boolean cka_trusted = false;23632364if (useSecmodTrust) {2365cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);2366} else {2367if (CKA_TRUSTED_SUPPORTED) {2368try {2369token.p11.C_GetAttributeValue2370(session.id(), handle, trustedAttr);2371cka_trusted = trustedAttr[0].getBoolean();2372} catch (PKCS11Exception pe) {2373if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {2374// XXX NSS, ibutton, sca10002375CKA_TRUSTED_SUPPORTED = false;2376if (debug != null) {2377debug.println2378("CKA_TRUSTED attribute not supported");2379}2380}2381}2382}2383}23842385HashSet<AliasInfo> infoSet = certMap.get(cka_label);2386if (infoSet == null) {2387infoSet = new HashSet<AliasInfo>(2);2388certMap.put(cka_label, infoSet);2389}23902391// initially create private key entry AliasInfo entries -2392// these entries will get resolved into their true2393// entry types later23942395infoSet.add(new AliasInfo2396(cka_label,2397cka_id,2398cka_trusted,2399cert));2400}24012402// create list secret key CKA_LABELS -2403// if there are duplicates (either between secret keys,2404// or between a secret key and another object),2405// throw an exception2406HashMap<String, AliasInfo> sKeyMap =2407new HashMap<String, AliasInfo>();24082409attrs = new CK_ATTRIBUTE[] {2410ATTR_SKEY_TOKEN_TRUE,2411ATTR_CLASS_SKEY,2412};2413handles = findObjects(session, attrs);24142415for (long handle : handles) {2416attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };2417token.p11.C_GetAttributeValue(session.id(), handle, attrs);2418if (attrs[0].pValue != null) {24192420// there is a CKA_LABEL2421String cka_label = new String(attrs[0].getCharArray());2422if (sKeyMap.get(cka_label) == null) {2423sKeyMap.put(cka_label, new AliasInfo(cka_label));2424} else {2425throw new KeyStoreException("invalid KeyStore state: " +2426"found multiple secret keys sharing same " +2427"CKA_LABEL [" +2428cka_label +2429"]");2430}2431}2432}24332434// update global aliasMap with alias mappings2435ArrayList<AliasInfo> matchedCerts =2436mapPrivateKeys(pkeyIDs, certMap);2437boolean sharedLabel = mapCerts(matchedCerts, certMap);2438mapSecretKeys(sKeyMap);24392440return sharedLabel;24412442} finally {2443token.releaseSession(session);2444}2445}24462447/**2448* for each private key CKA_ID, find corresponding cert with same CKA_ID.2449* if found cert, see if cert CKA_LABEL is unique.2450* if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.2451* if CKA_LABEL not unique, map private key/cert alias to:2452* CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL2453* if cert not found, ignore private key2454* (don't support private key entries without a cert chain yet)2455*2456* @return a list of AliasInfo entries that represents all matches2457*/2458private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,2459HashMap<String, HashSet<AliasInfo>> certMap)2460throws PKCS11Exception, CertificateException {24612462// reset global alias map2463aliasMap = new HashMap<String, AliasInfo>();24642465// list of matched certs that we will return2466ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();24672468for (byte[] pkeyID : pkeyIDs) {24692470// try to find a matching CKA_ID in a certificate24712472boolean foundMatch = false;2473Set<String> certLabels = certMap.keySet();2474for (String certLabel : certLabels) {24752476// get cert CKA_IDs (if present) for each cert24772478HashSet<AliasInfo> infoSet = certMap.get(certLabel);2479for (AliasInfo aliasInfo : infoSet) {2480if (Arrays.equals(pkeyID, aliasInfo.id)) {24812482// found private key with matching cert24832484if (infoSet.size() == 1) {2485// unique CKA_LABEL - use certLabel as alias2486aliasInfo.matched = true;2487aliasMap.put(certLabel, aliasInfo);2488} else {2489// create new alias2490aliasInfo.matched = true;2491aliasMap.put(getID(certLabel, aliasInfo.cert),2492aliasInfo);2493}2494matchedCerts.add(aliasInfo);2495foundMatch = true;2496break;2497}2498}2499if (foundMatch) {2500break;2501}2502}25032504if (!foundMatch) {2505if (debug != null) {2506debug.println2507("did not find match for private key with CKA_ID [" +2508getID(pkeyID) +2509"] (ignoring entry)");2510}2511}2512}25132514return matchedCerts;2515}25162517/**2518* for each cert not matched with a private key but is CKA_TRUSTED:2519* if CKA_LABEL unique, map cert to CKA_LABEL.2520* if CKA_LABEL not unique, map cert to [label+issuer+serialNum]2521*2522* if CKA_TRUSTED not supported, treat all certs not part of a chain2523* as trusted2524*2525* @return true if multiple certs found sharing the same CKA_LABEL2526*/2527private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,2528HashMap<String, HashSet<AliasInfo>> certMap)2529throws PKCS11Exception, CertificateException {25302531// load all cert chains2532for (AliasInfo aliasInfo : matchedCerts) {2533Session session = null;2534try {2535session = token.getOpSession();2536aliasInfo.chain = loadChain(session, aliasInfo.cert);2537} finally {2538token.releaseSession(session);2539}2540}25412542// find all certs in certMap not part of a cert chain2543// - these are trusted25442545boolean sharedLabel = false;25462547Set<String> certLabels = certMap.keySet();2548for (String certLabel : certLabels) {2549HashSet<AliasInfo> infoSet = certMap.get(certLabel);2550for (AliasInfo aliasInfo : infoSet) {25512552if (aliasInfo.matched == true) {2553// already found a private key match for this cert -2554// just continue2555aliasInfo.trusted = false;2556continue;2557}25582559// cert in this aliasInfo is not matched yet2560//2561// if CKA_TRUSTED_SUPPORTED == true,2562// then check if cert is trusted25632564if (CKA_TRUSTED_SUPPORTED) {2565if (aliasInfo.trusted) {2566// trusted certificate2567if (mapTrustedCert2568(certLabel, aliasInfo, infoSet) == true) {2569sharedLabel = true;2570}2571}2572continue;2573}25742575// CKA_TRUSTED_SUPPORTED == false2576//2577// XXX treat all certs not part of a chain as trusted2578// XXX2579// XXX Unsupported2580//2581// boolean partOfChain = false;2582// for (AliasInfo matchedInfo : matchedCerts) {2583// for (int i = 0; i < matchedInfo.chain.length; i++) {2584// if (matchedInfo.chain[i].equals(aliasInfo.cert)) {2585// partOfChain = true;2586// break;2587// }2588// }2589// if (partOfChain) {2590// break;2591// }2592// }2593//2594// if (!partOfChain) {2595// if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){2596// sharedLabel = true;2597// }2598// } else {2599// if (debug != null) {2600// debug.println("ignoring unmatched/untrusted cert " +2601// "that is part of cert chain - cert subject is [" +2602// aliasInfo.cert.getSubjectX500Principal().getName2603// (X500Principal.CANONICAL) +2604// "]");2605// }2606// }2607}2608}26092610return sharedLabel;2611}26122613private boolean mapTrustedCert(String certLabel,2614AliasInfo aliasInfo,2615HashSet<AliasInfo> infoSet) {26162617boolean sharedLabel = false;26182619aliasInfo.type = ATTR_CLASS_CERT;2620aliasInfo.trusted = true;2621if (infoSet.size() == 1) {2622// unique CKA_LABEL - use certLabel as alias2623aliasMap.put(certLabel, aliasInfo);2624} else {2625// create new alias2626sharedLabel = true;2627aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);2628}26292630return sharedLabel;2631}26322633/**2634* If the secret key shares a CKA_LABEL with another entry,2635* throw an exception2636*/2637private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)2638throws KeyStoreException {2639for (String label : sKeyMap.keySet()) {2640if (aliasMap.containsKey(label)) {2641throw new KeyStoreException("invalid KeyStore state: " +2642"found secret key sharing CKA_LABEL [" +2643label +2644"] with another token object");2645}2646}2647aliasMap.putAll(sKeyMap);2648}26492650private void dumpTokenMap() {2651Set<String> aliases = aliasMap.keySet();2652System.out.println("Token Alias Map:");2653if (aliases.isEmpty()) {2654System.out.println(" [empty]");2655} else {2656for (String s : aliases) {2657System.out.println(" " + s + aliasMap.get(s));2658}2659}2660}26612662private void checkWrite() throws KeyStoreException {2663if (writeDisabled) {2664throw new KeyStoreException2665("This PKCS11KeyStore does not support write capabilities");2666}2667}26682669private static final long[] LONG0 = new long[0];26702671private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)2672throws PKCS11Exception {2673Token token = session.token;2674long[] handles = LONG0;2675token.p11.C_FindObjectsInit(session.id(), attrs);2676while (true) {2677long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);2678if (h.length == 0) {2679break;2680}2681handles = P11Util.concat(handles, h);2682}2683token.p11.C_FindObjectsFinal(session.id());2684return handles;2685}26862687}268826892690