Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs11/P11KeyStore.java
38919 views
/*1* Copyright (c) 2003, 2019, 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;33import java.io.UnsupportedEncodingException;3435import java.util.Arrays;36import java.util.Collections;37import java.util.Date;38import java.util.Enumeration;39import java.util.ArrayList;40import java.util.HashSet;41import java.util.HashMap;42import java.util.Set;4344import java.security.*;45import java.security.KeyStore.*;4647import java.security.cert.Certificate;48import java.security.cert.X509Certificate;49import java.security.cert.CertificateFactory;50import java.security.cert.CertificateException;5152import java.security.interfaces.*;53import java.security.spec.*;5455import javax.crypto.SecretKey;56import javax.crypto.interfaces.*;5758import javax.security.auth.x500.X500Principal;59import javax.security.auth.login.LoginException;60import javax.security.auth.callback.Callback;61import javax.security.auth.callback.PasswordCallback;62import javax.security.auth.callback.CallbackHandler;63import javax.security.auth.callback.UnsupportedCallbackException;6465import sun.security.util.Debug;66import sun.security.util.DerValue;67import sun.security.util.ECUtil;6869import sun.security.pkcs11.Secmod.*;70import static sun.security.pkcs11.P11Util.*;7172import sun.security.pkcs11.wrapper.*;73import static sun.security.pkcs11.wrapper.PKCS11Constants.*;7475import sun.security.rsa.RSAKeyFactory;7677final class P11KeyStore extends KeyStoreSpi {7879private static final CK_ATTRIBUTE ATTR_CLASS_CERT =80new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE);81private static final CK_ATTRIBUTE ATTR_CLASS_PKEY =82new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY);83private static final CK_ATTRIBUTE ATTR_CLASS_SKEY =84new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);8586private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE =87new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509);8889private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE =90new CK_ATTRIBUTE(CKA_TOKEN, true);9192// XXX for testing purposes only93// - NSS doesn't support persistent secret keys94// (key type gets mangled if secret key is a token key)95// - if debug is turned on, then this is set to false96private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE;9798private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE =99new CK_ATTRIBUTE(CKA_TRUSTED, true);100private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE =101new CK_ATTRIBUTE(CKA_PRIVATE, true);102103private static final long NO_HANDLE = -1;104private static final long FINDOBJECTS_MAX = 100;105private static final String ALIAS_SEP = "/";106107private static final boolean NSS_TEST = false;108private static final Debug debug =109Debug.getInstance("pkcs11keystore");110private static boolean CKA_TRUSTED_SUPPORTED = true;111112private final Token token;113114// If multiple certs are found to share the same CKA_LABEL115// at load time (NSS-style keystore), then the keystore is read116// and the unique keystore aliases are mapped to the entries.117// However, write capabilities are disabled.118private boolean writeDisabled = false;119120// Map of unique keystore aliases to entries in the token121private HashMap<String, AliasInfo> aliasMap;122123// whether to use NSS Secmod info for trust attributes124private final boolean useSecmodTrust;125126// if useSecmodTrust == true, which type of trust we are interested in127private Secmod.TrustType nssTrustType;128129/**130* The underlying token may contain multiple certs belonging to the131* same "personality" (for example, a signing cert and encryption cert),132* all sharing the same CKA_LABEL. These must be resolved133* into unique keystore aliases.134*135* In addition, private keys and certs may not have a CKA_LABEL.136* It is assumed that a private key and corresponding certificate137* share the same CKA_ID, and that the CKA_ID is unique across the token.138* The CKA_ID may not be human-readable.139* These pairs must be resolved into unique keystore aliases.140*141* Furthermore, secret keys are assumed to have a CKA_LABEL142* unique across the entire token.143*144* When the KeyStore is loaded, instances of this class are145* created to represent the private keys/secret keys/certs146* that reside on the token.147*/148private static class AliasInfo {149150// CKA_CLASS - entry type151private CK_ATTRIBUTE type = null;152153// CKA_LABEL of cert and secret key154private String label = null;155156// CKA_ID of the private key/cert pair157private byte[] id = null;158159// CKA_TRUSTED - true if cert is trusted160private boolean trusted = false;161162// either end-entity cert or trusted cert depending on 'type'163private X509Certificate cert = null;164165// chain166private X509Certificate chain[] = null;167168// true if CKA_ID for private key and cert match up169private boolean matched = false;170171// SecretKeyEntry172public AliasInfo(String label) {173this.type = ATTR_CLASS_SKEY;174this.label = label;175}176177// PrivateKeyEntry178public AliasInfo(String label,179byte[] id,180boolean trusted,181X509Certificate cert) {182this.type = ATTR_CLASS_PKEY;183this.label = label;184this.id = id;185this.trusted = trusted;186this.cert = cert;187}188189public String toString() {190StringBuilder sb = new StringBuilder();191if (type == ATTR_CLASS_PKEY) {192sb.append("\ttype=[private key]\n");193} else if (type == ATTR_CLASS_SKEY) {194sb.append("\ttype=[secret key]\n");195} else if (type == ATTR_CLASS_CERT) {196sb.append("\ttype=[trusted cert]\n");197}198sb.append("\tlabel=[" + label + "]\n");199if (id == null) {200sb.append("\tid=[null]\n");201} else {202sb.append("\tid=" + P11KeyStore.getID(id) + "\n");203}204sb.append("\ttrusted=[" + trusted + "]\n");205sb.append("\tmatched=[" + matched + "]\n");206if (cert == null) {207sb.append("\tcert=[null]\n");208} else {209sb.append("\tcert=[\tsubject: " +210cert.getSubjectX500Principal() +211"\n\t\tissuer: " +212cert.getIssuerX500Principal() +213"\n\t\tserialNum: " +214cert.getSerialNumber().toString() +215"]");216}217return sb.toString();218}219}220221/**222* callback handler for passing password to Provider.login method223*/224private static class PasswordCallbackHandler implements CallbackHandler {225226private char[] password;227228private PasswordCallbackHandler(char[] password) {229if (password != null) {230this.password = password.clone();231}232}233234public void handle(Callback[] callbacks)235throws IOException, UnsupportedCallbackException {236if (!(callbacks[0] instanceof PasswordCallback)) {237throw new UnsupportedCallbackException(callbacks[0]);238}239PasswordCallback pc = (PasswordCallback)callbacks[0];240pc.setPassword(password); // this clones the password if not null241}242243protected void finalize() throws Throwable {244if (password != null) {245Arrays.fill(password, ' ');246}247super.finalize();248}249}250251/**252* getTokenObject return value.253*254* if object is not found, type is set to null.255* otherwise, type is set to the requested type.256*/257private static class THandle {258private final long handle; // token object handle259private final CK_ATTRIBUTE type; // CKA_CLASS260261private THandle(long handle, CK_ATTRIBUTE type) {262this.handle = handle;263this.type = type;264}265}266267P11KeyStore(Token token) {268this.token = token;269this.useSecmodTrust = token.provider.nssUseSecmodTrust;270}271272/**273* Returns the key associated with the given alias.274* The key must have been associated with275* the alias by a call to <code>setKeyEntry</code>,276* or by a call to <code>setEntry</code> with a277* <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>.278*279* @param alias the alias name280* @param password the password, which must be <code>null</code>281*282* @return the requested key, or null if the given alias does not exist283* or does not identify a key-related entry.284*285* @exception NoSuchAlgorithmException if the algorithm for recovering the286* key cannot be found287* @exception UnrecoverableKeyException if the key cannot be recovered288*/289public synchronized Key engineGetKey(String alias, char[] password)290throws NoSuchAlgorithmException, UnrecoverableKeyException {291292token.ensureValid();293if (password != null && !token.config.getKeyStoreCompatibilityMode()) {294throw new NoSuchAlgorithmException("password must be null");295}296297AliasInfo aliasInfo = aliasMap.get(alias);298if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {299return null;300}301302Session session = null;303try {304session = token.getOpSession();305306if (aliasInfo.type == ATTR_CLASS_PKEY) {307THandle h = getTokenObject(session,308aliasInfo.type,309aliasInfo.id,310null);311if (h.type == ATTR_CLASS_PKEY) {312return loadPkey(session, h.handle);313}314} else {315THandle h = getTokenObject(session,316ATTR_CLASS_SKEY,317null,318alias);319if (h.type == ATTR_CLASS_SKEY) {320return loadSkey(session, h.handle);321}322}323324// did not find anything325return null;326} catch (PKCS11Exception | KeyStoreException e) {327throw new ProviderException(e);328} finally {329token.releaseSession(session);330}331}332333/**334* Returns the certificate chain associated with the given alias.335* The certificate chain must have been associated with the alias336* by a call to <code>setKeyEntry</code>,337* or by a call to <code>setEntry</code> with a338* <code>PrivateKeyEntry</code>.339*340* @param alias the alias name341*342* @return the certificate chain (ordered with the user's certificate first343* and the root certificate authority last), or null if the given alias344* does not exist or does not contain a certificate chain345*/346public synchronized Certificate[] engineGetCertificateChain(String alias) {347348token.ensureValid();349350AliasInfo aliasInfo = aliasMap.get(alias);351if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) {352return null;353}354return aliasInfo.chain;355}356357/**358* Returns the certificate associated with the given alias.359*360* <p> If the given alias name identifies an entry361* created by a call to <code>setCertificateEntry</code>,362* or created by a call to <code>setEntry</code> with a363* <code>TrustedCertificateEntry</code>,364* then the trusted certificate contained in that entry is returned.365*366* <p> If the given alias name identifies an entry367* created by a call to <code>setKeyEntry</code>,368* or created by a call to <code>setEntry</code> with a369* <code>PrivateKeyEntry</code>,370* then the first element of the certificate chain in that entry371* (if a chain exists) is returned.372*373* @param alias the alias name374*375* @return the certificate, or null if the given alias does not exist or376* does not contain a certificate.377*/378public synchronized Certificate engineGetCertificate(String alias) {379token.ensureValid();380381AliasInfo aliasInfo = aliasMap.get(alias);382if (aliasInfo == null) {383return null;384}385return aliasInfo.cert;386}387388/**389* Returns the creation date of the entry identified by the given alias.390*391* @param alias the alias name392*393* @return the creation date of this entry, or null if the given alias does394* not exist395*/396public Date engineGetCreationDate(String alias) {397token.ensureValid();398throw new ProviderException(new UnsupportedOperationException());399}400401/**402* Assigns the given key to the given alias, protecting it with the given403* password.404*405* <p>If the given key is of type <code>java.security.PrivateKey</code>,406* it must be accompanied by a certificate chain certifying the407* corresponding public key.408*409* <p>If the given alias already exists, the keystore information410* associated with it is overridden by the given key (and possibly411* certificate chain).412*413* @param alias the alias name414* @param key the key to be associated with the alias415* @param password the password to protect the key416* @param chain the certificate chain for the corresponding public417* key (only required if the given key is of type418* <code>java.security.PrivateKey</code>).419*420* @exception KeyStoreException if the given key cannot be protected, or421* this operation fails for some other reason422*/423public synchronized void engineSetKeyEntry(String alias, Key key,424char[] password,425Certificate[] chain)426throws KeyStoreException {427428token.ensureValid();429checkWrite();430431if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) {432throw new KeyStoreException("key must be PrivateKey or SecretKey");433} else if (key instanceof PrivateKey && chain == null) {434throw new KeyStoreException435("PrivateKey must be accompanied by non-null chain");436} else if (key instanceof SecretKey && chain != null) {437throw new KeyStoreException438("SecretKey must be accompanied by null chain");439} else if (password != null &&440!token.config.getKeyStoreCompatibilityMode()) {441throw new KeyStoreException("Password must be null");442}443444KeyStore.Entry entry = null;445try {446if (key instanceof PrivateKey) {447entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, chain);448} else if (key instanceof SecretKey) {449entry = new KeyStore.SecretKeyEntry((SecretKey)key);450}451} catch (NullPointerException | IllegalArgumentException e) {452throw new KeyStoreException(e);453}454engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password));455}456457/**458* Assigns the given key (that has already been protected) to the given459* alias.460*461* <p>If the protected key is of type462* <code>java.security.PrivateKey</code>,463* it must be accompanied by a certificate chain certifying the464* corresponding public key.465*466* <p>If the given alias already exists, the keystore information467* associated with it is overridden by the given key (and possibly468* certificate chain).469*470* @param alias the alias name471* @param key the key (in protected format) to be associated with the alias472* @param chain the certificate chain for the corresponding public473* key (only useful if the protected key is of type474* <code>java.security.PrivateKey</code>).475*476* @exception KeyStoreException if this operation fails.477*/478public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)479throws KeyStoreException {480token.ensureValid();481throw new ProviderException(new UnsupportedOperationException());482}483484/**485* Assigns the given certificate to the given alias.486*487* <p> If the given alias identifies an existing entry488* created by a call to <code>setCertificateEntry</code>,489* or created by a call to <code>setEntry</code> with a490* <code>TrustedCertificateEntry</code>,491* the trusted certificate in the existing entry492* is overridden by the given certificate.493*494* @param alias the alias name495* @param cert the certificate496*497* @exception KeyStoreException if the given alias already exists and does498* not identify an entry containing a trusted certificate,499* or this operation fails for some other reason.500*/501public synchronized void engineSetCertificateEntry502(String alias, Certificate cert) throws KeyStoreException {503504token.ensureValid();505checkWrite();506507if (cert == null) {508throw new KeyStoreException("invalid null certificate");509}510511KeyStore.Entry entry = null;512entry = new KeyStore.TrustedCertificateEntry(cert);513engineSetEntry(alias, entry, null);514}515516/**517* Deletes the entry identified by the given alias from this keystore.518*519* @param alias the alias name520*521* @exception KeyStoreException if the entry cannot be removed.522*/523public synchronized void engineDeleteEntry(String alias)524throws KeyStoreException {525token.ensureValid();526527if (token.isWriteProtected()) {528throw new KeyStoreException("token write-protected");529}530checkWrite();531deleteEntry(alias);532}533534/**535* XXX - not sure whether to keep this536*/537private boolean deleteEntry(String alias) throws KeyStoreException {538AliasInfo aliasInfo = aliasMap.get(alias);539if (aliasInfo != null) {540541aliasMap.remove(alias);542543try {544if (aliasInfo.type == ATTR_CLASS_CERT) {545// trusted certificate entry546return destroyCert(aliasInfo.id);547} else if (aliasInfo.type == ATTR_CLASS_PKEY) {548// private key entry549return destroyPkey(aliasInfo.id) &&550destroyChain(aliasInfo.id);551} else if (aliasInfo.type == ATTR_CLASS_SKEY) {552// secret key entry553return destroySkey(alias);554} else {555throw new KeyStoreException("unexpected entry type");556}557} catch (PKCS11Exception | CertificateException e) {558throw new KeyStoreException(e);559}560}561return false;562}563564/**565* Lists all the alias names of this keystore.566*567* @return enumeration of the alias names568*/569public synchronized Enumeration<String> engineAliases() {570token.ensureValid();571572// don't want returned enumeration to iterate off actual keySet -573// otherwise applications that iterate and modify the keystore574// may run into concurrent modification problems575return Collections.enumeration(new HashSet<String>(aliasMap.keySet()));576}577578/**579* Checks if the given alias exists in this keystore.580*581* @param alias the alias name582*583* @return true if the alias exists, false otherwise584*/585public synchronized boolean engineContainsAlias(String alias) {586token.ensureValid();587return aliasMap.containsKey(alias);588}589590/**591* Retrieves the number of entries in this keystore.592*593* @return the number of entries in this keystore594*/595public synchronized int engineSize() {596token.ensureValid();597return aliasMap.size();598}599600/**601* Returns true if the entry identified by the given alias602* was created by a call to <code>setKeyEntry</code>,603* or created by a call to <code>setEntry</code> with a604* <code>PrivateKeyEntry</code> or a <code>SecretKeyEntry</code>.605*606* @param alias the alias for the keystore entry to be checked607*608* @return true if the entry identified by the given alias is a609* key-related, false otherwise.610*/611public synchronized boolean engineIsKeyEntry(String alias) {612token.ensureValid();613614AliasInfo aliasInfo = aliasMap.get(alias);615if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {616return false;617}618return true;619}620621/**622* Returns true if the entry identified by the given alias623* was created by a call to <code>setCertificateEntry</code>,624* or created by a call to <code>setEntry</code> with a625* <code>TrustedCertificateEntry</code>.626*627* @param alias the alias for the keystore entry to be checked628*629* @return true if the entry identified by the given alias contains a630* trusted certificate, false otherwise.631*/632public synchronized boolean engineIsCertificateEntry(String alias) {633token.ensureValid();634635AliasInfo aliasInfo = aliasMap.get(alias);636if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) {637return false;638}639return true;640}641642/**643* Returns the (alias) name of the first keystore entry whose certificate644* matches the given certificate.645*646* <p>This method attempts to match the given certificate with each647* keystore entry. If the entry being considered was648* created by a call to <code>setCertificateEntry</code>,649* or created by a call to <code>setEntry</code> with a650* <code>TrustedCertificateEntry</code>,651* then the given certificate is compared to that entry's certificate.652*653* <p> If the entry being considered was654* created by a call to <code>setKeyEntry</code>,655* or created by a call to <code>setEntry</code> with a656* <code>PrivateKeyEntry</code>,657* then the given certificate is compared to the first658* element of that entry's certificate chain.659*660* @param cert the certificate to match with.661*662* @return the alias name of the first entry with matching certificate,663* or null if no such entry exists in this keystore.664*/665public synchronized String engineGetCertificateAlias(Certificate cert) {666token.ensureValid();667Enumeration<String> e = engineAliases();668while (e.hasMoreElements()) {669String alias = e.nextElement();670Certificate tokenCert = engineGetCertificate(alias);671if (tokenCert != null && tokenCert.equals(cert)) {672return alias;673}674}675return null;676}677678/**679* engineStore currently is a No-op.680* Entries are stored to the token during engineSetEntry681*682* @param stream this must be <code>null</code>683* @param password this must be <code>null</code>684*/685public synchronized void engineStore(OutputStream stream, char[] password)686throws IOException, NoSuchAlgorithmException, CertificateException {687token.ensureValid();688if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {689throw new IOException("output stream must be null");690}691692if (password != null && !token.config.getKeyStoreCompatibilityMode()) {693throw new IOException("password must be null");694}695}696697/**698* engineStore currently is a No-op.699* Entries are stored to the token during engineSetEntry700*701* @param param this must be <code>null</code>702*703* @exception IllegalArgumentException if the given704* <code>KeyStore.LoadStoreParameter</code>705* input is not <code>null</code>706*/707public synchronized void engineStore(KeyStore.LoadStoreParameter param)708throws IOException, NoSuchAlgorithmException, CertificateException {709token.ensureValid();710if (param != null) {711throw new IllegalArgumentException712("LoadStoreParameter must be null");713}714}715716/**717* Loads the keystore.718*719* @param stream the input stream, which must be <code>null</code>720* @param password the password used to unlock the keystore,721* or <code>null</code> if the token supports a722* CKF_PROTECTED_AUTHENTICATION_PATH723*724* @exception IOException if the given <code>stream</code> is not725* <code>null</code>, if the token supports a726* CKF_PROTECTED_AUTHENTICATION_PATH and a non-null727* password is given, of if the token login operation failed728*/729public synchronized void engineLoad(InputStream stream, char[] password)730throws IOException, NoSuchAlgorithmException, CertificateException {731732token.ensureValid();733734if (NSS_TEST) {735ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);736}737738if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {739throw new IOException("input stream must be null");740}741742if (useSecmodTrust) {743nssTrustType = Secmod.TrustType.ALL;744}745746try {747if (password == null) {748login(null);749} else {750login(new PasswordCallbackHandler(password));751}752} catch(LoginException e) {753Throwable cause = e.getCause();754if (cause instanceof PKCS11Exception) {755PKCS11Exception pe = (PKCS11Exception) cause;756if (pe.getErrorCode() == CKR_PIN_INCORRECT) {757// if password is wrong, the cause of the IOException758// should be an UnrecoverableKeyException759throw new IOException("load failed",760new UnrecoverableKeyException().initCause(e));761}762}763throw new IOException("load failed", e);764}765766try {767if (mapLabels() == true) {768// CKA_LABELs are shared by multiple certs769writeDisabled = true;770}771if (debug != null) {772dumpTokenMap();773debug.println("P11KeyStore load. Entry count: " +774aliasMap.size());775}776} catch (KeyStoreException | PKCS11Exception e) {777throw new IOException("load failed", e);778}779}780781/**782* Loads the keystore using the given783* <code>KeyStore.LoadStoreParameter</code>.784*785* <p> The <code>LoadStoreParameter.getProtectionParameter()</code>786* method is expected to return a <code>KeyStore.PasswordProtection</code>787* object. The password is retrieved from that object and used788* to unlock the PKCS#11 token.789*790* <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH791* then the provided password must be <code>null</code>.792*793* @param param the <code>KeyStore.LoadStoreParameter</code>794*795* @exception IllegalArgumentException if the given796* <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,797* or if that parameter returns a <code>null</code>798* <code>ProtectionParameter</code> object.799* input is not recognized800* @exception IOException if the token supports a801* CKF_PROTECTED_AUTHENTICATION_PATH and the provided password802* is non-null, or if the token login operation fails803*/804public synchronized void engineLoad(KeyStore.LoadStoreParameter param)805throws IOException, NoSuchAlgorithmException,806CertificateException {807808token.ensureValid();809810if (NSS_TEST) {811ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);812}813814// if caller wants to pass a NULL password,815// force it to pass a non-NULL PasswordProtection that returns816// a NULL password817818if (param == null) {819throw new IllegalArgumentException820("invalid null LoadStoreParameter");821}822if (useSecmodTrust) {823if (param instanceof Secmod.KeyStoreLoadParameter) {824nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();825} else {826nssTrustType = Secmod.TrustType.ALL;827}828}829830CallbackHandler handler;831KeyStore.ProtectionParameter pp = param.getProtectionParameter();832if (pp instanceof PasswordProtection) {833char[] password = ((PasswordProtection)pp).getPassword();834if (password == null) {835handler = null;836} else {837handler = new PasswordCallbackHandler(password);838}839} else if (pp instanceof CallbackHandlerProtection) {840handler = ((CallbackHandlerProtection)pp).getCallbackHandler();841} else {842throw new IllegalArgumentException843("ProtectionParameter must be either " +844"PasswordProtection or CallbackHandlerProtection");845}846847try {848login(handler);849if (mapLabels() == true) {850// CKA_LABELs are shared by multiple certs851writeDisabled = true;852}853if (debug != null) {854dumpTokenMap();855}856} catch (LoginException | KeyStoreException | PKCS11Exception e) {857throw new IOException("load failed", e);858}859}860861private void login(CallbackHandler handler) throws LoginException {862if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {863token.provider.login(null, handler);864} else {865// token supports protected authentication path866// (external pin-pad, for example)867if (handler != null &&868!token.config.getKeyStoreCompatibilityMode()) {869throw new LoginException("can not specify password if token " +870"supports protected authentication path");871}872873// must rely on application-set or default handler874// if one is necessary875token.provider.login(null, null);876}877}878879/**880* Get a <code>KeyStore.Entry</code> for the specified alias881*882* @param alias get the <code>KeyStore.Entry</code> for this alias883* @param protParam this must be <code>null</code>884*885* @return the <code>KeyStore.Entry</code> for the specified alias,886* or <code>null</code> if there is no such entry887*888* @exception KeyStoreException if the operation failed889* @exception NoSuchAlgorithmException if the algorithm for recovering the890* entry cannot be found891* @exception UnrecoverableEntryException if the specified892* <code>protParam</code> were insufficient or invalid893*894* @since 1.5895*/896public synchronized KeyStore.Entry engineGetEntry(String alias,897KeyStore.ProtectionParameter protParam)898throws KeyStoreException, NoSuchAlgorithmException,899UnrecoverableEntryException {900901token.ensureValid();902903if (protParam != null &&904protParam instanceof KeyStore.PasswordProtection &&905((KeyStore.PasswordProtection)protParam).getPassword() != null &&906!token.config.getKeyStoreCompatibilityMode()) {907throw new KeyStoreException("ProtectionParameter must be null");908}909910AliasInfo aliasInfo = aliasMap.get(alias);911if (aliasInfo == null) {912if (debug != null) {913debug.println("engineGetEntry did not find alias [" +914alias +915"] in map");916}917return null;918}919920Session session = null;921try {922session = token.getOpSession();923924if (aliasInfo.type == ATTR_CLASS_CERT) {925// trusted certificate entry926if (debug != null) {927debug.println("engineGetEntry found trusted cert entry");928}929return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);930} else if (aliasInfo.type == ATTR_CLASS_SKEY) {931// secret key entry932if (debug != null) {933debug.println("engineGetEntry found secret key entry");934}935936THandle h = getTokenObject937(session, ATTR_CLASS_SKEY, null, aliasInfo.label);938if (h.type != ATTR_CLASS_SKEY) {939throw new KeyStoreException940("expected but could not find secret key");941} else {942SecretKey skey = loadSkey(session, h.handle);943return new KeyStore.SecretKeyEntry(skey);944}945} else {946// private key entry947if (debug != null) {948debug.println("engineGetEntry found private key entry");949}950951THandle h = getTokenObject952(session, ATTR_CLASS_PKEY, aliasInfo.id, null);953if (h.type != ATTR_CLASS_PKEY) {954throw new KeyStoreException955("expected but could not find private key");956} else {957PrivateKey pkey = loadPkey(session, h.handle);958Certificate[] chain = aliasInfo.chain;959if ((pkey != null) && (chain != null)) {960return new KeyStore.PrivateKeyEntry(pkey, chain);961} else {962if (debug != null) {963debug.println964("engineGetEntry got null cert chain or private key");965}966}967}968}969return null;970} catch (PKCS11Exception pe) {971throw new KeyStoreException(pe);972} finally {973token.releaseSession(session);974}975}976977/**978* Save a <code>KeyStore.Entry</code> under the specified alias.979*980* <p> If an entry already exists for the specified alias,981* it is overridden.982*983* <p> This KeyStore implementation only supports the standard984* entry types, and only supports X509Certificates in985* TrustedCertificateEntries. Also, this implementation does not support986* protecting entries using a different password987* from the one used for token login.988*989* <p> Entries are immediately stored on the token.990*991* @param alias save the <code>KeyStore.Entry</code> under this alias992* @param entry the <code>Entry</code> to save993* @param protParam this must be <code>null</code>994*995* @exception KeyStoreException if this operation fails996*997* @since 1.5998*/999public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,1000KeyStore.ProtectionParameter protParam)1001throws KeyStoreException {10021003token.ensureValid();1004checkWrite();10051006if (protParam != null &&1007protParam instanceof KeyStore.PasswordProtection &&1008((KeyStore.PasswordProtection)protParam).getPassword() != null &&1009!token.config.getKeyStoreCompatibilityMode()) {1010throw new KeyStoreException(new UnsupportedOperationException1011("ProtectionParameter must be null"));1012}10131014if (token.isWriteProtected()) {1015throw new KeyStoreException("token write-protected");1016}10171018if (entry instanceof KeyStore.TrustedCertificateEntry) {10191020if (useSecmodTrust == false) {1021// PKCS #11 does not allow app to modify trusted certs -1022throw new KeyStoreException(new UnsupportedOperationException1023("trusted certificates may only be set by " +1024"token initialization application"));1025}1026Module module = token.provider.nssModule;1027if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {1028// XXX allow TRUSTANCHOR module1029throw new KeyStoreException("Trusted certificates can only be "1030+ "added to the NSS KeyStore module");1031}1032Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();1033if (cert instanceof X509Certificate == false) {1034throw new KeyStoreException("Certificate must be an X509Certificate");1035}1036X509Certificate xcert = (X509Certificate)cert;1037AliasInfo info = aliasMap.get(alias);1038if (info != null) {1039// XXX try to update1040deleteEntry(alias);1041}1042try {1043storeCert(alias, xcert);1044module.setTrust(token, xcert);1045mapLabels();1046} catch (PKCS11Exception | CertificateException e) {1047throw new KeyStoreException(e);1048}10491050} else {10511052if (entry instanceof KeyStore.PrivateKeyEntry) {10531054PrivateKey key =1055((KeyStore.PrivateKeyEntry)entry).getPrivateKey();1056if (!(key instanceof P11Key) &&1057!(key instanceof RSAPrivateKey) &&1058!(key instanceof DSAPrivateKey) &&1059!(key instanceof DHPrivateKey) &&1060!(key instanceof ECPrivateKey)) {1061throw new KeyStoreException("unsupported key type: " +1062key.getClass().getName());1063}10641065// only support X509Certificate chains1066Certificate[] chain =1067((KeyStore.PrivateKeyEntry)entry).getCertificateChain();1068if (!(chain instanceof X509Certificate[])) {1069throw new KeyStoreException1070(new UnsupportedOperationException1071("unsupported certificate array type: " +1072chain.getClass().getName()));1073}10741075try {1076boolean updatedAlias = false;1077Set<String> aliases = aliasMap.keySet();1078for (String oldAlias : aliases) {10791080// see if there's an existing entry with the same info10811082AliasInfo aliasInfo = aliasMap.get(oldAlias);1083if (aliasInfo.type == ATTR_CLASS_PKEY &&1084aliasInfo.cert.getPublicKey().equals1085(chain[0].getPublicKey())) {10861087// found existing entry -1088// caller is renaming entry or updating cert chain1089//1090// set new CKA_LABEL/CKA_ID1091// and update certs if necessary10921093updatePkey(alias,1094aliasInfo.id,1095(X509Certificate[])chain,1096!aliasInfo.cert.equals(chain[0]));1097updatedAlias = true;1098break;1099}1100}11011102if (!updatedAlias) {1103// caller adding new entry1104engineDeleteEntry(alias);1105storePkey(alias, (KeyStore.PrivateKeyEntry)entry);1106}11071108} catch (PKCS11Exception | CertificateException pe) {1109throw new KeyStoreException(pe);1110}11111112} else if (entry instanceof KeyStore.SecretKeyEntry) {11131114KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;1115SecretKey skey = ske.getSecretKey();11161117try {1118// first check if the key already exists1119AliasInfo aliasInfo = aliasMap.get(alias);11201121if (aliasInfo != null) {1122engineDeleteEntry(alias);1123}1124storeSkey(alias, ske);11251126} catch (PKCS11Exception pe) {1127throw new KeyStoreException(pe);1128}11291130} else {1131throw new KeyStoreException(new UnsupportedOperationException1132("unsupported entry type: " + entry.getClass().getName()));1133}11341135try {11361137// XXX NSS does not write out the CKA_ID we pass to them1138//1139// therefore we must re-map labels1140// (can not simply update aliasMap)11411142mapLabels();1143if (debug != null) {1144dumpTokenMap();1145}1146} catch (PKCS11Exception | CertificateException pe) {1147throw new KeyStoreException(pe);1148}1149}11501151if (debug != null) {1152debug.println1153("engineSetEntry added new entry for [" +1154alias +1155"] to token");1156}1157}11581159/**1160* Determines if the keystore <code>Entry</code> for the specified1161* <code>alias</code> is an instance or subclass of the specified1162* <code>entryClass</code>.1163*1164* @param alias the alias name1165* @param entryClass the entry class1166*1167* @return true if the keystore <code>Entry</code> for the specified1168* <code>alias</code> is an instance or subclass of the1169* specified <code>entryClass</code>, false otherwise1170*/1171public synchronized boolean engineEntryInstanceOf1172(String alias, Class<? extends KeyStore.Entry> entryClass) {1173token.ensureValid();1174return super.engineEntryInstanceOf(alias, entryClass);1175}11761177private X509Certificate loadCert(Session session, long oHandle)1178throws PKCS11Exception, CertificateException {11791180CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]1181{ new CK_ATTRIBUTE(CKA_VALUE) };1182token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);11831184byte[] bytes = attrs[0].getByteArray();1185if (bytes == null) {1186throw new CertificateException1187("unexpectedly retrieved null byte array");1188}1189CertificateFactory cf = CertificateFactory.getInstance("X.509");1190return (X509Certificate)cf.generateCertificate1191(new ByteArrayInputStream(bytes));1192}11931194private X509Certificate[] loadChain(Session session,1195X509Certificate endCert)1196throws PKCS11Exception, CertificateException {11971198ArrayList<X509Certificate> lChain = null;11991200if (endCert.getSubjectX500Principal().equals1201(endCert.getIssuerX500Principal())) {1202// self signed1203return new X509Certificate[] { endCert };1204} else {1205lChain = new ArrayList<X509Certificate>();1206lChain.add(endCert);1207}12081209// try loading remaining certs in chain by following1210// issuer->subject links12111212X509Certificate next = endCert;1213while (true) {1214CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1215ATTR_TOKEN_TRUE,1216ATTR_CLASS_CERT,1217new CK_ATTRIBUTE(CKA_SUBJECT,1218next.getIssuerX500Principal().getEncoded()) };1219long[] ch = findObjects(session, attrs);12201221if (ch == null || ch.length == 0) {1222// done1223break;1224} else {1225// if more than one found, use first1226if (debug != null && ch.length > 1) {1227debug.println("engineGetEntry found " +1228ch.length +1229" certificate entries for subject [" +1230next.getIssuerX500Principal().toString() +1231"] in token - using first entry");1232}12331234next = loadCert(session, ch[0]);1235lChain.add(next);1236if (next.getSubjectX500Principal().equals1237(next.getIssuerX500Principal())) {1238// self signed1239break;1240}1241}1242}12431244return lChain.toArray(new X509Certificate[lChain.size()]);1245}12461247private SecretKey loadSkey(Session session, long oHandle)1248throws PKCS11Exception {12491250CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1251new CK_ATTRIBUTE(CKA_KEY_TYPE) };1252token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1253long kType = attrs[0].getLong();12541255String keyType = null;1256int keyLength = -1;12571258// XXX NSS mangles the stored key type for secret key token objects12591260if (kType == CKK_DES || kType == CKK_DES3) {1261if (kType == CKK_DES) {1262keyType = "DES";1263keyLength = 64;1264} else if (kType == CKK_DES3) {1265keyType = "DESede";1266keyLength = 192;1267}1268} else {1269if (kType == CKK_AES) {1270keyType = "AES";1271} else if (kType == CKK_BLOWFISH) {1272keyType = "Blowfish";1273} else if (kType == CKK_RC4) {1274keyType = "ARCFOUR";1275} else {1276if (debug != null) {1277debug.println("unknown key type [" +1278kType +1279"] - using 'Generic Secret'");1280}1281keyType = "Generic Secret";1282}12831284// XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?1285if (NSS_TEST) {1286keyLength = 128;1287} else {1288attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };1289token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1290keyLength = (int)attrs[0].getLong();1291}1292}12931294return P11Key.secretKey(session, oHandle, keyType, keyLength, null);1295}12961297private PrivateKey loadPkey(Session session, long oHandle)1298throws PKCS11Exception, KeyStoreException {12991300CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1301new CK_ATTRIBUTE(CKA_KEY_TYPE) };1302token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1303long kType = attrs[0].getLong();1304String keyType = null;1305int keyLength = 0;13061307if (kType == CKK_RSA) {13081309keyType = "RSA";13101311attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };1312token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1313BigInteger modulus = attrs[0].getBigInteger();1314keyLength = modulus.bitLength();13151316// This check will combine our "don't care" values here1317// with the system-wide min/max values.1318try {1319RSAKeyFactory.checkKeyLengths(keyLength, null,1320-1, Integer.MAX_VALUE);1321} catch (InvalidKeyException e) {1322throw new KeyStoreException(e.getMessage());1323}13241325return P11Key.privateKey(session,1326oHandle,1327keyType,1328keyLength,1329null);13301331} else if (kType == CKK_DSA) {13321333keyType = "DSA";13341335attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };1336token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1337BigInteger prime = attrs[0].getBigInteger();1338keyLength = prime.bitLength();13391340return P11Key.privateKey(session,1341oHandle,1342keyType,1343keyLength,1344null);13451346} else if (kType == CKK_DH) {13471348keyType = "DH";13491350attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };1351token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1352BigInteger prime = attrs[0].getBigInteger();1353keyLength = prime.bitLength();13541355return P11Key.privateKey(session,1356oHandle,1357keyType,1358keyLength,1359null);13601361} else if (kType == CKK_EC) {13621363attrs = new CK_ATTRIBUTE[] {1364new CK_ATTRIBUTE(CKA_EC_PARAMS),1365};1366token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);1367byte[] encodedParams = attrs[0].getByteArray();1368try {1369ECParameterSpec params =1370ECUtil.getECParameterSpec(null, encodedParams);1371keyLength = params.getCurve().getField().getFieldSize();1372} catch (IOException e) {1373// we do not want to accept key with unsupported parameters1374throw new KeyStoreException("Unsupported parameters", e);1375}13761377return P11Key.privateKey(session, oHandle, "EC", keyLength, null);13781379} else {1380if (debug != null) {1381debug.println("unknown key type [" + kType + "]");1382}1383throw new KeyStoreException("unknown key type");1384}1385}138613871388/**1389* XXX On ibutton, when you C_SetAttribute(CKA_ID) for a private key1390* it not only changes the CKA_ID of the private key,1391* it changes the CKA_ID of the corresponding cert too.1392* And vice versa.1393*1394* XXX On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)1395* for a private key, and then try to delete the corresponding cert.1396* So this code reverses the order.1397* After the cert is first destroyed (if necessary),1398* then the CKA_ID of the private key can be changed successfully.1399*1400* @param replaceCert if true, then caller is updating alias info for1401* existing cert (only update CKA_ID/CKA_LABEL).1402* if false, then caller is updating cert chain1403* (delete old end cert and add new chain).1404*/1405private void updatePkey(String alias,1406byte[] cka_id,1407X509Certificate[] chain,1408boolean replaceCert) throws1409KeyStoreException, CertificateException, PKCS11Exception {14101411// XXX1412//1413// always set replaceCert to true1414//1415// NSS does not allow resetting of CKA_LABEL on an existing cert1416// (C_SetAttribute call succeeds, but is ignored)14171418replaceCert = true;14191420Session session = null;1421try {1422session = token.getOpSession();14231424// first get private key object handle and hang onto it14251426THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);1427long pKeyHandle;1428if (h.type == ATTR_CLASS_PKEY) {1429pKeyHandle = h.handle;1430} else {1431throw new KeyStoreException1432("expected but could not find private key " +1433"with CKA_ID " +1434getID(cka_id));1435}14361437// next find existing end entity cert14381439h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1440if (h.type != ATTR_CLASS_CERT) {1441throw new KeyStoreException1442("expected but could not find certificate " +1443"with CKA_ID " +1444getID(cka_id));1445} else {1446if (replaceCert) {1447// replacing existing cert and chain1448destroyChain(cka_id);1449} else {1450// renaming alias for existing cert1451CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1452new CK_ATTRIBUTE(CKA_LABEL, alias),1453new CK_ATTRIBUTE(CKA_ID, alias) };1454token.p11.C_SetAttributeValue1455(session.id(), h.handle, attrs);1456}1457}14581459// add new chain14601461if (replaceCert) {1462// add all certs in chain1463storeChain(alias, chain);1464} else {1465// already updated alias info for existing end cert -1466// just update CA certs1467storeCaCerts(chain, 1);1468}14691470// finally update CKA_ID for private key1471//1472// ibutton may have already done this (that is ok)14731474CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1475new CK_ATTRIBUTE(CKA_ID, alias) };1476token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);14771478if (debug != null) {1479debug.println("updatePkey set new alias [" +1480alias +1481"] for private key entry");1482}1483} finally {1484token.releaseSession(session);1485}1486}14871488// retrieves the native key handle and either update it directly or make a copy1489private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)1490throws PKCS11Exception {14911492// if token key, update alias.1493// if session key, convert to token key.14941495Session session = null;1496long keyID = key.getKeyID();1497try {1498session = token.getOpSession();1499if (key.tokenObject == true) {1500// token key - set new CKA_ID15011502CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1503new CK_ATTRIBUTE(CKA_ID, alias) };1504token.p11.C_SetAttributeValue1505(session.id(), keyID, attrs);1506if (debug != null) {1507debug.println("updateP11Pkey set new alias [" +1508alias +1509"] for key entry");1510}1511} else {1512// session key - convert to token key and set CKA_ID15131514CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1515ATTR_TOKEN_TRUE,1516new CK_ATTRIBUTE(CKA_ID, alias),1517};1518if (attribute != null) {1519attrs = addAttribute(attrs, attribute);1520}1521// creates a new token key with the desired CKA_ID1522token.p11.C_CopyObject(session.id(), keyID, attrs);1523if (debug != null) {1524debug.println("updateP11Pkey copied private session key " +1525"for [" +1526alias +1527"] to token entry");1528}1529}1530} finally {1531token.releaseSession(session);1532key.releaseKeyID();1533}1534}15351536private void storeCert(String alias, X509Certificate cert)1537throws PKCS11Exception, CertificateException {15381539ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();1540attrList.add(ATTR_TOKEN_TRUE);1541attrList.add(ATTR_CLASS_CERT);1542attrList.add(ATTR_X509_CERT_TYPE);1543attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,1544cert.getSubjectX500Principal().getEncoded()));1545attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,1546cert.getIssuerX500Principal().getEncoded()));1547attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,1548cert.getSerialNumber().toByteArray()));1549attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));15501551if (alias != null) {1552attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));1553attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));1554} else {1555// ibutton requires something to be set1556// - alias must be unique1557attrList.add(new CK_ATTRIBUTE(CKA_ID,1558getID(cert.getSubjectX500Principal().getName1559(X500Principal.CANONICAL), cert)));1560}15611562Session session = null;1563try {1564session = token.getOpSession();1565token.p11.C_CreateObject(session.id(),1566attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));1567} finally {1568token.releaseSession(session);1569}1570}15711572private void storeChain(String alias, X509Certificate[] chain)1573throws PKCS11Exception, CertificateException {15741575// add new chain1576//1577// end cert has CKA_LABEL and CKA_ID set to alias.1578// other certs in chain have neither set.15791580storeCert(alias, chain[0]);1581storeCaCerts(chain, 1);1582}15831584private void storeCaCerts(X509Certificate[] chain, int start)1585throws PKCS11Exception, CertificateException {15861587// do not add duplicate CA cert if already in token1588//1589// XXX ibutton stores duplicate CA certs, NSS does not15901591Session session = null;1592HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();1593try {1594session = token.getOpSession();1595CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1596ATTR_TOKEN_TRUE,1597ATTR_CLASS_CERT };1598long[] handles = findObjects(session, attrs);15991600// load certs currently on the token1601for (long handle : handles) {1602cacerts.add(loadCert(session, handle));1603}1604} finally {1605token.releaseSession(session);1606}16071608for (int i = start; i < chain.length; i++) {1609if (!cacerts.contains(chain[i])) {1610storeCert(null, chain[i]);1611} else if (debug != null) {1612debug.println("ignoring duplicate CA cert for [" +1613chain[i].getSubjectX500Principal() +1614"]");1615}1616}1617}16181619private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)1620throws PKCS11Exception, KeyStoreException {16211622SecretKey skey = ske.getSecretKey();1623// No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since1624// they are handled in P11SecretKeyFactory.createKey() method.1625CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {1626ATTR_SKEY_TOKEN_TRUE,1627ATTR_PRIVATE_TRUE,1628new CK_ATTRIBUTE(CKA_LABEL, alias),1629};1630try {1631P11SecretKeyFactory.convertKey(token, skey, null, attrs);1632} catch (InvalidKeyException ike) {1633// re-throw KeyStoreException to match javadoc1634throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);1635}16361637// update global alias map1638aliasMap.put(alias, new AliasInfo(alias));16391640if (debug != null) {1641debug.println("storeSkey created token secret key for [" +1642alias + "]");1643}1644}16451646private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {1647int n = attrs.length;1648CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];1649System.arraycopy(attrs, 0, newAttrs, 0, n);1650newAttrs[n] = attr;1651return newAttrs;1652}16531654private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)1655throws PKCS11Exception, CertificateException, KeyStoreException {16561657PrivateKey key = pke.getPrivateKey();1658CK_ATTRIBUTE[] attrs = null;16591660// If the key is a token object on this token, update it instead1661// of creating a duplicate key object.1662// Otherwise, treat a P11Key like any other key, if is is extractable.1663if (key instanceof P11Key) {1664P11Key p11Key = (P11Key)key;1665if (p11Key.tokenObject && (p11Key.token == this.token)) {1666updateP11Pkey(alias, null, p11Key);1667storeChain(alias, (X509Certificate[])pke.getCertificateChain());1668return;1669}1670}16711672boolean useNDB = token.config.getNssNetscapeDbWorkaround();1673PublicKey publicKey = pke.getCertificate().getPublicKey();16741675if (key instanceof RSAPrivateKey) {16761677X509Certificate cert = (X509Certificate)pke.getCertificate();1678attrs = getRsaPrivKeyAttrs1679(alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());16801681} else if (key instanceof DSAPrivateKey) {16821683DSAPrivateKey dsaKey = (DSAPrivateKey)key;16841685CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1686if (idAttrs[0] == null) {1687idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1688}16891690attrs = new CK_ATTRIBUTE[] {1691ATTR_TOKEN_TRUE,1692ATTR_CLASS_PKEY,1693ATTR_PRIVATE_TRUE,1694new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),1695idAttrs[0],1696new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),1697new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),1698new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),1699new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),1700};1701if (idAttrs[1] != null) {1702attrs = addAttribute(attrs, idAttrs[1]);1703}17041705attrs = token.getAttributes1706(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);17071708if (debug != null) {1709debug.println("storePkey created DSA template");1710}17111712} else if (key instanceof DHPrivateKey) {17131714DHPrivateKey dhKey = (DHPrivateKey)key;17151716CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1717if (idAttrs[0] == null) {1718idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1719}17201721attrs = new CK_ATTRIBUTE[] {1722ATTR_TOKEN_TRUE,1723ATTR_CLASS_PKEY,1724ATTR_PRIVATE_TRUE,1725new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),1726idAttrs[0],1727new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),1728new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),1729new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),1730};1731if (idAttrs[1] != null) {1732attrs = addAttribute(attrs, idAttrs[1]);1733}17341735attrs = token.getAttributes1736(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);17371738} else if (key instanceof ECPrivateKey) {17391740ECPrivateKey ecKey = (ECPrivateKey)key;17411742CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);1743if (idAttrs[0] == null) {1744idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);1745}17461747byte[] encodedParams =1748ECUtil.encodeECParameterSpec(null, ecKey.getParams());1749attrs = new CK_ATTRIBUTE[] {1750ATTR_TOKEN_TRUE,1751ATTR_CLASS_PKEY,1752ATTR_PRIVATE_TRUE,1753new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),1754idAttrs[0],1755new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),1756new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),1757};1758if (idAttrs[1] != null) {1759attrs = addAttribute(attrs, idAttrs[1]);1760}17611762attrs = token.getAttributes1763(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);17641765if (debug != null) {1766debug.println("storePkey created EC template");1767}17681769} else if (key instanceof P11Key) {1770// sensitive/non-extractable P11Key1771P11Key p11Key = (P11Key)key;1772if (p11Key.token != this.token) {1773throw new KeyStoreException1774("Cannot move sensitive keys across tokens");1775}1776CK_ATTRIBUTE netscapeDB = null;1777if (useNDB) {1778// Note that this currently fails due to an NSS bug.1779// They do not allow the CKA_NETSCAPE_DB attribute to be1780// specified during C_CopyObject() and fail with1781// CKR_ATTRIBUTE_READ_ONLY.1782// But if we did not specify it, they would fail with1783// CKA_TEMPLATE_INCOMPLETE, so leave this code in here.1784CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);1785netscapeDB = idAttrs[1];1786}1787// Update the key object.1788updateP11Pkey(alias, netscapeDB, p11Key);1789storeChain(alias, (X509Certificate[])pke.getCertificateChain());1790return;17911792} else {1793throw new KeyStoreException("unsupported key type: " + key);1794}17951796Session session = null;1797try {1798session = token.getOpSession();17991800// create private key entry1801token.p11.C_CreateObject(session.id(), attrs);1802if (debug != null) {1803debug.println("storePkey created token key for [" +1804alias +1805"]");1806}1807} finally {1808token.releaseSession(session);1809}18101811storeChain(alias, (X509Certificate[])pke.getCertificateChain());1812}18131814private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,1815RSAPrivateKey key,1816X500Principal subject) throws PKCS11Exception {18171818// subject is currently ignored - could be used to set CKA_SUBJECT18191820CK_ATTRIBUTE[] attrs = null;1821if (key instanceof RSAPrivateCrtKey) {18221823if (debug != null) {1824debug.println("creating RSAPrivateCrtKey attrs");1825}18261827RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;18281829attrs = new CK_ATTRIBUTE[] {1830ATTR_TOKEN_TRUE,1831ATTR_CLASS_PKEY,1832ATTR_PRIVATE_TRUE,1833new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),1834new CK_ATTRIBUTE(CKA_ID, alias),1835new CK_ATTRIBUTE(CKA_MODULUS,1836rsaKey.getModulus()),1837new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,1838rsaKey.getPrivateExponent()),1839new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,1840rsaKey.getPublicExponent()),1841new CK_ATTRIBUTE(CKA_PRIME_1,1842rsaKey.getPrimeP()),1843new CK_ATTRIBUTE(CKA_PRIME_2,1844rsaKey.getPrimeQ()),1845new CK_ATTRIBUTE(CKA_EXPONENT_1,1846rsaKey.getPrimeExponentP()),1847new CK_ATTRIBUTE(CKA_EXPONENT_2,1848rsaKey.getPrimeExponentQ()),1849new CK_ATTRIBUTE(CKA_COEFFICIENT,1850rsaKey.getCrtCoefficient()) };1851attrs = token.getAttributes1852(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);18531854} else {18551856if (debug != null) {1857debug.println("creating RSAPrivateKey attrs");1858}18591860RSAPrivateKey rsaKey = key;18611862attrs = new CK_ATTRIBUTE[] {1863ATTR_TOKEN_TRUE,1864ATTR_CLASS_PKEY,1865ATTR_PRIVATE_TRUE,1866new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),1867new CK_ATTRIBUTE(CKA_ID, alias),1868new CK_ATTRIBUTE(CKA_MODULUS,1869rsaKey.getModulus()),1870new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,1871rsaKey.getPrivateExponent()) };1872attrs = token.getAttributes1873(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);1874}18751876return attrs;1877}18781879/**1880* Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be1881* used for this private key. It uses the same algorithm to calculate the1882* values as NSS. The public and private keys MUST match for the result to1883* be correct.1884*1885* It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB1886* at index 1. The boolean flags determine what is to be calculated.1887* If false or if we could not calculate the value, that element is null.1888*1889* NOTE that we currently do not use the CKA_ID value calculated by this1890* method.1891*/1892private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,1893PublicKey publicKey, boolean id, boolean netscapeDb) {1894CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];1895if ((id || netscapeDb) == false) {1896return attrs;1897}1898String alg = privateKey.getAlgorithm();1899if (alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {1900if (id) {1901BigInteger n = ((RSAPublicKey)publicKey).getModulus();1902attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));1903}1904// CKA_NETSCAPE_DB not needed for RSA public keys1905} else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {1906BigInteger y = ((DSAPublicKey)publicKey).getY();1907if (id) {1908attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));1909}1910if (netscapeDb) {1911attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);1912}1913} else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {1914BigInteger y = ((DHPublicKey)publicKey).getY();1915if (id) {1916attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));1917}1918if (netscapeDb) {1919attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);1920}1921} else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {1922ECPublicKey ecPub = (ECPublicKey)publicKey;1923ECPoint point = ecPub.getW();1924ECParameterSpec params = ecPub.getParams();1925byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());1926if (id) {1927attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));1928}1929if (netscapeDb) {1930attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);1931}1932} else {1933throw new RuntimeException("Unknown key algorithm " + alg);1934}1935return attrs;1936}19371938/**1939* return true if cert destroyed1940*/1941private boolean destroyCert(byte[] cka_id)1942throws PKCS11Exception, KeyStoreException {1943Session session = null;1944try {1945session = token.getOpSession();1946THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1947if (h.type != ATTR_CLASS_CERT) {1948return false;1949}19501951token.p11.C_DestroyObject(session.id(), h.handle);1952if (debug != null) {1953debug.println("destroyCert destroyed cert with CKA_ID [" +1954getID(cka_id) +1955"]");1956}1957return true;1958} finally {1959token.releaseSession(session);1960}1961}19621963/**1964* return true if chain destroyed1965*/1966private boolean destroyChain(byte[] cka_id)1967throws PKCS11Exception, CertificateException, KeyStoreException {19681969Session session = null;1970try {1971session = token.getOpSession();19721973THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);1974if (h.type != ATTR_CLASS_CERT) {1975if (debug != null) {1976debug.println("destroyChain could not find " +1977"end entity cert with CKA_ID [0x" +1978Functions.toHexString(cka_id) +1979"]");1980}1981return false;1982}19831984X509Certificate endCert = loadCert(session, h.handle);1985token.p11.C_DestroyObject(session.id(), h.handle);1986if (debug != null) {1987debug.println("destroyChain destroyed end entity cert " +1988"with CKA_ID [" +1989getID(cka_id) +1990"]");1991}19921993// build chain following issuer->subject links19941995X509Certificate next = endCert;1996while (true) {19971998if (next.getSubjectX500Principal().equals1999(next.getIssuerX500Principal())) {2000// self signed - done2001break;2002}20032004CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {2005ATTR_TOKEN_TRUE,2006ATTR_CLASS_CERT,2007new CK_ATTRIBUTE(CKA_SUBJECT,2008next.getIssuerX500Principal().getEncoded()) };2009long[] ch = findObjects(session, attrs);20102011if (ch == null || ch.length == 0) {2012// done2013break;2014} else {2015// if more than one found, use first2016if (debug != null && ch.length > 1) {2017debug.println("destroyChain found " +2018ch.length +2019" certificate entries for subject [" +2020next.getIssuerX500Principal() +2021"] in token - using first entry");2022}20232024next = loadCert(session, ch[0]);20252026// only delete if not part of any other chain20272028attrs = new CK_ATTRIBUTE[] {2029ATTR_TOKEN_TRUE,2030ATTR_CLASS_CERT,2031new CK_ATTRIBUTE(CKA_ISSUER,2032next.getSubjectX500Principal().getEncoded()) };2033long[] issuers = findObjects(session, attrs);20342035boolean destroyIt = false;2036if (issuers == null || issuers.length == 0) {2037// no other certs with this issuer -2038// destroy it2039destroyIt = true;2040} else if (issuers.length == 1) {2041X509Certificate iCert = loadCert(session, issuers[0]);2042if (next.equals(iCert)) {2043// only cert with issuer is itself (self-signed) -2044// destroy it2045destroyIt = true;2046}2047}20482049if (destroyIt) {2050token.p11.C_DestroyObject(session.id(), ch[0]);2051if (debug != null) {2052debug.println2053("destroyChain destroyed cert in chain " +2054"with subject [" +2055next.getSubjectX500Principal() + "]");2056}2057} else {2058if (debug != null) {2059debug.println("destroyChain did not destroy " +2060"shared cert in chain with subject [" +2061next.getSubjectX500Principal() + "]");2062}2063}2064}2065}20662067return true;20682069} finally {2070token.releaseSession(session);2071}2072}20732074/**2075* return true if secret key destroyed2076*/2077private boolean destroySkey(String alias)2078throws PKCS11Exception, KeyStoreException {2079Session session = null;2080try {2081session = token.getOpSession();20822083THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);2084if (h.type != ATTR_CLASS_SKEY) {2085if (debug != null) {2086debug.println("destroySkey did not find secret key " +2087"with CKA_LABEL [" +2088alias +2089"]");2090}2091return false;2092}2093token.p11.C_DestroyObject(session.id(), h.handle);2094return true;2095} finally {2096token.releaseSession(session);2097}2098}20992100/**2101* return true if private key destroyed2102*/2103private boolean destroyPkey(byte[] cka_id)2104throws PKCS11Exception, KeyStoreException {2105Session session = null;2106try {2107session = token.getOpSession();21082109THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);2110if (h.type != ATTR_CLASS_PKEY) {2111if (debug != null) {2112debug.println2113("destroyPkey did not find private key with CKA_ID [" +2114getID(cka_id) +2115"]");2116}2117return false;2118}2119token.p11.C_DestroyObject(session.id(), h.handle);2120return true;2121} finally {2122token.releaseSession(session);2123}2124}21252126/**2127* build [alias + issuer + serialNumber] string from a cert2128*/2129private String getID(String alias, X509Certificate cert) {2130X500Principal issuer = cert.getIssuerX500Principal();2131BigInteger serialNum = cert.getSerialNumber();21322133return alias +2134ALIAS_SEP +2135issuer.getName(X500Principal.CANONICAL) +2136ALIAS_SEP +2137serialNum.toString();2138}21392140/**2141* build CKA_ID string from bytes2142*/2143private static String getID(byte[] bytes) {2144boolean printable = true;2145for (int i = 0; i < bytes.length; i++) {2146if (!DerValue.isPrintableStringChar((char)bytes[i])) {2147printable = false;2148break;2149}2150}21512152if (!printable) {2153return "0x" + Functions.toHexString(bytes);2154} else {2155try {2156return new String(bytes, "UTF-8");2157} catch (UnsupportedEncodingException uee) {2158return "0x" + Functions.toHexString(bytes);2159}2160}2161}21622163/**2164* find an object on the token2165*2166* @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY2167* @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY2168* @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY2169*/2170private THandle getTokenObject(Session session,2171CK_ATTRIBUTE type,2172byte[] cka_id,2173String cka_label)2174throws PKCS11Exception, KeyStoreException {21752176CK_ATTRIBUTE[] attrs;2177if (type == ATTR_CLASS_SKEY) {2178attrs = new CK_ATTRIBUTE[] {2179ATTR_SKEY_TOKEN_TRUE,2180new CK_ATTRIBUTE(CKA_LABEL, cka_label),2181type };2182} else {2183attrs = new CK_ATTRIBUTE[] {2184ATTR_TOKEN_TRUE,2185new CK_ATTRIBUTE(CKA_ID, cka_id),2186type };2187}2188long[] h = findObjects(session, attrs);2189if (h.length == 0) {2190if (debug != null) {2191if (type == ATTR_CLASS_SKEY) {2192debug.println("getTokenObject did not find secret key " +2193"with CKA_LABEL [" +2194cka_label +2195"]");2196} else if (type == ATTR_CLASS_CERT) {2197debug.println2198("getTokenObject did not find cert with CKA_ID [" +2199getID(cka_id) +2200"]");2201} else {2202debug.println("getTokenObject did not find private key " +2203"with CKA_ID [" +2204getID(cka_id) +2205"]");2206}2207}2208} else if (h.length == 1) {22092210// found object handle - return it2211return new THandle(h[0], type);22122213} else {22142215// found multiple object handles -2216// see if token ignored CKA_LABEL during search (e.g. NSS)22172218if (type == ATTR_CLASS_SKEY) {22192220ArrayList<THandle> list = new ArrayList<THandle>(h.length);2221for (int i = 0; i < h.length; i++) {22222223CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]2224{ new CK_ATTRIBUTE(CKA_LABEL) };2225token.p11.C_GetAttributeValue(session.id(), h[i], label);2226if (label[0].pValue != null &&2227cka_label.equals(new String(label[0].getCharArray()))) {2228list.add(new THandle(h[i], ATTR_CLASS_SKEY));2229}2230}2231if (list.size() == 1) {2232// yes, there was only one CKA_LABEL that matched2233return list.get(0);2234} else {2235throw new KeyStoreException("invalid KeyStore state: " +2236"found " +2237list.size() +2238" secret keys sharing CKA_LABEL [" +2239cka_label +2240"]");2241}2242} else if (type == ATTR_CLASS_CERT) {2243throw new KeyStoreException("invalid KeyStore state: " +2244"found " +2245h.length +2246" certificates sharing CKA_ID " +2247getID(cka_id));2248} else {2249throw new KeyStoreException("invalid KeyStore state: " +2250"found " +2251h.length +2252" private keys sharing CKA_ID " +2253getID(cka_id));2254}2255}2256return new THandle(NO_HANDLE, null);2257}22582259/**2260* Create a mapping of all key pairs, trusted certs, and secret keys2261* on the token into logical KeyStore entries unambiguously2262* accessible via an alias.2263*2264* If the token is removed, the map may contain stale values.2265* KeyStore.load should be called to re-create the map.2266*2267* Assume all private keys and matching certs share a unique CKA_ID.2268*2269* Assume all secret keys have a unique CKA_LABEL.2270*2271* @return true if multiple certs found sharing the same CKA_LABEL2272* (if so, write capabilities are disabled)2273*/2274private boolean mapLabels() throws2275PKCS11Exception, CertificateException, KeyStoreException {22762277CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {2278new CK_ATTRIBUTE(CKA_TRUSTED) };22792280Session session = null;2281try {2282session = token.getOpSession();22832284// get all private key CKA_IDs22852286ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();2287CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {2288ATTR_TOKEN_TRUE,2289ATTR_CLASS_PKEY,2290};2291long[] handles = findObjects(session, attrs);22922293for (long handle : handles) {2294attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };2295token.p11.C_GetAttributeValue(session.id(), handle, attrs);22962297if (attrs[0].pValue != null) {2298pkeyIDs.add(attrs[0].getByteArray());2299}2300}23012302// Get all certificates2303//2304// If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.2305//2306// Get the CKA_LABEL for each cert2307// (if the cert does not have a CKA_LABEL, use the CKA_ID).2308//2309// Map each cert to the its CKA_LABEL2310// (multiple certs may be mapped to a single CKA_LABEL)23112312HashMap<String, HashSet<AliasInfo>> certMap =2313new HashMap<String, HashSet<AliasInfo>>();23142315attrs = new CK_ATTRIBUTE[] {2316ATTR_TOKEN_TRUE,2317ATTR_CLASS_CERT,2318};2319handles = findObjects(session, attrs);23202321for (long handle : handles) {2322attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };23232324String cka_label = null;2325byte[] cka_id = null;2326try {2327token.p11.C_GetAttributeValue(session.id(), handle, attrs);2328if (attrs[0].pValue != null) {2329// there is a CKA_LABEL2330cka_label = new String(attrs[0].getCharArray());2331}2332} catch (PKCS11Exception pe) {2333if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {2334throw pe;2335}23362337// GetAttributeValue for CKA_LABEL not supported2338//2339// XXX SCA10002340}23412342// get CKA_ID23432344attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };2345token.p11.C_GetAttributeValue(session.id(), handle, attrs);2346if (attrs[0].pValue == null) {2347if (cka_label == null) {2348// no cka_label nor cka_id - ignore2349continue;2350}2351} else {2352if (cka_label == null) {2353// use CKA_ID as CKA_LABEL2354cka_label = getID(attrs[0].getByteArray());2355}2356cka_id = attrs[0].getByteArray();2357}23582359X509Certificate cert = loadCert(session, handle);23602361// get CKA_TRUSTED23622363boolean cka_trusted = false;23642365if (useSecmodTrust) {2366cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);2367} else {2368if (CKA_TRUSTED_SUPPORTED) {2369try {2370token.p11.C_GetAttributeValue2371(session.id(), handle, trustedAttr);2372cka_trusted = trustedAttr[0].getBoolean();2373} catch (PKCS11Exception pe) {2374if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {2375// XXX NSS, ibutton, sca10002376CKA_TRUSTED_SUPPORTED = false;2377if (debug != null) {2378debug.println2379("CKA_TRUSTED attribute not supported");2380}2381}2382}2383}2384}23852386HashSet<AliasInfo> infoSet = certMap.get(cka_label);2387if (infoSet == null) {2388infoSet = new HashSet<AliasInfo>(2);2389certMap.put(cka_label, infoSet);2390}23912392// initially create private key entry AliasInfo entries -2393// these entries will get resolved into their true2394// entry types later23952396infoSet.add(new AliasInfo2397(cka_label,2398cka_id,2399cka_trusted,2400cert));2401}24022403// create list secret key CKA_LABELS -2404// if there are duplicates (either between secret keys,2405// or between a secret key and another object),2406// throw an exception2407HashMap<String, AliasInfo> sKeyMap =2408new HashMap<String, AliasInfo>();24092410attrs = new CK_ATTRIBUTE[] {2411ATTR_SKEY_TOKEN_TRUE,2412ATTR_CLASS_SKEY,2413};2414handles = findObjects(session, attrs);24152416for (long handle : handles) {2417attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };2418token.p11.C_GetAttributeValue(session.id(), handle, attrs);2419if (attrs[0].pValue != null) {24202421// there is a CKA_LABEL2422String cka_label = new String(attrs[0].getCharArray());2423if (sKeyMap.get(cka_label) == null) {2424sKeyMap.put(cka_label, new AliasInfo(cka_label));2425} else {2426throw new KeyStoreException("invalid KeyStore state: " +2427"found multiple secret keys sharing same " +2428"CKA_LABEL [" +2429cka_label +2430"]");2431}2432}2433}24342435// update global aliasMap with alias mappings2436ArrayList<AliasInfo> matchedCerts =2437mapPrivateKeys(pkeyIDs, certMap);2438boolean sharedLabel = mapCerts(matchedCerts, certMap);2439mapSecretKeys(sKeyMap);24402441return sharedLabel;24422443} finally {2444token.releaseSession(session);2445}2446}24472448/**2449* for each private key CKA_ID, find corresponding cert with same CKA_ID.2450* if found cert, see if cert CKA_LABEL is unique.2451* if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.2452* if CKA_LABEL not unique, map private key/cert alias to:2453* CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL2454* if cert not found, ignore private key2455* (don't support private key entries without a cert chain yet)2456*2457* @return a list of AliasInfo entries that represents all matches2458*/2459private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,2460HashMap<String, HashSet<AliasInfo>> certMap)2461throws PKCS11Exception, CertificateException {24622463// reset global alias map2464aliasMap = new HashMap<String, AliasInfo>();24652466// list of matched certs that we will return2467ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();24682469for (byte[] pkeyID : pkeyIDs) {24702471// try to find a matching CKA_ID in a certificate24722473boolean foundMatch = false;2474Set<String> certLabels = certMap.keySet();2475for (String certLabel : certLabels) {24762477// get cert CKA_IDs (if present) for each cert24782479HashSet<AliasInfo> infoSet = certMap.get(certLabel);2480for (AliasInfo aliasInfo : infoSet) {2481if (Arrays.equals(pkeyID, aliasInfo.id)) {24822483// found private key with matching cert24842485if (infoSet.size() == 1) {2486// unique CKA_LABEL - use certLabel as alias2487aliasInfo.matched = true;2488aliasMap.put(certLabel, aliasInfo);2489} else {2490// create new alias2491aliasInfo.matched = true;2492aliasMap.put(getID(certLabel, aliasInfo.cert),2493aliasInfo);2494}2495matchedCerts.add(aliasInfo);2496foundMatch = true;2497break;2498}2499}2500if (foundMatch) {2501break;2502}2503}25042505if (!foundMatch) {2506if (debug != null) {2507debug.println2508("did not find match for private key with CKA_ID [" +2509getID(pkeyID) +2510"] (ignoring entry)");2511}2512}2513}25142515return matchedCerts;2516}25172518/**2519* for each cert not matched with a private key but is CKA_TRUSTED:2520* if CKA_LABEL unique, map cert to CKA_LABEL.2521* if CKA_LABEL not unique, map cert to [label+issuer+serialNum]2522*2523* if CKA_TRUSTED not supported, treat all certs not part of a chain2524* as trusted2525*2526* @return true if multiple certs found sharing the same CKA_LABEL2527*/2528private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,2529HashMap<String, HashSet<AliasInfo>> certMap)2530throws PKCS11Exception, CertificateException {25312532// load all cert chains2533for (AliasInfo aliasInfo : matchedCerts) {2534Session session = null;2535try {2536session = token.getOpSession();2537aliasInfo.chain = loadChain(session, aliasInfo.cert);2538} finally {2539token.releaseSession(session);2540}2541}25422543// find all certs in certMap not part of a cert chain2544// - these are trusted25452546boolean sharedLabel = false;25472548Set<String> certLabels = certMap.keySet();2549for (String certLabel : certLabels) {2550HashSet<AliasInfo> infoSet = certMap.get(certLabel);2551for (AliasInfo aliasInfo : infoSet) {25522553if (aliasInfo.matched == true) {2554// already found a private key match for this cert -2555// just continue2556aliasInfo.trusted = false;2557continue;2558}25592560// cert in this aliasInfo is not matched yet2561//2562// if CKA_TRUSTED_SUPPORTED == true,2563// then check if cert is trusted25642565if (CKA_TRUSTED_SUPPORTED) {2566if (aliasInfo.trusted) {2567// trusted certificate2568if (mapTrustedCert2569(certLabel, aliasInfo, infoSet) == true) {2570sharedLabel = true;2571}2572}2573continue;2574}25752576// CKA_TRUSTED_SUPPORTED == false2577//2578// XXX treat all certs not part of a chain as trusted2579// XXX2580// XXX Unsupported2581//2582// boolean partOfChain = false;2583// for (AliasInfo matchedInfo : matchedCerts) {2584// for (int i = 0; i < matchedInfo.chain.length; i++) {2585// if (matchedInfo.chain[i].equals(aliasInfo.cert)) {2586// partOfChain = true;2587// break;2588// }2589// }2590// if (partOfChain) {2591// break;2592// }2593// }2594//2595// if (!partOfChain) {2596// if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){2597// sharedLabel = true;2598// }2599// } else {2600// if (debug != null) {2601// debug.println("ignoring unmatched/untrusted cert " +2602// "that is part of cert chain - cert subject is [" +2603// aliasInfo.cert.getSubjectX500Principal().getName2604// (X500Principal.CANONICAL) +2605// "]");2606// }2607// }2608}2609}26102611return sharedLabel;2612}26132614private boolean mapTrustedCert(String certLabel,2615AliasInfo aliasInfo,2616HashSet<AliasInfo> infoSet) {26172618boolean sharedLabel = false;26192620aliasInfo.type = ATTR_CLASS_CERT;2621aliasInfo.trusted = true;2622if (infoSet.size() == 1) {2623// unique CKA_LABEL - use certLabel as alias2624aliasMap.put(certLabel, aliasInfo);2625} else {2626// create new alias2627sharedLabel = true;2628aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);2629}26302631return sharedLabel;2632}26332634/**2635* If the secret key shares a CKA_LABEL with another entry,2636* throw an exception2637*/2638private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)2639throws KeyStoreException {2640for (String label : sKeyMap.keySet()) {2641if (aliasMap.containsKey(label)) {2642throw new KeyStoreException("invalid KeyStore state: " +2643"found secret key sharing CKA_LABEL [" +2644label +2645"] with another token object");2646}2647}2648aliasMap.putAll(sKeyMap);2649}26502651private void dumpTokenMap() {2652Set<String> aliases = aliasMap.keySet();2653System.out.println("Token Alias Map:");2654if (aliases.isEmpty()) {2655System.out.println(" [empty]");2656} else {2657for (String s : aliases) {2658System.out.println(" " + s + aliasMap.get(s));2659}2660}2661}26622663private void checkWrite() throws KeyStoreException {2664if (writeDisabled) {2665throw new KeyStoreException2666("This PKCS11KeyStore does not support write capabilities");2667}2668}26692670private final static long[] LONG0 = new long[0];26712672private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)2673throws PKCS11Exception {2674Token token = session.token;2675long[] handles = LONG0;2676token.p11.C_FindObjectsInit(session.id(), attrs);2677while (true) {2678long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);2679if (h.length == 0) {2680break;2681}2682handles = P11Util.concat(handles, h);2683}2684token.p11.C_FindObjectsFinal(session.id());2685return handles;2686}26872688}268926902691