xades4j.providers.impl.PKIXCertificateValidationProvider Maven / Gradle / Ivy
Show all versions of xades4j Show documentation
/*
* XAdES4j - A Java library for generation and verification of XAdES signatures.
* Copyright (C) 2010 Luis Goncalves.
*
* XAdES4j is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or any later version.
*
* XAdES4j 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with XAdES4j. If not, see .
*/
package xades4j.providers.impl;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLSelector;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import xades4j.providers.CannotBuildCertificationPathException;
import xades4j.providers.CannotSelectCertificateException;
import xades4j.providers.CertificateValidationException;
import xades4j.providers.CertificateValidationProvider;
import xades4j.providers.ValidationData;
import xades4j.verification.UnexpectedJCAException;
/**
* Implementation of {@code CertificateValidationProvider} using a PKIX {@code CertPathBuilder}.
*
* Since the Java's PKIX API doesn't allow to access the CRLs used in the certification
* path validation, this is manually done. There has to be a CRL for each issuer
* in the path which is valid at the moment of validation (signature and date).
* @author Luís
*/
public class PKIXCertificateValidationProvider implements CertificateValidationProvider
{
private static final int DEFAULT_MAX_PATH_LENGTH = 6;
private final KeyStore trustAnchors;
private final boolean revocationEnabled;
private final int maxPathLength;
private final CertStore[] intermCertsAndCrls;
private final CertPathBuilder certPathBuilder;
private final String signatureProvider;
/**
* Initializes a new instance that uses the specified JCE providers for CertPathBuilder
* and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param maxPathLength the maximum length of the certification paths
* @param certPathBuilderProvider the CertPathBuilder provider
* @param signatureProvider the Signature provider
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
int maxPathLength,
String certPathBuilderProvider,
String signatureProvider,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
if (null == trustAnchors)
{
throw new NullPointerException("Trust anchors cannot be null");
}
this.trustAnchors = trustAnchors;
this.revocationEnabled = revocationEnabled;
this.maxPathLength = maxPathLength;
this.certPathBuilder = certPathBuilderProvider == null ? CertPathBuilder.getInstance("PKIX") : CertPathBuilder.getInstance("PKIX", certPathBuilderProvider);
this.signatureProvider = signatureProvider;
this.intermCertsAndCrls = intermCertsAndCrls;
}
/**
* Initializes a new instance that uses the specified JCE providers for CertPathBuilder
* and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param certPathBuilderProvider the CertPathBuilder provider
* @param signatureProvider the Signature provider
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
String certPathBuilderProvider,
String signatureProvider,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, certPathBuilderProvider, signatureProvider, intermCertsAndCrls);
}
/**
* Initializes a new instance that uses the specified JCE provider for both
* CertPathBuilder and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param maxPathLength the maximum length of the certification paths
* @param jceProvider the CertPathBuilder and Signature provider
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
int maxPathLength,
String jceProvider,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
this(trustAnchors, revocationEnabled, maxPathLength, jceProvider, jceProvider, intermCertsAndCrls);
}
/**
* Initializes a new instance that uses the specified JCE provider for both
* CertPathBuilder and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param jceProvider the CertPathBuilder and Signature provider
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
String jceProvider,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, jceProvider, intermCertsAndCrls);
}
/**
* Initializes a new instance without specifying the JCE providers for CertPathBuilder
* and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param maxPathLength the maximum length of the certification paths
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
int maxPathLength,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
this(trustAnchors, revocationEnabled, maxPathLength, null, null, intermCertsAndCrls);
}
/**
* Initializes a new instance without specifying the JCE providers for CertPathBuilder
* and Signature.
* @param trustAnchors the keystore with the trust-anchors ({@code TrustedCertificateEntry})
* @param revocationEnabled whether revocation is enabled
* @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
* used in the construction of the certification path. May contain CRLs to be used
* if revocation is enabled
* @see xades4j.utils.FileSystemDirectoryCertStore
* @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
*/
public PKIXCertificateValidationProvider(
KeyStore trustAnchors,
boolean revocationEnabled,
CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException, NoSuchProviderException
{
this(trustAnchors, revocationEnabled, DEFAULT_MAX_PATH_LENGTH, null, null, intermCertsAndCrls);
}
@Override
public ValidationData validate(
X509CertSelector certSelector,
Date validationDate,
Collection otherCerts) throws CertificateValidationException, UnexpectedJCAException
{
PKIXBuilderParameters builderParams;
try
{
builderParams = new PKIXBuilderParameters(trustAnchors, certSelector);
} catch (KeyStoreException ex)
{
throw new CannotBuildCertificationPathException(certSelector, "Trust anchors KeyStore is not initialized", ex);
} catch (InvalidAlgorithmParameterException ex)
{
throw new CannotBuildCertificationPathException(certSelector, "Trust anchors KeyStore has no trusted certificate entries", ex);
}
PKIXCertPathBuilderResult builderRes;
try
{
// Certificates to be used to build the certification path.
// - The other certificates from the signature (e.g. from KeyInfo).
if (otherCerts != null)
{
CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(otherCerts);
CertStore othersCertStore = CertStore.getInstance("Collection", ccsp);
builderParams.addCertStore(othersCertStore);
}
// - The external certificates/CRLs.
for (int i = 0; i < intermCertsAndCrls.length; i++)
{
builderParams.addCertStore(intermCertsAndCrls[i]);
}
builderParams.setRevocationEnabled(revocationEnabled);
builderParams.setMaxPathLength(maxPathLength);
builderParams.setDate(validationDate);
builderParams.setSigProvider(this.signatureProvider);
builderRes = (PKIXCertPathBuilderResult) certPathBuilder.build(builderParams);
}
catch (CertPathBuilderException ex)
{
throw new CannotBuildCertificationPathException(certSelector, ex.getMessage(), ex);
} catch (InvalidAlgorithmParameterException ex)
{
// SHOULD NOT be thrown due to wrong type of parameters.
// Seems to be thrown when the CertSelector (in builderParams) criteria
// cannot be applied.
throw new CannotSelectCertificateException(certSelector, ex);
} catch (NoSuchAlgorithmException ex)
{
// SHOULD NOT be thrown.
throw new UnexpectedJCAException("No provider for Collection CertStore", ex);
}
// The cert path returned by the builder ends in a certificate issued by
// the trust anchor. However, the complete path may be needed for property
// verification.
List certPath = (List) builderRes.getCertPath().getCertificates();
// - Create a new list since the previous is immutable.
certPath = new ArrayList(certPath);
// - Add the trust anchor certificate.
certPath.add(builderRes.getTrustAnchor().getTrustedCert());
if (revocationEnabled)
{
return new ValidationData(certPath, getCRLsForCertPath(certPath, validationDate));
}
return new ValidationData(certPath);
}
private Collection getCRLsForCertPath(
List certPath,
Date validationDate) throws CertificateValidationException
{
// Map the issuers certificates in the chain. This is used to know the issuers
// and later to verify the signatures in the CRLs.
Map issuersCerts = new HashMap(certPath.size() - 1);
for (int i = 0; i < certPath.size() - 1; i++)
{
// The issuer of one certificate is the subject of the following one.
issuersCerts.put(certPath.get(i).getIssuerX500Principal(), certPath.get(i + 1));
}
// Select all the CRLs from the issuers involved in the certification path
// that are valid at the moment.
X509CRLSelector crlSelector = new X509CRLSelector();
for (X500Principal issuer : issuersCerts.keySet())
{
// - "The issuer distinguished name in the X509CRL must match at least
// one of the specified distinguished names."
crlSelector.addIssuer(issuer);
}
// - "The specified date must be equal to or later than the value of the
// thisUpdate component of the X509CRL and earlier than the value of the
// nextUpdate component."
crlSelector.setDateAndTime(validationDate);
Set crls = new HashSet();
try
{
// Get the CRLs on each CertStore.
for (int i = 0; i < intermCertsAndCrls.length; i++)
{
Collection storeCRLs = intermCertsAndCrls[i].getCRLs(crlSelector);
crls.addAll(Collections.checkedCollection(storeCRLs, X509CRL.class));
}
} catch (CertStoreException ex)
{
throw new CertificateValidationException(null, "Cannot get CRLs", ex);
}
// Verify the CRLs' signatures. The issuers' certificates were validated
// as part of the cert path creation.
for (X509CRL crl : crls)
{
try
{
X509Certificate crlIssuerCert = issuersCerts.get(crl.getIssuerX500Principal());
if (null == this.signatureProvider)
{
crl.verify(crlIssuerCert.getPublicKey());
}
else
{
crl.verify(crlIssuerCert.getPublicKey(), this.signatureProvider);
}
}
catch (Exception ex)
{
throw new CertificateValidationException(null, "Invalid CRL signature from " + crl.getIssuerX500Principal().getName(), ex);
}
}
return crls;
}
}