Java tutorial
/* * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.security; import java.io.IOException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.CertSelector; import java.security.cert.CertStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import mitm.common.security.certificate.X509CertificateInspector; import mitm.common.security.certstore.CertificateAlreadyExistsException; import mitm.common.security.certstore.Expired; import mitm.common.security.certstore.Match; import mitm.common.security.certstore.MissingKeyAlias; import mitm.common.security.certstore.X509CertStoreEntry; import mitm.common.security.certstore.X509CertStoreExt; import mitm.common.security.certstore.X509StoreEventListener; import mitm.common.util.Check; import mitm.common.util.CloseableIterator; import mitm.common.util.CloseableIteratorException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KeyAndCertStoreImpl implements KeyAndCertStore { private static Logger logger = LoggerFactory.getLogger(KeyAndCertStoreImpl.class); private final X509CertStoreExt certStore; private final KeyStore keyStore; private final String keyEntryPassword; public KeyAndCertStoreImpl(X509CertStoreExt certStore, KeyStore keyStore, String keyEntryPassword) { Check.notNull(certStore, "certStore"); this.certStore = certStore; this.keyStore = keyStore; this.keyEntryPassword = keyEntryPassword; } /** * @see X509CertStoreExt.getByEmail */ @Override public CloseableIterator<? extends X509CertStoreEntry> getByEmail(String email, Match match, Expired expired, MissingKeyAlias missingKeyAlias) throws CertStoreException { return certStore.getByEmail(email, match, expired, missingKeyAlias); } /** * @see X509CertStoreExt.getByEmail */ @Override public CloseableIterator<? extends X509CertStoreEntry> getByEmail(String email, Match match, Expired expired, MissingKeyAlias missingKeyAlias, Integer firstResult, Integer maxResults) throws CertStoreException { return certStore.getByEmail(email, match, expired, missingKeyAlias, firstResult, maxResults); } /** * @see X509CertStoreExt.getByEmailCount */ @Override public long getByEmailCount(String email, Match match, Expired expired, MissingKeyAlias missingKeyAlias) throws CertStoreException { return certStore.getByEmailCount(email, match, expired, missingKeyAlias); } /** * @see X509CertStoreExt.searchBySubject */ @Override public CloseableIterator<? extends X509CertStoreEntry> searchBySubject(String subject, Expired expired, MissingKeyAlias missingKeyAlias, Integer firstResult, Integer maxResults) throws CertStoreException { return certStore.searchBySubject(subject, expired, missingKeyAlias, firstResult, maxResults); } /** * @see X509CertStoreExt.getSearchBySubjectCount */ @Override public long getSearchBySubjectCount(String subject, Expired expired, MissingKeyAlias missingKeyAlias) throws CertStoreException { return certStore.getSearchBySubjectCount(subject, expired, missingKeyAlias); } /** * @see X509CertStoreExt.searchByIssuer */ @Override public CloseableIterator<? extends X509CertStoreEntry> searchByIssuer(String issuer, Expired expired, MissingKeyAlias missingKeyAlias, Integer firstResult, Integer maxResults) throws CertStoreException { return certStore.searchByIssuer(issuer, expired, missingKeyAlias, firstResult, maxResults); } /** * @see X509CertStoreExt.getSearchByIssuerCount */ @Override public long getSearchByIssuerCount(String issuer, Expired expired, MissingKeyAlias missingKeyAlias) throws CertStoreException { return certStore.getSearchByIssuerCount(issuer, expired, missingKeyAlias); } /** * @see X509CertStoreExt.getByThumbprint */ @Override public X509CertStoreEntry getByThumbprint(String thumbprint) throws CertStoreException { return certStore.getByThumbprint(thumbprint); } /** * @see X509CertStoreExt.getByCertificate */ @Override public X509CertStoreEntry getByCertificate(X509Certificate certificate) throws CertStoreException { return certStore.getByCertificate(certificate); } /** * @see X509CertStoreExt.contains */ @Override public boolean contains(X509Certificate certificate) throws CertStoreException { return certStore.contains(certificate); } /** * @see X509CertStoreExt.getLatest */ @Override public X509CertStoreEntry getLatest() { return certStore.getLatest(); } /** * @see X509CertStoreExt.size */ @Override public long size() { return certStore.size(); } /** * @see X509CertStoreExt.size */ @Override public long size(Expired expired, MissingKeyAlias missingKeyAlias) { return certStore.size(expired, missingKeyAlias); } /** * @see X509CertStoreExt.addCertificate */ @Override public X509CertStoreEntry addCertificate(X509Certificate certificate, String keyAlias) throws CertStoreException, CertificateAlreadyExistsException { return certStore.addCertificate(certificate, keyAlias); } /** * @see X509CertStoreExt.addCertificate */ @Override public X509CertStoreEntry addCertificate(X509Certificate certificate) throws CertStoreException, CertificateAlreadyExistsException { return certStore.addCertificate(certificate); } /** * Removes the certificate and if the certificate has an associated key entry an attempt is made to remove the * associated key (note that not all KeyStores allow keys to be removed) */ @Override public void removeCertificate(X509Certificate certificate) throws CertStoreException { if (logger.isDebugEnabled()) { logger.debug("Removing certificate: {}", certificate); } X509CertStoreEntry entry = getByCertificate(certificate); if (entry == null) { throw new CertStoreException("CertStore entry not found."); } if (keyStore != null && entry.getKeyAlias() != null) { if (logger.isDebugEnabled()) { logger.debug("Deleting associated key with alias: {}", entry.getKeyAlias()); } /* * try to remove the associated key from the keyStore */ try { keyStore.deleteEntry(entry.getKeyAlias()); } catch (KeyStoreException e) { logger.error("Unable to remove associated key for certificate: " + certificate, e); } } certStore.removeCertificate(certificate); } /** * Removes all certificate and key entries. */ @Override public void removeAllEntries() throws CertStoreException { /* remove all entries from the certificate store */ certStore.removeAllEntries(); /* remove all entries from the KeyStore */ if (keyStore != null) { try { Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); keyStore.deleteEntry(alias); } } catch (KeyStoreException e) { throw new CertStoreException(e); } } } /** * @see X509CertStoreExt.getCertStoreIterator */ @Override public CloseableIterator<? extends X509CertStoreEntry> getCertStoreIterator(CertSelector certSelector, MissingKeyAlias missingKeyAlias, Integer firstResult, Integer maxResults) throws CertStoreException { return certStore.getCertStoreIterator(certSelector, missingKeyAlias, firstResult, maxResults); } /** * @see X509CertStoreExt.getCertificates */ @Override public Collection<X509Certificate> getCertificates(CertSelector certSelector) throws CertStoreException { return certStore.getCertificates(certSelector); } /** * @see X509CertStoreExt.getCertificateIterator */ @Override public CloseableIterator<X509Certificate> getCertificateIterator(CertSelector certSelector) throws CertStoreException { return certStore.getCertificateIterator(certSelector); } /** * Sets the listener that listens for store events */ @Override public void setStoreEventListener(X509StoreEventListener eventListener) { certStore.setStoreEventListener(eventListener); } /** * @See KeyAndCertStore.addKeyAndCertificate */ @Override public boolean addKeyAndCertificate(KeyAndCertificate keyAndCertificate) throws CertStoreException, KeyStoreException { Check.notNull(keyAndCertificate, "keyAndCertificate"); X509Certificate certificate = keyAndCertificate.getCertificate(); PrivateKey privateKey = keyAndCertificate.getPrivateKey(); Check.notNull(certificate, "certificate"); if (logger.isDebugEnabled()) { logger.debug("Adding KeyAndCertificate: {}", certificate); } String keyAlias = null; if (keyStore != null && privateKey != null) { try { /* we set the key alias to the thumbprint of the associated certificate */ keyAlias = X509CertificateInspector.getThumbprint(certificate); } catch (CertificateEncodingException e) { throw new CertStoreException(e); } catch (NoSuchAlgorithmException e) { throw new CertStoreException(e); } catch (NoSuchProviderException e) { throw new CertStoreException(e); } if (logger.isDebugEnabled()) { logger.debug("Setting Key entry with alias: {}", keyAlias); } keyStore.setKeyEntry(keyAlias, privateKey, getPassword(), new Certificate[] { certificate }); } X509CertStoreEntry certStoreEntry = certStore.getByCertificate(keyAndCertificate.getCertificate()); boolean added = false; if (certStoreEntry == null) { certStore.addCertificate(certificate, keyAlias); added = true; } else { /* * Only set the key alias if the key alias is set and never set the key alias to null if the new * key alias is null. If we would set the key alias to null for an existing cert store entry, it * might happen that when we re-import a certificate but now without a key (for example from a * p7b), the key alias will be set to null which is not want we want. Why? suppose you first * import a pfx file with certificates and keys and then import a p7b file containing a large * number of certificates. Now if the p7b file contains a certificate that was also stored in * the pfx together with the private key, if we would have set the key alias to null when we * imported the p7b, we no longer have the associated private key. The private key is still * there but detached from it's certificate. */ if (keyAlias != null) { /* * Only set if the key alias is changing */ if (!keyAlias.equals(certStoreEntry.getKeyAlias())) { certStoreEntry.setKeyAlias(keyAlias); added = true; } } } return added; } /** * @See KeyAndCertStore.getKeyAndCertificate */ @Override public KeyAndCertificate getKeyAndCertificate(X509CertStoreEntry certStoreEntry) throws CertStoreException, KeyStoreException { KeyAndCertificate keyAndCertificate = null; if (certStoreEntry != null) { PrivateKey privateKey; try { privateKey = getPrivateKey(certStoreEntry); } catch (UnrecoverableKeyException e) { throw new KeyStoreException(e); } catch (NoSuchAlgorithmException e) { throw new KeyStoreException(e); } keyAndCertificate = new KeyAndCertificateImpl(privateKey, certStoreEntry.getCertificate()); } return keyAndCertificate; } /** * @See KeyAndCertStore.getMatchingKeys */ @Override public Collection<? extends PrivateKey> getMatchingKeys(KeyIdentifier keyIdentifier) throws KeyStoreException { return getMatchingKeys(keyIdentifier, null, null); } /** * @See KeyAndCertStore.getMatchingKeys */ @Override public Collection<? extends PrivateKey> getMatchingKeys(KeyIdentifier keyIdentifier, Integer firstResult, Integer maxResults) throws KeyStoreException { Set<PrivateKey> keys = new HashSet<PrivateKey>(); if (keyIdentifier == null) { logger.debug("null keyIdentifier."); return keys; } CertSelector certSelector = null; try { if (keyIdentifier instanceof CertSelectorKeyIdentifier) { CertSelectorKeyIdentifier certSelectorKeyIdentifier = (CertSelectorKeyIdentifier) keyIdentifier; certSelector = certSelectorKeyIdentifier.getSelector(); } else { logger.warn("Unsupported KeyIdentifier '" + keyIdentifier.getClass().getCanonicalName() + "'"); } /* only get entries with for which the key alias is set */ CloseableIterator<? extends X509CertStoreEntry> certStoreIterator = certStore .getCertStoreIterator(certSelector, MissingKeyAlias.NOT_ALLOWED, firstResult, maxResults); try { while (certStoreIterator.hasNext()) { X509CertStoreEntry certStoreEntry = certStoreIterator.next(); PrivateKey privateKey = getPrivateKey(certStoreEntry); if (privateKey != null) { keys.add(privateKey); } } } finally { certStoreIterator.close(); } } catch (IOException e) { throw new KeyStoreException(e); } catch (CertStoreException e) { throw new KeyStoreException(e); } catch (CloseableIteratorException e) { throw new KeyStoreException(e); } catch (NoSuchAlgorithmException e) { throw new KeyStoreException(e); } catch (UnrecoverableKeyException e) { throw new KeyStoreException(e); } return keys; } /** * @See KeyAndCertStore.sync */ @Override public void sync(SyncMode syncMode) throws KeyStoreException, CertStoreException { if (keyStore == null) { logger.warn("keyStore is null."); return; } if (syncMode == SyncMode.CERT_STORE || syncMode == SyncMode.ALL) { logger.info("Syncing Key store --> Certificate store"); Enumeration<String> aliases = keyStore.aliases(); /* * sync keyStore by getting the certificates and keys from the keyStore and placing it in the certStore. */ while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); Certificate certificate = keyStore.getCertificate(alias); if (!(certificate instanceof X509Certificate)) { /* * only X509Certificates are supported. If the key entry does not have an * associated certificate we will not import the key because the key * would not be used. */ continue; } X509Certificate x509Certificate = (X509Certificate) certificate; try { String keyAlias = null; if (keyStore.isKeyEntry(alias)) { keyAlias = alias; } X509CertStoreEntry certEntry = getByCertificate(x509Certificate); if (certEntry != null) { /* * Certificate already exists. Set key alias if different */ if (!StringUtils.equals(certEntry.getKeyAlias(), keyAlias)) { logger.info("Certificate with thumbprint {} already exist. Setting new key alias: {}", X509CertificateInspector.getThumbprint(certificate), keyAlias); certEntry.setKeyAlias(keyAlias); } } else { logger.info("Adding certificate with thumbprint {} and key alias: {}", X509CertificateInspector.getThumbprint(certificate), keyAlias); addCertificate(x509Certificate, keyAlias); } } catch (Exception e) { logger.error("Error syncing the item with alias: " + alias, e); } } } if (syncMode == SyncMode.KEY_STORE || syncMode == SyncMode.ALL) { logger.info("Syncing Certificate store --> Key store"); /* * synchronize certStore entries to make sure that all key aliases references valid keys. If a key alias * references a key that is no longer available the key entry will be set to null. * * Warning: if a removable keyStore (like smartcard) is used and the keyStore is temporarily unavailable * synchronizing will result in the deassociation of the key with the certificate. */ CloseableIterator<? extends X509CertStoreEntry> certStoreIterator = certStore.getCertStoreIterator(null, MissingKeyAlias.NOT_ALLOWED, null, null); try { try { while (certStoreIterator.hasNext()) { X509CertStoreEntry certStoreEntry = certStoreIterator.next(); String keyAlias = certStoreEntry.getKeyAlias(); if (keyAlias != null) { if (!keyStore.isKeyEntry(keyAlias)) { logger.warn("Key with alias {} cannot be found. Removing key alias.", keyAlias); certStoreEntry.setKeyAlias(null); } } } } finally { certStoreIterator.close(); } } catch (CloseableIteratorException e) { throw new CertStoreException(e); } } logger.info("Syncing finished"); } private PrivateKey getPrivateKey(X509CertStoreEntry certStoreEntry) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException { PrivateKey privateKey = null; if (keyStore == null) { logger.warn("keyStore is null."); return null; } if (certStoreEntry != null) { String keyAlias = certStoreEntry.getKeyAlias(); if (keyAlias != null) { Key key = keyStore.getKey(keyAlias, getPassword()); if (key instanceof PrivateKey) { privateKey = (PrivateKey) key; } else { if (key != null) { logger.warn("Key with alias '" + keyAlias + "' is not a private key. Skipping this key."); } else { logger.warn("Key with alias '" + keyAlias + "' does not exist. Skipping this key."); } } } else { logger.debug("keyAlias is null."); } } return privateKey; } private char[] getPassword() { return (keyEntryPassword != null ? keyEntryPassword.toCharArray() : null); } }