Java tutorial
/************************************************************************* * * * SignServer: The OpenSource Automated Signing Server * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.signserver.server.cryptotokens; import org.cesecore.keys.token.*; import org.signserver.common.CryptoTokenOfflineException; import org.signserver.common.UnsupportedCryptoTokenParameter; import org.signserver.common.NoSuchAliasException; import java.io.*; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.*; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.naming.NamingException; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.operator.OperatorCreationException; import org.cesecore.util.query.QueryCriteria; import org.ejbca.util.keystore.KeyTools; import org.signserver.common.*; import org.signserver.ejb.interfaces.IWorkerSession; import org.signserver.server.IServices; import org.signserver.server.ServicesImpl; import org.signserver.server.log.AdminInfo; /** * Class that uses a PKCS12 or JKS file on the file system for signing. * <p> * If no DEFAULTKEY property is specified the first key found will be used. * <p> * Loads on activation and releases the keys from memory when deactivating * <p> * Available properties are: * KEYSTOREPATH : The full path to the key store to load. (required) * KEYSTOREPASSWORD : The password that locks the key store. * KEYSTORETYPE : PKCS12 or JKS. (required) * DEFAULTKEY : Alias of keystore entry to use. (optional) * NEXTCERTSIGNKEY : Can be used to hold the alias of the next key. (optional) * * @author Philip Vendil, Markus Kilas * @version $Id: KeystoreCryptoToken.java 6147 2015-07-17 11:14:51Z netmackan $ */ public class KeystoreCryptoToken extends BaseCryptoToken { /** * Logger for this class. */ private static final Logger LOG = Logger.getLogger(KeystoreCryptoToken.class); public static final String KEYSTOREPATH = "KEYSTOREPATH"; public static final String KEYSTOREPASSWORD = "KEYSTOREPASSWORD"; public static final String KEYSTORETYPE = "KEYSTORETYPE"; public static final String DEFAULTKEY = "DEFAULTKEY"; public static final String NEXTKEY = "NEXTCERTSIGNKEY"; public static final String TYPE_PKCS12 = "PKCS12"; public static final String TYPE_JKS = "JKS"; public static final String TYPE_INTERNAL = "INTERNAL"; private String keystorepath = null; private String keystorepassword = null; private KeyStore ks; private String keystoretype; private Properties properties; /** * Mapping from alias or key purpose to KeyEntry. */ private Map<Object, KeyEntry> entries; private char[] authenticationCode; private IWorkerSession.ILocal workerSession; private int workerId; private Integer keygenerationLimit; @Override public void init(int workerId, Properties properties) throws CryptoTokenInitializationFailureException { this.properties = properties; this.workerId = workerId; keystorepath = properties.getProperty(KEYSTOREPATH); keystorepassword = properties.getProperty(KEYSTOREPASSWORD); keystoretype = properties.getProperty(KEYSTORETYPE); // check keystore type if (keystoretype == null) { throw new CryptoTokenInitializationFailureException("Missing KEYSTORETYPE property"); } if (!TYPE_PKCS12.equals(keystoretype) && !TYPE_JKS.equals(keystoretype) && !TYPE_INTERNAL.equals(keystoretype)) { throw new CryptoTokenInitializationFailureException( "KEYSTORETYPE should be either PKCS12, JKS, or INTERNAL"); } // check keystore file if (TYPE_PKCS12.equals(keystoretype) || TYPE_JKS.equals(keystoretype)) { if (keystorepath == null) { throw new CryptoTokenInitializationFailureException("Missing KEYSTOREPATH property"); } else { final File keystoreFile = new File(keystorepath); if (!keystoreFile.isFile()) { throw new CryptoTokenInitializationFailureException("File not found: " + keystorepath); } } } // Read property KEYGENERATIONLIMIT final String keygenLimitValue = properties.getProperty(CryptoTokenHelper.PROPERTY_KEYGENERATIONLIMIT); if (keygenLimitValue != null && !keygenLimitValue.trim().isEmpty()) { try { keygenerationLimit = Integer.parseInt(keygenLimitValue.trim()); } catch (NumberFormatException ex) { throw new CryptoTokenInitializationFailureException("Incorrect value for " + CryptoTokenHelper.PROPERTY_KEYGENERATIONLIMIT + ": " + ex.getLocalizedMessage()); } } } @Override public int getCryptoTokenStatus() { if (entries != null && entries.get(PURPOSE_SIGN) != null && (!properties.containsKey(NEXTKEY) || entries.get(PURPOSE_NEXTKEY) != null)) { return WorkerStatus.STATUS_ACTIVE; } return WorkerStatus.STATUS_OFFLINE; } @Override public byte[] decryptByteData(String alias, String authcode, byte[] encryptedData, IServices services) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, CryptoTokenOfflineException, UnrecoverableKeyException, KeyStoreException, InvalidKeyException { throw new UnsupportedOperationException("Not supported medthod"); } @Override public byte[] encryptMessage(String alias, String authcode, byte[] message, IServices services) throws NoSuchAlgorithmException, NoSuchPaddingException, org.cesecore.keys.token.CryptoTokenOfflineException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException { throw new UnsupportedOperationException("Not supported medthod"); } @Override public int getCryptoTokenStatus(final IServices services) { return getCryptoTokenStatus(); } /** * (Re)read from keystore to in-memory representation. */ private void readFromKeystore(final String authenticationcode) throws KeyStoreException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException { if (authenticationcode != null) { this.authenticationCode = authenticationcode.toCharArray(); } this.ks = getKeystore(keystoretype, keystorepath, authenticationCode); entries = new HashMap<Object, KeyEntry>(); Enumeration<String> e = ks.aliases(); while (e.hasMoreElements()) { final String alias = e.nextElement(); if (ks.isKeyEntry(alias)) { if (LOG.isDebugEnabled()) { LOG.debug("Alias " + alias + " is KeyEntry."); } final Key key = ks.getKey(alias, authenticationCode); if (key instanceof PrivateKey) { final Certificate[] chain = KeyTools.getCertChain(ks, alias); if (LOG.isDebugEnabled()) { LOG.debug("Loaded certificate chain with length " + chain.length + " from keystore."); } final KeyEntry entry = new KeyEntry((PrivateKey) key, chain[0], Arrays.asList(chain)); entries.put(alias, entry); } else { LOG.error("Not a private key for alias " + alias); } } } // Use the first entry as default key if none specified if (properties.getProperty(DEFAULTKEY) == null) { e = ks.aliases(); while (e.hasMoreElements()) { final String alias = e.nextElement(); if (ks.isKeyEntry(alias)) { if (ks.getKey(alias, authenticationCode) != null) { LOG.debug("Aliases " + alias + " is KeyEntry."); properties.setProperty(DEFAULTKEY, alias); break; } } } } final String defaultKey = properties.getProperty(DEFAULTKEY); if (defaultKey != null) { final KeyEntry entry = entries.get(defaultKey); if (entry != null) { entries.put(ICryptoToken.PURPOSE_SIGN, entry); entries.put(ICryptoToken.PURPOSE_DECRYPT, entry); } else { LOG.error("Not a private key for alias " + defaultKey); } } final String nextKey = properties.getProperty(NEXTKEY); if (nextKey != null) { final KeyEntry entry = entries.get(nextKey); if (entry != null) { entries.put(ICryptoToken.PURPOSE_NEXTKEY, entry); } else { LOG.error("Not a private key for alias " + defaultKey); } } } @Override public void activate(String authenticationcode) throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException { if (LOG.isDebugEnabled()) { LOG.debug("Keystore type is " + keystoretype + " and path is " + keystorepath); } try { readFromKeystore(authenticationcode); } catch (KeyStoreException e1) { LOG.error("Error :", e1); throw new CryptoTokenAuthenticationFailureException("KeyStoreException " + e1.getMessage()); } catch (FileNotFoundException e) { LOG.error("Error :", e); throw new CryptoTokenAuthenticationFailureException("Keystore file not found : " + e.getMessage()); } catch (NoSuchProviderException e1) { LOG.error("Error :", e1); throw new CryptoTokenAuthenticationFailureException("NoSuchProviderException " + e1.getMessage()); } catch (NoSuchAlgorithmException e) { LOG.error("Error :", e); throw new CryptoTokenAuthenticationFailureException("NoSuchAlgorithmException " + e.getMessage()); } catch (CertificateException e) { LOG.error("Error :", e); throw new CryptoTokenAuthenticationFailureException("CertificateException " + e.getMessage()); } catch (IOException e) { LOG.error("Error :", e); throw new CryptoTokenAuthenticationFailureException("IOException " + e.getMessage()); } catch (UnrecoverableKeyException e) { LOG.error("Error :", e); throw new CryptoTokenAuthenticationFailureException("UnrecoverableKeyException " + e.getMessage()); } } @Override public boolean deactivate() { entries = null; ks = null; if (authenticationCode != null) { Arrays.fill(authenticationCode, '\0'); } this.authenticationCode = null; return true; } @Override public PrivateKey getPrivateKey(int purpose) throws CryptoTokenOfflineException { if (entries == null) { if (keystorepassword != null) { try { activate(keystorepassword); } catch (CryptoTokenAuthenticationFailureException e) { throw new CryptoTokenOfflineException( "Error trying to autoactivating the keystore, wrong password set? " + e.getMessage()); } } else { throw new CryptoTokenOfflineException("Signtoken isn't active."); } } KeyEntry entry = entries.get(purpose); // If key for 'purpose' not available and no nextKey defined, try with // default if ((entry == null || entry.getPrivateKey() == null) && !properties.containsKey(NEXTKEY)) { entry = entries.get(PURPOSE_SIGN); } if (entry == null || entry.getPrivateKey() == null) { throw new CryptoTokenOfflineException("No key available for purpose: " + purpose); } return entry.getPrivateKey(); } @Override public PublicKey getPublicKey(int purpose) throws CryptoTokenOfflineException { final Certificate cert = getKeyEntry(purpose).getCertificate(); return cert.getPublicKey(); } @Override public String getProvider(int providerUsage) { return "BC"; } private KeyEntry getKeyEntry(final Object purposeOrAlias) throws CryptoTokenOfflineException { if (entries == null) { if (keystorepassword != null) { try { activate(keystorepassword); } catch (CryptoTokenAuthenticationFailureException e) { throw new CryptoTokenOfflineException( "Error trying to autoactivating the keystore, wrong password set? " + e.getMessage()); } } else { throw new CryptoTokenOfflineException("Signtoken isn't active."); } } KeyEntry entry = entries.get(purposeOrAlias); if (entry == null || entry.getCertificate() == null) { throw new CryptoTokenOfflineException("No key available for purpose: " + purposeOrAlias); } return entry; } @Override public Certificate getCertificate(int purpose) throws CryptoTokenOfflineException { return getCertificateFromEntries(purpose); } @Override public List<Certificate> getCertificateChain(int purpose) throws CryptoTokenOfflineException { return getCertificateChainFromEntries(purpose); } private Certificate getCertificateFromEntries(Object purposeOrAlias) throws CryptoTokenOfflineException { try { final KeyEntry entry = getKeyEntry(purposeOrAlias); Certificate result = entry.getCertificate(); // Do not return the dummy certificate if (CryptoTokenHelper.isDummyCertificate(result)) { result = null; } return result; } catch (CryptoTokenOfflineException ex) { return null; } } private List<Certificate> getCertificateChainFromEntries(Object purposeOrAlias) throws CryptoTokenOfflineException { final KeyEntry entry = getKeyEntry(purposeOrAlias); List<Certificate> result = entry.getCertificateChain(); // Do not return the dummy certificate if (result.size() == 1) { if (CryptoTokenHelper.isDummyCertificate(result.get(0))) { result = null; } } return result; } @Override public Certificate getCertificate(String alias) throws CryptoTokenOfflineException { return getCertificateFromEntries(alias); } @Override public List<Certificate> getCertificateChain(String alias) throws CryptoTokenOfflineException { return getCertificateChainFromEntries(alias); } @Override public ICertReqData genCertificateRequest(ISignerCertReqInfo info, final boolean explicitEccParameters, final boolean defaultKey) throws CryptoTokenOfflineException { final int purpose = defaultKey ? PURPOSE_SIGN : PURPOSE_NEXTKEY; if (LOG.isDebugEnabled()) { LOG.debug("Purpose: " + purpose); } try { return CryptoTokenHelper.genCertificateRequest(info, getPrivateKey(purpose), getProvider(ICryptoToken.PROVIDERUSAGE_SIGN), getPublicKey(purpose), explicitEccParameters); } catch (IllegalArgumentException ex) { throw new CryptoTokenOfflineException(ex.getMessage(), ex); } } /** * Method not supported */ @Override public boolean destroyKey(int purpose) { return false; } @Override public Collection<KeyTestResult> testKey(final String alias, final char[] authCode) throws CryptoTokenOfflineException, KeyStoreException { return CryptoTokenHelper.testKey(getKeyStore(), alias, authCode, "BC"); } @Override public Collection<KeyTestResult> testKey(final String alias, final char[] authCode, final IServices services) throws CryptoTokenOfflineException, KeyStoreException { return testKey(alias, authCode); } @Override public TokenSearchResults searchTokenEntries(final int startIndex, final int max, QueryCriteria qc, boolean includeData, Map<String, Object> params, IServices services) throws CryptoTokenOfflineException, QueryException { try { KeyStore keyStore = getKeyStore(); return CryptoTokenHelper.searchTokenEntries(keyStore, startIndex, max, qc, includeData); } catch (KeyStoreException ex) { throw new CryptoTokenOfflineException(ex); } } @Override public byte[] decryptByteData(String alias, String pin, byte[] encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, CryptoTokenOfflineException, UnrecoverableKeyException, KeyStoreException, InvalidKeyException { throw new UnsupportedOperationException("Not supported medthod"); } @Override public byte[] encryptMessage(String alias, String authcode, byte[] message) throws NoSuchPaddingException, NoSuchAlgorithmException, org.cesecore.keys.token.CryptoTokenOfflineException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException { throw new UnsupportedOperationException("Not supported medthod"); } @Override public void generateKey(String keyAlgorithm, String keySpec, String alias, char[] authCode) throws CryptoTokenOfflineException, IllegalArgumentException { throw new UnsupportedOperationException("Old method not supported, use V3 or later"); } @Override public Certificate generateSignerKeyAndGetCertificate(String keyAlgorithm, String keySpec, String alias, char[] authCode) throws CryptoTokenOfflineException, IllegalArgumentException { throw new UnsupportedOperationException("Old method not supported, use V3 or later"); } @Override public void generateKey(String keyAlgorithm, String keySpec, String alias, char[] authCode, Map<String, Object> params, IServices services) throws CryptoTokenOfflineException, IllegalArgumentException { if (keySpec == null) { throw new IllegalArgumentException("Missing keyspec parameter"); } if (alias == null) { throw new IllegalArgumentException("Missing alias parameter"); } if (LOG.isDebugEnabled()) { LOG.debug("keyAlgorithm: " + keyAlgorithm + ", keySpec: " + keySpec + ", alias: " + alias); } try { final KeyStore keystore = getKeyStore(); // Check key generation limit, if configured if (keygenerationLimit != null && keygenerationLimit > -1) { final int current; try { current = keystore.size(); if (current >= keygenerationLimit) { throw new TokenOutOfSpaceException("Key generation limit exceeded: " + current); } } catch (KeyStoreException ex) { LOG.error("Checking key generation limit failed", ex); throw new TokenOutOfSpaceException( "Current number of key entries could not be obtained: " + ex.getMessage(), ex); } } final KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm, "BC"); if ("ECDSA".equals(keyAlgorithm)) { kpg.initialize(ECNamedCurveTable.getParameterSpec(keySpec)); } else { kpg.initialize(Integer.valueOf(keySpec)); } final String sigAlgName = "SHA1With" + keyAlgorithm; LOG.debug("generating..."); final KeyPair keyPair = kpg.generateKeyPair(); Certificate[] chain = new Certificate[1]; chain[0] = CryptoTokenHelper.createDummyCertificate(alias, sigAlgName, keyPair, getProvider(PROVIDERUSAGE_SIGN)); LOG.debug("Creating certificate with entry " + alias + '.'); keystore.setKeyEntry(alias, keyPair.getPrivate(), authCode, chain); final OutputStream os; if (TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { os = new ByteArrayOutputStream(); } else { os = new FileOutputStream(new File(keystorepath)); } keystore.store(os, authenticationCode); if (TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { final ByteArrayOutputStream baos = (ByteArrayOutputStream) os; final IWorkerSession.ILocal workerSessionLocal = services.get(IWorkerSession.ILocal.class); if (workerSessionLocal == null) { throw new IllegalStateException("No WorkerSession available"); } workerSessionLocal.setKeystoreData(new AdminInfo("Internal", null, null), workerId, baos.toByteArray()); } final KeyEntry entry = new KeyEntry((PrivateKey) keyPair.getPrivate(), chain[0], Arrays.asList(chain)); // If this is the first entry entries.put(alias, entry); if (properties.getProperty(DEFAULTKEY) == null) { properties.setProperty(DEFAULTKEY, alias); entries.put(ICryptoToken.PURPOSE_SIGN, entry); entries.put(ICryptoToken.PURPOSE_DECRYPT, entry); } } catch (UnsupportedOperationException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (KeyStoreException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (NoSuchAlgorithmException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (NoSuchProviderException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (InvalidAlgorithmParameterException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (NumberFormatException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (OperatorCreationException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (CertificateException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (IOException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } catch (IllegalStateException ex) { LOG.error(ex, ex); throw new CryptoTokenOfflineException(ex); } } @Override public Certificate generateSignerKeyAndGetCertificate(String keyAlgorithm, String keySpec, String alias, char[] authCode, Map<String, Object> params, IServices services) throws TokenOutOfSpaceException, CryptoTokenOfflineException, DuplicateAliasException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, UnsupportedCryptoTokenParameter { generateKey(keyAlgorithm, keySpec, alias, authCode, params, services); return getCertificate(alias); } @Override public KeyStore getKeyStore() throws UnsupportedOperationException, CryptoTokenOfflineException, KeyStoreException { if (ks == null) { throw new CryptoTokenOfflineException("Not activated"); } return ks; } private KeyStore getKeystore(final String type, final String path, final char[] authCode) throws KeyStoreException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException, FileNotFoundException, IOException { final KeyStore result; if (TYPE_PKCS12.equalsIgnoreCase(type) || TYPE_INTERNAL.equalsIgnoreCase(type)) { result = KeyStore.getInstance("PKCS12", "BC"); } else { result = KeyStore.getInstance("JKS"); } InputStream in = null; try { if (!TYPE_INTERNAL.equalsIgnoreCase(type)) { if (path == null) { throw new FileNotFoundException("Missing property " + KeystoreCryptoToken.KEYSTOREPATH + "."); } in = new FileInputStream(path); } else { // load data from internal worker data... final byte[] keystoreData = getWorkerSession() .getKeystoreData(new AdminInfo("Internal", null, null), this.workerId); if (keystoreData != null) { in = new ByteArrayInputStream(keystoreData); } } result.load(in, authCode); } catch (NamingException e) { throw new KeyStoreException("Failed to get worker session: " + e.getMessage()); } finally { if (in != null) { try { in.close(); } catch (IOException ex) { LOG.error("Error closing file", ex); } } } return result; } @Override public boolean removeKey(String alias) throws CryptoTokenOfflineException, KeyStoreException, SignServerException { final KeyStore keyStore = getKeyStore(); boolean result = CryptoTokenHelper.removeKey(keyStore, alias); if (result) { OutputStream out = null; try { if (!TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { out = new FileOutputStream(new File(keystorepath)); } else { // use internal worker data out = new ByteArrayOutputStream(); } keyStore.store(out, authenticationCode); if (TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { final byte[] data = ((ByteArrayOutputStream) out).toByteArray(); getWorkerSession().setKeystoreData(new AdminInfo("Internal", null, null), this.workerId, data); } readFromKeystore(null); } catch (NamingException ex) { LOG.error("Unable to lookup worker session: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } catch (IOException ex) { LOG.error("Unable to persist new keystore after key removal: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } catch (NoSuchAlgorithmException ex) { LOG.error("Unable to persist new keystore after key removal: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } catch (CertificateException ex) { LOG.error("Unable to persist new keystore after key removal: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } catch (NoSuchProviderException ex) { LOG.error("Unable to persist new keystore after key removal: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } catch (UnrecoverableKeyException ex) { LOG.error("Unable to persist new keystore after key removal: " + ex.getMessage(), ex); throw new SignServerException("Unable to persist key removal"); } finally { IOUtils.closeQuietly(out); } } return result; } @Override public PrivateKey getPrivateKey(String alias) throws CryptoTokenOfflineException { return getKeyEntry(alias).getPrivateKey(); } @Override public PublicKey getPublicKey(String alias) throws CryptoTokenOfflineException { return getKeyEntry(alias).getCertificate().getPublicKey(); } @Override public ICertReqData genCertificateRequest(ISignerCertReqInfo info, boolean explicitEccParameters, String keyAlias) throws CryptoTokenOfflineException { return genCertificateRequest(info, explicitEccParameters, keyAlias, new ServicesImpl()); } @Override public ICertReqData genCertificateRequest(ISignerCertReqInfo info, boolean explicitEccParameters, String keyAlias, IServices services) throws CryptoTokenOfflineException { if (LOG.isDebugEnabled()) { LOG.debug("Alias: " + keyAlias); } try { return CryptoTokenHelper.genCertificateRequest(info, getPrivateKey(keyAlias), getProvider(ICryptoToken.PROVIDERUSAGE_SIGN), getPublicKey(keyAlias), explicitEccParameters); } catch (IllegalArgumentException ex) { if (LOG.isDebugEnabled()) { LOG.error("Certificate request error", ex); } throw new CryptoTokenOfflineException(ex.getMessage(), ex); } } @Override public void importCertificateChain(final List<Certificate> certChain, final String alias, final char[] authCode, final Map<String, Object> params, final IServices services) throws CryptoTokenOfflineException, IllegalArgumentException { if (certChain.size() < 1) { throw new IllegalArgumentException("Certificate chain can not be empty"); } try { final KeyStore keyStore = getKeyStore(); final Key key = keyStore.getKey(alias, authCode != null ? authCode : authenticationCode); CryptoTokenHelper.ensureNewPublicKeyMatchesOld(keyStore, alias, certChain.get(0)); keyStore.setKeyEntry(alias, key, authCode != null ? authCode : authenticationCode, certChain.toArray(new Certificate[0])); // persist keystore OutputStream out = null; if (!TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { out = new FileOutputStream(new File(keystorepath)); } else { // use internal worker data out = new ByteArrayOutputStream(); } keyStore.store(out, authenticationCode); if (TYPE_INTERNAL.equalsIgnoreCase(keystoretype)) { final byte[] data = ((ByteArrayOutputStream) out).toByteArray(); getWorkerSession().setKeystoreData(new AdminInfo("Internal", null, null), this.workerId, data); } // update in-memory representation KeyEntry entry = getKeyEntry(alias); final Certificate signingCert = certChain.get(0); if (entry == null) { entry = new KeyEntry(); } entry.setCertificate(signingCert); entry.setCertificateChain(certChain); } catch (Exception e) { throw new CryptoTokenOfflineException(e); } } protected IWorkerSession.ILocal getWorkerSession() throws NamingException { if (workerSession == null) { workerSession = ServiceLocator.getInstance().lookupLocal(IWorkerSession.ILocal.class); } return workerSession; } @Override public ICryptoInstance acquireCryptoInstance(String alias, Map<String, Object> params, RequestContext context) throws CryptoTokenOfflineException, NoSuchAliasException, InvalidAlgorithmParameterException, UnsupportedCryptoTokenParameter, IllegalRequestException { final KeyEntry entry = getKeyEntry(alias); return new DefaultCryptoInstance(alias, context, ks.getProvider(), entry.getPrivateKey(), entry.getCertificateChain()); } @Override public void releaseCryptoInstance(ICryptoInstance instance, RequestContext context) { // NOP } private static class KeyEntry { private PrivateKey privateKey; private Certificate certificate; private List<Certificate> certificateChain; public KeyEntry() { } public KeyEntry(final PrivateKey privateKey, final Certificate certificate, final List<Certificate> certificateChain) { this.privateKey = privateKey; this.certificate = certificate; this.certificateChain = certificateChain; } public Certificate getCertificate() { return certificate; } public List<Certificate> getCertificateChain() { return certificateChain; } public PrivateKey getPrivateKey() { return privateKey; } public void setCertificate(final Certificate cert) { certificate = cert; } public void setCertificateChain(final List<Certificate> certChain) { certificateChain = certChain; } public void setPrivateKey(final PrivateKey privKey) { privateKey = privKey; } } }