mitm.common.security.crl.CRLStoreUpdaterImpl.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.security.crl.CRLStoreUpdaterImpl.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.common.security.crl;

import java.io.IOException;
import java.net.URI;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathBuilderResult;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.cert.X509Extension;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import mitm.common.security.NoSuchProviderRuntimeException;
import mitm.common.security.SecurityFactoryFactoryException;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.security.certificate.X509ExtensionInspector;
import mitm.common.security.certpath.CertificatePathBuilder;
import mitm.common.security.certstore.BasicCertStore;
import mitm.common.security.certstore.CertStoreUtils;
import mitm.common.security.crlstore.CRLStoreException;
import mitm.common.util.CloseableIterator;
import mitm.common.util.CloseableIteratorException;

import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of CRLStoreUpdater.
 * 
 * @author Martijn Brinkers
 *
 */
public class CRLStoreUpdaterImpl implements CRLStoreUpdater {
    private final static Logger logger = LoggerFactory.getLogger(CRLStoreUpdaterImpl.class);

    private final CRLStoreUpdaterParameters updaterParameters;

    public CRLStoreUpdaterImpl(CRLStoreUpdaterParameters updaterParameters) {
        this.updaterParameters = updaterParameters;
    }

    /*
     * Check if the certificate is trusted up to a Trust Anchor.
     */
    private boolean isTrusted(X509Certificate certificate) throws NoSuchProviderException {
        boolean trusted = false;

        try {
            CertificatePathBuilder pathBuilder = updaterParameters.getCertificatePathBuilderFactory()
                    .createCertificatePathBuilder();

            /*
             * add the certificate to the PathBuilder so it is found using X509CertSelector 
             */
            try {
                pathBuilder.addCertStore(CertStoreUtils.createCertStore(certificate));
            } catch (InvalidAlgorithmParameterException e) {
                throw new CertPathBuilderException(e);
            } catch (NoSuchAlgorithmException e) {
                throw new CertPathBuilderException(e);
            } catch (SecurityFactoryFactoryException e) {
                throw new CertPathBuilderException(e);
            }

            CertPathBuilderResult result = pathBuilder.buildPath(certificate);

            if (result == null) {
                throw new CertPathBuilderException("No valid CertPath found.");
            }

            trusted = true;
        } catch (CertPathBuilderException e) {
            /*
             * CertPathBuilderException is thrown for a lot of reasons so we will try to extract the reason. 
             * Because this exception can happen frequently under 'normal' circumstances 
             * (for example when a root is not installed) we will log the messages with the WARN level.
             */
            Throwable rootCause = ExceptionUtils.getRootCause(e);

            Throwable cause = (rootCause != null ? rootCause : e);

            if (cause instanceof CertificateExpiredException) {
                logger.warn("Certificate is expired. Certificate: " + X509CertificateInspector.toString(certificate)
                        + ". Message: " + cause.getMessage());
            } else {
                String errorMessage = "Error while building path for certificate. Certificate: "
                        + X509CertificateInspector.toString(certificate);

                if (logger.isDebugEnabled()) {
                    logger.warn(errorMessage, cause);
                } else {
                    logger.warn(errorMessage + ". Message: " + cause.getMessage());
                }
            }
        }

        return trusted;
    }

    /*
     * Check if the CRL is trusted up to a Trust Anchor.
     */
    private boolean isTrusted(X509CRL crl) throws NoSuchProviderException {
        boolean trusted = false;

        try {
            updaterParameters.getCRLPathBuilderFactory().createCRLPathBuilder().buildPath(crl);

            trusted = true;
        } catch (CertPathBuilderException e) {
            /*
             * CertPathBuilderException is thrown for a lot of reasons so we will try to extract the reason. 
             * Because this exception can happen frequently under 'normal' circumstances 
             * (for example when a root is not installed) we will log the messages with the WARN level.
             */
            Throwable rootCause = ExceptionUtils.getRootCause(e);

            Throwable cause = (rootCause != null ? rootCause : e);

            if (cause instanceof CertificateExpiredException) {
                logger.warn("CRL is expired. CRL: " + X509CRLInspector.toString(crl) + ". Message: "
                        + cause.getMessage());
            } else {
                String errorMessage = "Error while building path for CRL. CRL: " + X509CRLInspector.toString(crl);

                if (logger.isDebugEnabled()) {
                    logger.warn(errorMessage, cause);
                } else {
                    logger.warn(errorMessage + ". Message: " + cause.getMessage());
                }
            }
        } catch (CRLStoreException e) {
            logger.error("Error while building path for CRL.", e);
        }

        return trusted;
    }

    private boolean isTrusted(X509Extension extension) throws NoSuchProviderException {
        if (extension instanceof X509Certificate) {
            return isTrusted((X509Certificate) extension);
        } else if (extension instanceof X509CRL) {
            return isTrusted((X509CRL) extension);
        } else {
            throw new IllegalArgumentException("Unsupported extension.");
        }
    }

