blob: dfe9cef52b9371c231a76a27b1d6f879c908b27a [file] [log] [blame]
package org.bouncycastle.jce.provider;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathBuilderResult;
import java.security.cert.CertPathBuilderSpi;
import java.security.cert.CertPathParameters;
import java.security.cert.CertificateParsingException;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.jcajce.PKIXCertStore;
import org.bouncycastle.jcajce.PKIXCertStoreSelector;
import org.bouncycastle.jcajce.PKIXExtendedBuilderParameters;
import org.bouncycastle.jcajce.PKIXExtendedParameters;
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
import org.bouncycastle.jce.exception.ExtCertPathBuilderException;
import org.bouncycastle.x509.ExtendedPKIXBuilderParameters;
import org.bouncycastle.x509.ExtendedPKIXParameters;
/**
* Implements the PKIX CertPathBuilding algorithm for BouncyCastle.
*
* @see CertPathBuilderSpi
*/
public class PKIXCertPathBuilderSpi
extends CertPathBuilderSpi
{
/**
* Build and validate a CertPath using the given parameter.
*
* @param params PKIXBuilderParameters object containing all information to
* build the CertPath
*/
public CertPathBuilderResult engineBuild(CertPathParameters params)
throws CertPathBuilderException, InvalidAlgorithmParameterException
{
PKIXExtendedBuilderParameters paramsPKIX;
if (params instanceof PKIXBuilderParameters)
{
PKIXExtendedParameters.Builder paramsPKIXBldr = new PKIXExtendedParameters.Builder((PKIXBuilderParameters)params);
PKIXExtendedBuilderParameters.Builder paramsBldrPKIXBldr;
if (params instanceof ExtendedPKIXParameters)
{
ExtendedPKIXBuilderParameters extPKIX = (ExtendedPKIXBuilderParameters)params;
;
for (Iterator it = extPKIX.getAdditionalStores().iterator(); it.hasNext();)
{
paramsPKIXBldr.addCertificateStore((PKIXCertStore)it.next());
}
paramsBldrPKIXBldr = new PKIXExtendedBuilderParameters.Builder(paramsPKIXBldr.build());
paramsBldrPKIXBldr.addExcludedCerts(extPKIX.getExcludedCerts());
paramsBldrPKIXBldr.setMaxPathLength(extPKIX.getMaxPathLength());
}
else
{
paramsBldrPKIXBldr = new PKIXExtendedBuilderParameters.Builder((PKIXBuilderParameters)params);
}
paramsPKIX = paramsBldrPKIXBldr.build();
}
else if (params instanceof PKIXExtendedBuilderParameters)
{
paramsPKIX = (PKIXExtendedBuilderParameters)params;
}
else
{
throw new InvalidAlgorithmParameterException(
"Parameters must be an instance of "
+ PKIXBuilderParameters.class.getName() + " or "
+ PKIXExtendedBuilderParameters.class.getName() + ".");
}
Collection targets;
Iterator targetIter;
List certPathList = new ArrayList();
X509Certificate cert;
// search target certificates
PKIXCertStoreSelector certSelect = paramsPKIX.getBaseParameters().getTargetConstraints();
try
{
targets = CertPathValidatorUtilities.findCertificates(certSelect, paramsPKIX.getBaseParameters().getCertificateStores());
targets.addAll(CertPathValidatorUtilities.findCertificates(certSelect, paramsPKIX.getBaseParameters().getCertStores()));
}
catch (AnnotatedException e)
{
throw new ExtCertPathBuilderException(
"Error finding target certificate.", e);
}
if (targets.isEmpty())
{
throw new CertPathBuilderException(
"No certificate found matching targetContraints.");
}
CertPathBuilderResult result = null;
// check all potential target certificates
targetIter = targets.iterator();
while (targetIter.hasNext() && result == null)
{
cert = (X509Certificate) targetIter.next();
result = build(cert, paramsPKIX, certPathList);
}
if (result == null && certPathException != null)
{
if (certPathException instanceof AnnotatedException)
{
throw new CertPathBuilderException(certPathException.getMessage(), certPathException.getCause());
}
throw new CertPathBuilderException(
"Possible certificate chain could not be validated.",
certPathException);
}
if (result == null && certPathException == null)
{
throw new CertPathBuilderException(
"Unable to find certificate chain.");
}
return result;
}
private Exception certPathException;
protected CertPathBuilderResult build(X509Certificate tbvCert,
PKIXExtendedBuilderParameters pkixParams, List tbvPath)
{
// If tbvCert is readily present in tbvPath, it indicates having run
// into a cycle in the
// PKI graph.
if (tbvPath.contains(tbvCert))
{
return null;
}
// step out, the certificate is not allowed to appear in a certification
// chain.
if (pkixParams.getExcludedCerts().contains(tbvCert))
{
return null;
}
// test if certificate path exceeds maximum length
if (pkixParams.getMaxPathLength() != -1)
{
if (tbvPath.size() - 1 > pkixParams.getMaxPathLength())
{
return null;
}
}
tbvPath.add(tbvCert);
CertificateFactory cFact;
PKIXCertPathValidatorSpi validator;
CertPathBuilderResult builderResult = null;
try
{
cFact = new CertificateFactory();
validator = new PKIXCertPathValidatorSpi();
}
catch (Exception e)
{
// cannot happen
throw new RuntimeException("Exception creating support classes.");
}
try
{
// check whether the issuer of <tbvCert> is a TrustAnchor
if (CertPathValidatorUtilities.findTrustAnchor(tbvCert, pkixParams.getBaseParameters().getTrustAnchors(),
pkixParams.getBaseParameters().getSigProvider()) != null)
{
// exception message from possibly later tried certification
// chains
CertPath certPath = null;
PKIXCertPathValidatorResult result = null;
try
{
certPath = cFact.engineGenerateCertPath(tbvPath);
}
catch (Exception e)
{
throw new AnnotatedException(
"Certification path could not be constructed from certificate list.",
e);
}
try
{
result = (PKIXCertPathValidatorResult) validator.engineValidate(
certPath, pkixParams);
}
catch (Exception e)
{
throw new AnnotatedException(
"Certification path could not be validated.", e);
}
return new PKIXCertPathBuilderResult(certPath, result
.getTrustAnchor(), result.getPolicyTree(), result
.getPublicKey());
}
else
{
List stores = new ArrayList();
stores.addAll(pkixParams.getBaseParameters().getCertificateStores());
// add additional X.509 stores from locations in certificate
try
{
stores.addAll(CertPathValidatorUtilities.getAdditionalStoresFromAltNames(
tbvCert.getExtensionValue(Extension.issuerAlternativeName.getId()), pkixParams.getBaseParameters().getNamedCertificateStoreMap()));
}
catch (CertificateParsingException e)
{
throw new AnnotatedException(
"No additional X.509 stores can be added from certificate locations.",
e);
}
Collection issuers = new HashSet();
// try to get the issuer certificate from one
// of the stores
try
{
issuers.addAll(CertPathValidatorUtilities.findIssuerCerts(tbvCert, pkixParams.getBaseParameters().getCertStores(), stores));
}
catch (AnnotatedException e)
{
throw new AnnotatedException(
"Cannot find issuer certificate for certificate in certification path.",
e);
}
if (issuers.isEmpty())
{
throw new AnnotatedException(
"No issuer certificate for certificate in certification path found.");
}
Iterator it = issuers.iterator();
while (it.hasNext() && builderResult == null)
{
X509Certificate issuer = (X509Certificate) it.next();
builderResult = build(issuer, pkixParams, tbvPath);
}
}
}
catch (AnnotatedException e)
{
certPathException = e;
}
if (builderResult == null)
{
tbvPath.remove(tbvCert);
}
return builderResult;
}
}