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.common.security.crl; import java.io.IOException; import java.math.BigInteger; import java.security.cert.CRL; import java.security.cert.CertPath; import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathBuilderResult; import java.security.cert.CertificateExpiredException; import java.security.cert.X509CRL; import java.security.cert.X509CRLSelector; import java.util.Collection; import java.util.Collections; import mitm.common.security.crlstore.CRLStoreException; import mitm.common.security.crlstore.X509CRLStoreExt; import mitm.common.util.Check; import mitm.common.util.CloseableIterator; import mitm.common.util.CloseableIteratorException; import mitm.common.util.MissingDateException; import org.apache.commons.lang.exception.ExceptionUtils; import org.bouncycastle.asn1.x509.IssuingDistributionPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This CRL maintainer keeps the CRL store as up-to-date as possible by removing CRLs that are * superseded by new CRLs. This CRL maintainer does not check whether CRLs are trusted and valid. * It's the callers responsibility to check the trust. * * @author Martijn Brinkers * */ public class CRLStoreMaintainerImpl implements CRLStoreMaintainer { private final static Logger logger = LoggerFactory.getLogger(CRLStoreMaintainerImpl.class); /* * Store for the CRLs */ private final X509CRLStoreExt crlStore; /* * Used to check the trust of a CRL */ private final CRLPathBuilderFactory pathBuilderFactory; /* * If true the CRLs will be trust checked */ private final boolean checktrust; public CRLStoreMaintainerImpl(X509CRLStoreExt crlStore, CRLPathBuilderFactory pathBuilderFactory) { Check.notNull(crlStore, "crlStore"); Check.notNull(pathBuilderFactory, "pathBuilderFactory"); this.crlStore = crlStore; this.pathBuilderFactory = pathBuilderFactory; this.checktrust = true; } public CRLStoreMaintainerImpl(X509CRLStoreExt crlStore, CRLPathBuilderFactory pathBuilderFactory, boolean checkTrust) { Check.notNull(crlStore, "crlStore"); Check.notNull(pathBuilderFactory, "pathBuilderFactory"); this.crlStore = crlStore; this.pathBuilderFactory = pathBuilderFactory; this.checktrust = checkTrust; } private CertPath getCRLCertPath(X509CRL crl) { CertPath certPath = null; try { CRLPathBuilder pathBuilder = pathBuilderFactory.createCRLPathBuilder(); try { CertPathBuilderResult pathBuilderResult = pathBuilder.buildPath(crl); if (pathBuilderResult != null) { certPath = pathBuilderResult.getCertPath(); } } catch (CertPathBuilderException e) { /* * CertPathBuilderException is thrown for a lot of reasons so we will try to extract * the reason. */ Throwable rootCause = ExceptionUtils.getRootCause(e); Throwable cause = (rootCause != null ? rootCause : e); String errorMessage; if (cause instanceof CertificateExpiredException) { errorMessage = "Certificate in the CRL path is expired. CRL: " + X509CRLInspector.toString(crl) + ". Message: " + cause.getMessage(); } else { errorMessage = "Error while building path for CRL. CRL: " + X509CRLInspector.toString(crl); } if (logger.isDebugEnabled()) { logger.error(errorMessage, cause); } else { logger.error(errorMessage + ". Message: " + cause.getMessage()); } } } catch (CRLStoreException e) { logger.error("error creating CRLPathBuilder", e); } return certPath; } private CloseableIterator<X509CRL> getCRLsWithSameIssuer(X509CRL crl) throws CRLStoreException { Check.notNull(crl, "crl"); X509CRLSelector crlSelector = new X509CRLSelector(); crlSelector.setIssuers(Collections.singletonList(crl.getIssuerX500Principal())); CloseableIterator<X509CRL> iterator = crlStore.getCRLIterator(crlSelector); return iterator; } /* * Returns true if the Issuing Distribution Points are the same. If both are null they are * considered the same. */ private boolean isSameIDP(IssuingDistributionPoint idp, IssuingDistributionPoint otherIDP) { if (idp == otherIDP) { return true; } if (idp != null && idp.equals(otherIDP)) { return true; } if (otherIDP != null && otherIDP.equals(idp)) { return true; } return false; } private boolean internalAddX509CRL(X509CRL newCRL, CertPath newCRLCertPath) throws CRLStoreException, CloseableIteratorException, IOException { /* * Find all CRLs that have the same issuer. Note that this does not mean that the CRL is * issued by the same issuer, only that they have the same issuer X500 subject. We should * build a path check if they really have the same issuer. */ CloseableIterator<X509CRL> crlIterator = getCRLsWithSameIssuer(newCRL); boolean added = false; try { IssuingDistributionPoint newIDP = X509CRLInspector.getIssuingDistributionPoint(newCRL); boolean isDeltaCRL = X509CRLInspector.isDeltaCRL(newCRL); BigInteger crlNumber = X509CRLInspector.getCRLNumber(newCRL); /* * true if the CRL is completely new (ie. there is no CRL which is newer or older) */ boolean isNewCRL = true; while (crlIterator.hasNext()) { try { X509CRL oldCRL = crlIterator.next(); if (oldCRL == null) { logger.warn("CRL is null"); continue; } if (checktrust) { /* * We should check if the new CRL has the same issuer as the old CRL and not just * equal issuer subject. */ CertPath oldCRLCertPath = getCRLCertPath(oldCRL); if (oldCRLCertPath == null) { /* * Because the old CRL is not trusted there is no reliable way to detect whether * the new CRL supersedes the old CRL. */ logger.debug("Old CRL is not trusted. Skip old CRL."); continue; } /* * Compare the certificate paths to make sure they are equal */ if (!oldCRLCertPath.equals(newCRLCertPath)) { logger.debug( "new CRL has a different issuer than old CRL even though subjects are equal."); continue; } } IssuingDistributionPoint oldIDP = X509CRLInspector.getIssuingDistributionPoint(oldCRL); if (isSameIDP(newIDP, oldIDP)) { /* * either both CRLs must be delta CRLs or both are not delta CRLs and * either both have a CRLNumber or both do not have a CRLNumber */ if (isDeltaCRL == X509CRLInspector.isDeltaCRL(oldCRL) && ((crlNumber != null) == (X509CRLInspector.getCRLNumber(oldCRL) != null))) { try { if (CRLUtils.isNewer(newCRL, oldCRL)) { logger.info("Replacing " + X509CRLInspector.toString(oldCRL) + " with " + X509CRLInspector.toString(newCRL)); crlStore.replace(oldCRL, newCRL); added = true; /* the CRL is a replacement so it's not a new CRL */ isNewCRL = false; } else { logger.debug("The CRL is older than the CRL in the store."); /* A newer version of the CRL is present so it's not a new CRL */ isNewCRL = false; } } catch (MissingDateException e) { logger.error("Error reading CRL. Skipping CRL.", e); continue; } } } } catch (IOException e) { logger.error("Error reading CRL. Skipping CRL.", e); continue; } catch (CloseableIteratorException e) { logger.error("Error reading CRL. Skipping CRL.", e); continue; } } if (isNewCRL) { crlStore.addCRL(newCRL); added = true; } return added; } finally { crlIterator.close(); } } private boolean internalAddCRL(CRL crl) throws CRLStoreException, NoX509CRLException { if (!(crl instanceof X509CRL)) { throw new NoX509CRLException("Only X509CRLs are supported."); } boolean added = false; X509CRL x509CRL = (X509CRL) crl; if (crlStore.contains(x509CRL)) { logger.debug("CRL is already in the store."); } else { try { CertPath newCRLCertPath = null; boolean shouldAdd = true; if (checktrust) { newCRLCertPath = getCRLCertPath(x509CRL); if (newCRLCertPath == null) { logger.debug("CRL is not trusted and therefore not added."); shouldAdd = false; } } if (shouldAdd) { added = internalAddX509CRL(x509CRL, newCRLCertPath); } } catch (CloseableIteratorException e) { throw new CRLStoreException(e); } catch (IOException e) { throw new CRLStoreException(e); } } return added; } /** * Adds the CRL to the store but only if it is newer than existing CRLs. */ @Override public boolean addCRL(CRL crl) throws CRLStoreException { try { return internalAddCRL(crl); } catch (NoX509CRLException e) { throw new CRLStoreException(e); } } /** * Adds the CRLs to the store but only if they are newer than existing CRLs. */ @Override public int addCRLs(Collection<? extends CRL> crls) { int nrAdded = 0; for (CRL crl : crls) { try { boolean added = internalAddCRL(crl); if (added) nrAdded++; } catch (CRLStoreException e) { logger.error("Error reading CRL. Skipping CRL", e); } catch (NoX509CRLException e) { logger.warn("CRL skipped because it is not a X509CRL."); } } return nrAdded; } }