    private void addURIsFromCRLDistPoint(X509Extension extension, CRLDistPoint crlDistPoint, Set<URI> uris)
            throws NoSuchProviderException, CRLException {
        if (crlDistPoint != null) {
            Set<URI> certificateURIs = CRLUtils.getAllDistributionPointURIs(crlDistPoint);

            for (URI uri : certificateURIs) {
                if (!uris.contains(uri)) {
                    /* if it's a new uri we must check the validity of the object */
                    if (updaterParameters.checkTrust() && !isTrusted(extension)) {
                        logger.debug(
                                "Certificate or CRL is not trusted so CRL will not be downloaded. URI: " + uri);
                    } else {
                        uris.add(uri);
                    }
                }
            }
        }
    }

    private void addURIsFromExtension(X509Extension extension, Set<URI> uris)
            throws NoSuchProviderException, CRLException {
        try {
            CRLDistPoint crlDistPoint = X509ExtensionInspector.getCRLDistibutionPoints(extension);

            addURIsFromCRLDistPoint(extension, crlDistPoint, uris);
        } catch (IOException e) {
            logger.error("Error getting CRL Distibution Points for:" + SystemUtils.LINE_SEPARATOR + extension, e);
        }

        try {
            CRLDistPoint crlDistPoint = X509ExtensionInspector.getFreshestCRL(extension);

            addURIsFromCRLDistPoint(extension, crlDistPoint, uris);
        } catch (IOException e) {
            logger.error(
                    "Error getting Freshest CRL distibution Points for:" + SystemUtils.LINE_SEPARATOR + extension,
                    e);
        }
    }

    private void addURIsFromCertificates(CloseableIterator<? extends Certificate> iterator, Set<URI> uris)
            throws NoSuchProviderException, CloseableIteratorException {
        while (iterator.hasNext()) {
            try {
                Certificate certificate = iterator.next();

                if (!(certificate instanceof X509Extension)) {
                    logger.warn("Certificate is not a X509Extension.");

                    continue;
                }

                addURIsFromExtension((X509Extension) certificate, uris);
            } catch (Exception e) {
                logger.error("Error getting URIs from certificate. Skipping certificate.", e);
            }
        }
    }

    private void addURIsFromCRLs(CloseableIterator<? extends CRL> iterator, Set<URI> uris)
            throws NoSuchProviderException, CloseableIteratorException {
        while (iterator.hasNext()) {
            try {
                CRL crl = iterator.next();

                if (!(crl instanceof X509Extension)) {
                    logger.warn("CRL is not a X509Extension.");

                    continue;
                }

                addURIsFromExtension((X509Extension) crl, uris);
            } catch (Exception e) {
                logger.error("Error getting URIs from CRL. Skipping CRL.", e);
            }
        }
    }

    private int downloadCRLs(Set<URI> uris) {
        int crlsAdded = 0;

        for (URI uri : uris) {
            if (uri == null) {
                logger.warn("URL is null.");

                continue;
            }

            if (uri.getScheme() == null) {
                logger.warn("Missing scheme. " + uri);
            }

            logger.debug("Downloading CRL from: " + uri);

            try {
                Collection<? extends CRL> downloadedCrls = updaterParameters.getCRLDownloader().downloadCRLs(uri);

                logger.debug("Successfully downloaded CRLs from: " + uri);

                int newAdded = updaterParameters.getCRLStoreMaintainer().addCRLs(downloadedCrls);

                crlsAdded = crlsAdded + newAdded;
            } catch (CRLException e) {
                logger.error("Error handling CRL. URI: " + uri, e);
            } catch (IOException e) {
                /*
                 * We will log WARN level because downloading CRLs often result in IOException's because the CRL
                 * distribution point is no longer available for a lot of roots.
                 */
                logger.warn("IO Exception downloading CRL. URI: " + uri + ". Message: " + e.getMessage());

                if (logger.isDebugEnabled()) {
                    logger.debug("More info.", e);
                }
            } catch (Exception e) {
                /*
                 * Catch all exceptions to make sure that other CRLs are downloaded.
                 */
                logger.error("Error while downloading CRL.  URI: " + uri, e);
            }
        }

        return crlsAdded;
    }

    @Override
    public void update() throws CRLStoreException {
        Set<URI> uris = new HashSet<URI>();

        try {
            Collection<? extends BasicCertStore> stores = updaterParameters.getCertStores();

            for (BasicCertStore store : stores) {
                /* Add CRL distribution points from the certificates in the CertStore */
                CloseableIterator<? extends Certificate> certificateIterator = store.getCertificateIterator(null);

                try {
                    addURIsFromCertificates(certificateIterator, uris);
                } finally {
                    certificateIterator.close();
                }

                /* Add CRL distribution points from the CRLs in the CRLStore */
                CloseableIterator<? extends CRL> crlIterator = updaterParameters.getCRLStore().getCRLIterator(null);

                try {
                    addURIsFromCRLs(crlIterator, uris);
                } finally {
                    crlIterator.close();
                }
            }
        } catch (NoSuchProviderException e) {
            throw new NoSuchProviderRuntimeException(e);
        } catch (CloseableIteratorException e) {
            throw new CRLStoreException(e);
        } catch (CertStoreException e) {
            throw new CRLStoreException(e);
        }

        logger.info(uris.size() + " CRL distibution points found.");

        int crlsAdded = downloadCRLs(uris);

        logger.info(crlsAdded + " new CRLs added to the CRL store.");
    }
}