Java tutorial
/* * Copyright (c) 2008-2011, 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.application.djigzo.workflow.impl; import java.io.IOException; import java.io.OutputStream; 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.CertPath; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathBuilderResult; import java.security.cert.CertStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.PKIXCertPathBuilderResult; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import mitm.application.djigzo.workflow.KeyAndCertificateWorkflow; import mitm.common.hibernate.DatabaseAction; import mitm.common.hibernate.DatabaseException; import mitm.common.hibernate.DatabaseVoidAction; import mitm.common.hibernate.SessionManager; import mitm.common.security.KeyAndCertStore; import mitm.common.security.KeyAndCertificate; import mitm.common.security.KeyAndCertificateImpl; import mitm.common.security.NoSuchProviderRuntimeException; import mitm.common.security.SecurityFactoryFactory; import mitm.common.security.SecurityFactoryFactoryException; import mitm.common.security.certificate.X509CertificateInspector; import mitm.common.security.certpath.CertificatePathBuilder; import mitm.common.security.certpath.CertificatePathBuilderFactory; import mitm.common.security.certstore.CertificateAlreadyExistsException; import mitm.common.security.certstore.X509CertStoreEntry; import mitm.common.util.Check; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.hibernate.Session; import org.hibernate.exception.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Some methods of this KeyAndCertificateWorkflow implementation handle their own transactions. * * @author Martijn Brinkers * */ public class KeyAndCertificateWorkflowImpl extends CertificateWorkflowImpl implements KeyAndCertificateWorkflow { private final static Logger logger = LoggerFactory.getLogger(KeyAndCertificateWorkflowImpl.class); private final KeyAndCertStore keyAndCertStore; private final CertificatePathBuilderFactory pathBuilderFactory; public KeyAndCertificateWorkflowImpl(KeyAndCertStore keyAndCertStore, CertificatePathBuilderFactory pathBuilderFactory, SessionManager sessionManager) { super(keyAndCertStore, sessionManager); Check.notNull(keyAndCertStore, "keyAndCertStore"); Check.notNull(pathBuilderFactory, "pathBuilderFactory"); this.keyAndCertStore = keyAndCertStore; this.pathBuilderFactory = pathBuilderFactory; } @Override public int importKeyStore(final KeyStore keyStore, final MissingKey missingKey) throws KeyStoreException { Check.notNull(keyStore, "keyStore"); Check.notNull(missingKey, "missingKey"); int importedEntries = 0; try { importedEntries = getActionExecutor().executeTransaction(new DatabaseAction<Integer>() { @Override public Integer doAction(Session session) throws DatabaseException { try { Session previousSession = getSessionManager().getSession(); getSessionManager().setSession(session); try { return importKeyStoreTransacted(keyStore, missingKey); } finally { /* restore the session */ getSessionManager().setSession(previousSession); } } catch (KeyStoreException e) { throw new DatabaseException(e); } } }); } catch (DatabaseException e) { Throwable cause = e.getCause(); if (cause == null) { cause = e; } if (cause instanceof KeyStoreException) { throw (KeyStoreException) cause; } throw new KeyStoreException(cause); } catch (ConstraintViolationException e) { logger.warn( "ConstraintViolationException. A certificate was probably already in the certStore. Message: " + e.getMessage()); } return importedEntries; } @Override public void getPFX(final Collection<X509Certificate> certificates, final char[] password, final OutputStream pfx) throws KeyStoreException { getPFX(certificates, password, true, pfx); } @Override public void getPFX(final Collection<X509Certificate> certificates, final char[] password, final boolean includeRoot, final OutputStream pfx) throws KeyStoreException { Check.notNull(certificates, "certificates"); Check.notNull(password, "password"); try { getActionExecutor().executeTransaction(new DatabaseVoidAction() { @Override public void doAction(Session session) throws DatabaseException { try { Session previousSession = getSessionManager().getSession(); getSessionManager().setSession(session); try { getPFXTransacted(certificates, password, includeRoot, pfx); } finally { /* restore the session */ getSessionManager().setSession(previousSession); } } catch (KeyStoreException e) { throw new DatabaseException(e); } } }); } catch (DatabaseException e) { Throwable cause = e.getCause(); if (cause == null) { cause = e; } if (cause instanceof KeyStoreException) { throw (KeyStoreException) cause; } throw new KeyStoreException(cause); } } private int importChain(Certificate[] chain) { int imported = 0; if (chain == null || chain.length == 0) { return imported; } for (Certificate certificate : chain) { if (!(certificate instanceof X509Certificate)) { continue; } X509Certificate x509Certificate = (X509Certificate) certificate; try { if (!keyAndCertStore.contains(x509Certificate)) { keyAndCertStore.addCertificate((X509Certificate) certificate); imported++; } } catch (CertificateAlreadyExistsException e) { // ignore } catch (CertStoreException e) { logger.error("Error importing certificate.", e); } } return imported; } private int importKeyStoreTransacted(KeyStore keyStore, MissingKey missingKey) throws KeyStoreException { Check.notNull(keyStore, "keyStore"); Check.notNull(missingKey, "missingKey"); int importedEntries = 0; Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); logger.debug("Alias: " + alias); Certificate certificate = keyStore.getCertificate(alias); if (!(certificate instanceof X509Certificate)) { /* * only X509Certificates are supported */ continue; } try { Key key = keyStore.getKey(alias, null); if (!(key instanceof PrivateKey)) { key = null; } if (key == null && missingKey == MissingKey.SKIP_CERTIFICATE) { logger.debug("Certificate found but missing Private key. Skipping certificate"); continue; } KeyAndCertificate keyAndCertificate = new KeyAndCertificateImpl((PrivateKey) key, (X509Certificate) certificate); if (keyAndCertStore.addKeyAndCertificate(keyAndCertificate)) { importedEntries++; } Certificate[] chain = keyStore.getCertificateChain(alias); importedEntries += importChain(chain); } catch (UnrecoverableKeyException e) { logger.error("Unable to retrieve the key.", e); } catch (NoSuchAlgorithmException e) { logger.error("Unable to retrieve the key.", e); } catch (KeyStoreException e) { logger.error("Unable to retrieve the key.", e); } catch (CertStoreException e) { logger.error("Unable to retrieve the key.", e); } } return importedEntries; } private void getPFXTransacted(Collection<X509Certificate> certificates, char[] password, boolean includeRoot, OutputStream pfx) throws KeyStoreException { try { KeyStore keyStore = SecurityFactoryFactory.getSecurityFactory().createKeyStore("PKCS12"); keyStore.load(null); for (X509Certificate certificate : certificates) { if (certificate == null) { continue; } X509CertStoreEntry entry = keyAndCertStore.getByCertificate(certificate); if (entry != null && entry.getCertificate() != null) { KeyAndCertificate keyAndCertificate = keyAndCertStore.getKeyAndCertificate(entry); if (keyAndCertificate != null) { if (!certificate.equals(keyAndCertificate.getCertificate())) { throw new IllegalStateException("Certificate mismatch."); } X509Certificate[] chain = null; /* * Build a certificate chain so we add the chain (if valid) */ try { CertificatePathBuilder pathBuilder = pathBuilderFactory.createCertificatePathBuilder(); CertPathBuilderResult pathBuilderResult = pathBuilder.buildPath(certificate); X509Certificate root = null; if (includeRoot && pathBuilderResult instanceof PKIXCertPathBuilderResult) { TrustAnchor trustAnchor = ((PKIXCertPathBuilderResult) pathBuilderResult) .getTrustAnchor(); if (trustAnchor != null) { root = trustAnchor.getTrustedCert(); } } CertPath certPath = pathBuilderResult.getCertPath(); if (certPath != null && CollectionUtils.isNotEmpty(certPath.getCertificates())) { List<X509Certificate> completePath = new LinkedList<X509Certificate>(); for (Certificate fromPath : certPath.getCertificates()) { if (!(fromPath instanceof X509Certificate)) { /* * only X509Certificates are supported */ continue; } completePath.add((X509Certificate) fromPath); } if (root != null && includeRoot) { completePath.add(root); } chain = new X509Certificate[completePath.size()]; chain = completePath.toArray(chain); } } catch (CertPathBuilderException e) { logger.warn( "Could not build a path. Message: " + ExceptionUtils.getRootCauseMessage(e)); } if (ArrayUtils.getLength(chain) == 0) { chain = new X509Certificate[] { certificate }; } String alias = X509CertificateInspector.getThumbprint(certificate); if (keyAndCertificate.getPrivateKey() != null) { keyStore.setKeyEntry(alias, keyAndCertificate.getPrivateKey(), password, chain); } else { keyStore.setCertificateEntry(alias, certificate); } } } } keyStore.store(pfx, password); } catch (NoSuchAlgorithmException e) { throw new KeyStoreException(e); } catch (CertificateException e) { throw new KeyStoreException(e); } catch (IOException e) { throw new KeyStoreException(e); } catch (CertStoreException e) { throw new KeyStoreException(e); } catch (NoSuchProviderException e) { throw new NoSuchProviderRuntimeException(e); } catch (SecurityFactoryFactoryException e) { throw new KeyStoreException(e); } } }