mitm.application.djigzo.workflow.impl.KeyAndCertificateWorkflowImpl.java Source code

Java tutorial

Introduction

Here is the source code for mitm.application.djigzo.workflow.impl.KeyAndCertificateWorkflowImpl.java

Source

/*
 * 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);
        }
    }
}