org.bouncycastle.pkix.jcajce.RevocationUtilities Maven / Gradle / Ivy
package org.bouncycastle.pkix.jcajce;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.cert.CRLException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLEntry;
import java.security.cert.X509CRLSelector;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1Enumerated;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
import org.bouncycastle.jcajce.PKIXCRLStore;
import org.bouncycastle.jcajce.PKIXCRLStoreSelector;
import org.bouncycastle.jcajce.PKIXCertStoreSelector;
import org.bouncycastle.jcajce.PKIXExtendedParameters;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.util.Selector;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.StoreException;
class RevocationUtilities
{
protected static final String ISSUING_DISTRIBUTION_POINT = Extension.issuingDistributionPoint.getId();
protected static Date getValidityDate(PKIXExtendedParameters paramsPKIX, Date currentDate)
{
Date validityDate = paramsPKIX.getValidityDate();
return null == validityDate ? currentDate : validityDate;
}
/**
* Extract the value of the given extension, if it exists.
*
* @param ext
* The extension object.
* @param oid
* The object identifier to obtain.
* @throws AnnotatedException
* if the extension cannot be read.
*/
protected static ASN1Primitive getExtensionValue(java.security.cert.X509Extension ext, ASN1ObjectIdentifier oid)
throws AnnotatedException
{
byte[] bytes = ext.getExtensionValue(oid.getId());
return null == bytes ? null : getObject(oid, bytes);
}
private static ASN1Primitive getObject(ASN1ObjectIdentifier oid, byte[] ext) throws AnnotatedException
{
try
{
return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(ext).getOctets());
}
catch (Exception e)
{
throw new AnnotatedException("exception processing extension " + oid, e);
}
}
/**
* Add to a LinkedHashSet all certificates or attribute certificates found in the X509Store's
* that are matching the certSelect criteria.
*
* @param certs
* a {@link LinkedHashSet} to which the certificates will be added.
* @param certSelect
* a {@link Selector} object that will be used to select the certificates
* @param certStores
* a List containing only {@link Store} objects. These are used to search for
* certificates.
* @return a Collection of all found {@link X509Certificate} May be empty but never
* null
.
*/
protected static void findCertificates(LinkedHashSet certs, PKIXCertStoreSelector certSelect, List certStores)
throws AnnotatedException
{
Iterator iter = certStores.iterator();
while (iter.hasNext())
{
Object obj = iter.next();
if (obj instanceof Store)
{
Store certStore = (Store)obj;
try
{
certs.addAll(certStore.getMatches(certSelect));
}
catch (StoreException e)
{
throw new AnnotatedException("Problem while picking certificates from X.509 store.", e);
}
}
else
{
CertStore certStore = (CertStore)obj;
try
{
certs.addAll(PKIXCertStoreSelector.getCertificates(certSelect, certStore));
}
catch (CertStoreException e)
{
throw new AnnotatedException("Problem while picking certificates from certificate store.", e);
}
}
}
}
static List getAdditionalStoresFromCRLDistributionPoint(CRLDistPoint crldp,
Map namedCRLStoreMap) throws AnnotatedException
{
if (crldp == null)
{
return Collections.emptyList();
}
DistributionPoint dps[];
try
{
dps = crldp.getDistributionPoints();
}
catch (Exception e)
{
throw new AnnotatedException("Distribution points could not be read.", e);
}
List stores = new ArrayList();
for (int i = 0; i < dps.length; i++)
{
DistributionPointName dpn = dps[i].getDistributionPoint();
// look for URIs in fullName
if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME)
{
GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()).getNames();
for (int j = 0; j < genNames.length; j++)
{
PKIXCRLStore store = namedCRLStoreMap.get(genNames[j]);
if (store != null)
{
stores.add(store);
}
}
}
}
return stores;
}
/**
* Add the CRL issuers from the cRLIssuer field of the distribution point or
* from the certificate if not given to the issuer criterion of the
* selector
.
*
* The issuerPrincipals
are a collection with a single
* X500Name
for X509Certificate
s.
*
* @param dp The distribution point.
* @param issuerPrincipals The issuers of the certificate or attribute
* certificate which contains the distribution point.
* @param selector The CRL selector.
* @throws AnnotatedException if an exception occurs while processing.
* @throws ClassCastException if issuerPrincipals
does not
* contain only X500Name
s.
*/
protected static void getCRLIssuersFromDistributionPoint(DistributionPoint dp, Collection issuerPrincipals,
X509CRLSelector selector) throws AnnotatedException
{
List issuers = new ArrayList();
// indirect CRL
if (dp.getCRLIssuer() != null)
{
GeneralName genNames[] = dp.getCRLIssuer().getNames();
// look for a DN
for (int j = 0; j < genNames.length; j++)
{
if (genNames[j].getTagNo() == GeneralName.directoryName)
{
try
{
issuers.add(X500Name.getInstance(genNames[j].getName()));
}
catch (IllegalArgumentException e)
{
throw new AnnotatedException(
"CRL issuer information from distribution point cannot be decoded.", e);
}
}
}
}
else
{
/*
* certificate issuer is CRL issuer, distributionPoint field MUST be
* present.
*/
if (dp.getDistributionPoint() == null)
{
throw new AnnotatedException(
"CRL issuer is omitted from distribution point but no distributionPoint field present.");
}
// add and check issuer principals
for (Iterator it = issuerPrincipals.iterator(); it.hasNext(); )
{
issuers.add(it.next());
}
}
// TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
// distributionPoint
// if (dp.getDistributionPoint() != null)
// {
// // look for nameRelativeToCRLIssuer
// if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
// {
// // append fragment to issuer, only one
// // issuer can be there, if this is given
// if (issuers.size() != 1)
// {
// throw new AnnotatedException(
// "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
// }
// ASN1Encodable relName = dp.getDistributionPoint().getName();
// Iterator it = issuers.iterator();
// List issuersTemp = new ArrayList(issuers.size());
// while (it.hasNext())
// {
// Enumeration e = null;
// try
// {
// e = ASN1Sequence.getInstance(
// new ASN1InputStream(((X500Principal) it.next())
// .getEncoded()).readObject()).getObjects();
// }
// catch (IOException ex)
// {
// throw new AnnotatedException(
// "Cannot decode CRL issuer information.", ex);
// }
// ASN1EncodableVector v = new ASN1EncodableVector();
// while (e.hasMoreElements())
// {
// v.add((ASN1Encodable) e.nextElement());
// }
// v.add(relName);
// issuersTemp.add(new X500Principal(new DERSequence(v)
// .getDEREncoded()));
// }
// issuers.clear();
// issuers.addAll(issuersTemp);
// }
// }
Iterator it = issuers.iterator();
while (it.hasNext())
{
try
{
selector.addIssuerName(((X500Name)it.next()).getEncoded());
}
catch (IOException ex)
{
throw new AnnotatedException(
"Cannot decode CRL issuer information.", ex);
}
}
}
protected static void getCertStatus(Date validDate, X509CRL crl, Object cert, CertStatus certStatus)
throws AnnotatedException
{
boolean isIndirect;
try
{
isIndirect = isIndirectCRL(crl);
}
catch (CRLException exception)
{
throw new AnnotatedException("Failed check for indirect CRL.", exception);
}
X509Certificate x509Cert = (X509Certificate)cert;
X500Name x509CertIssuer = getIssuer(x509Cert);
if (!isIndirect)
{
X500Name crlIssuer = getIssuer(crl);
if (!x509CertIssuer.equals(crlIssuer))
{
return;
}
}
X509CRLEntry crl_entry = crl.getRevokedCertificate(x509Cert.getSerialNumber());
if (null == crl_entry)
{
return;
}
if (isIndirect)
{
X500Principal certificateIssuer = crl_entry.getCertificateIssuer();
X500Name expectedCertIssuer;
if (null == certificateIssuer)
{
expectedCertIssuer = getIssuer(crl);
}
else
{
expectedCertIssuer = getX500Name(certificateIssuer);
}
if (!x509CertIssuer.equals(expectedCertIssuer))
{
return;
}
}
int reasonCodeValue = CRLReason.unspecified;
if (crl_entry.hasExtensions())
{
try
{
ASN1Primitive extValue = RevocationUtilities.getExtensionValue(crl_entry, Extension.reasonCode);
ASN1Enumerated reasonCode = ASN1Enumerated.getInstance(extValue);
if (null != reasonCode)
{
reasonCodeValue = reasonCode.intValueExact();
}
}
catch (Exception e)
{
throw new AnnotatedException("Reason code CRL entry extension could not be decoded.", e);
}
}
Date revocationDate = crl_entry.getRevocationDate();
if (validDate.before(revocationDate))
{
switch (reasonCodeValue)
{
case CRLReason.unspecified:
case CRLReason.keyCompromise:
case CRLReason.cACompromise:
case CRLReason.aACompromise:
break;
default:
return;
}
}
// (i) or (j)
certStatus.setCertStatus(reasonCodeValue);
certStatus.setRevocationDate(revocationDate);
}
/**
* Fetches delta CRLs according to RFC 3280 section 5.2.4.
*
* @param validityDate
* The date for which the delta CRLs must be valid.
* @param completeCRL
* The complete CRL the delta CRL is for.
* @return A Set
of X509CRL
s with delta CRLs.
* @throws AnnotatedException
* if an exception occurs while picking the delta CRLs.
*/
protected static Set getDeltaCRLs(Date validityDate, X509CRL completeCRL, List certStores,
List pkixCrlStores) throws AnnotatedException
{
X509CRLSelector baseDeltaSelect = new X509CRLSelector();
// 5.2.4 (a)
try
{
baseDeltaSelect.addIssuerName(completeCRL.getIssuerX500Principal().getEncoded());
}
catch (IOException e)
{
throw new AnnotatedException("cannot extract issuer from CRL.", e);
}
BigInteger completeCRLNumber = null;
try
{
ASN1Primitive derObject = RevocationUtilities.getExtensionValue(completeCRL, Extension.cRLNumber);
if (derObject != null)
{
completeCRLNumber = ASN1Integer.getInstance(derObject).getPositiveValue();
}
}
catch (Exception e)
{
throw new AnnotatedException(
"cannot extract CRL number extension from CRL", e);
}
// 5.2.4 (b)
byte[] idp;
try
{
idp = completeCRL.getExtensionValue(ISSUING_DISTRIBUTION_POINT);
}
catch (Exception e)
{
throw new AnnotatedException("issuing distribution point extension value could not be read", e);
}
// 5.2.4 (d)
baseDeltaSelect.setMinCRLNumber(completeCRLNumber == null ? null : completeCRLNumber
.add(BigInteger.valueOf(1)));
PKIXCRLStoreSelector.Builder selBuilder = new PKIXCRLStoreSelector.Builder(baseDeltaSelect);
selBuilder.setIssuingDistributionPoint(idp);
selBuilder.setIssuingDistributionPointEnabled(true);
// 5.2.4 (c)
selBuilder.setMaxBaseCRLNumber(completeCRLNumber);
PKIXCRLStoreSelector deltaSelect = selBuilder.build();
// find delta CRLs
Set temp = PKIXCRLUtil.findCRLs(deltaSelect, validityDate, certStores, pkixCrlStores);
Set result = new HashSet();
for (Iterator it = temp.iterator(); it.hasNext(); )
{
X509CRL crl = (X509CRL)it.next();
if (isDeltaCRL(crl))
{
result.add(crl);
}
}
return result;
}
private static boolean isDeltaCRL(X509CRL crl)
{
Set critical = crl.getCriticalExtensionOIDs();
return null == critical ? false : critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR);
}
/**
* Fetches complete CRLs according to RFC 3280.
*
* @param dp
* The distribution point for which the complete CRL
* @param cert
* The X509Certificate
for which the CRL should be searched.
* @return A Set
of X509CRL
s with complete CRLs.
* @throws AnnotatedException
* if an exception occurs while picking the CRLs or no CRLs are found.
*/
protected static Set getCompleteCRLs(DistributionPoint dp, Object cert, Date validityDate, List certStores, List crlStores)
throws AnnotatedException, CRLNotFoundException
{
X509CRLSelector baseCrlSelect = new X509CRLSelector();
try
{
Set issuers = new HashSet();
issuers.add(getIssuer((X509Certificate)cert));
RevocationUtilities.getCRLIssuersFromDistributionPoint(dp, issuers, baseCrlSelect);
}
catch (AnnotatedException e)
{
throw new AnnotatedException(
"Could not get issuer information from distribution point.", e);
}
if (cert instanceof X509Certificate)
{
baseCrlSelect.setCertificateChecking((X509Certificate)cert);
}
PKIXCRLStoreSelector crlSelect = new PKIXCRLStoreSelector.Builder(baseCrlSelect).setCompleteCRLEnabled(true).build();
Set crls = PKIXCRLUtil.findCRLs(crlSelect, validityDate, certStores, crlStores);
checkCRLsNotEmpty(crls, cert);
return crls;
}
/**
* Return the next working key inheriting DSA parameters if necessary.
*
* This methods inherits DSA parameters from the indexed certificate or
* previous certificates in the certificate chain to the returned
* PublicKey
. The list is searched upwards, meaning the end
* certificate is at position 0 and previous certificates are following.
*
*
* If the indexed certificate does not contain a DSA key this method simply
* returns the public key. If the DSA key already contains DSA parameters
* the key is also only returned.
*
*
* @param certs The certification path.
* @param index The index of the certificate which contains the public key
* which should be extended with DSA parameters.
* @return The public key of the certificate in list position
* index
extended with DSA parameters if applicable.
* @throws AnnotatedException if DSA parameters cannot be inherited.
*/
protected static PublicKey getNextWorkingKey(List certs, int index, JcaJceHelper helper)
throws CertPathValidatorException
{
Certificate cert = (Certificate)certs.get(index);
PublicKey pubKey = cert.getPublicKey();
if (!(pubKey instanceof DSAPublicKey))
{
return pubKey;
}
DSAPublicKey dsaPubKey = (DSAPublicKey)pubKey;
if (dsaPubKey.getParams() != null)
{
return dsaPubKey;
}
for (int i = index + 1; i < certs.size(); i++)
{
X509Certificate parentCert = (X509Certificate)certs.get(i);
pubKey = parentCert.getPublicKey();
if (!(pubKey instanceof DSAPublicKey))
{
throw new CertPathValidatorException(
"DSA parameters cannot be inherited from previous certificate.");
}
DSAPublicKey prevDSAPubKey = (DSAPublicKey)pubKey;
if (prevDSAPubKey.getParams() == null)
{
continue;
}
DSAParams dsaParams = prevDSAPubKey.getParams();
DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(
dsaPubKey.getY(), dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
try
{
KeyFactory keyFactory = helper.createKeyFactory("DSA");
return keyFactory.generatePublic(dsaPubKeySpec);
}
catch (Exception exception)
{
throw new RuntimeException(exception.getMessage());
}
}
throw new CertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
}
static void checkCRLsNotEmpty(Set crls, Object cert)
throws CRLNotFoundException
{
if (crls.isEmpty())
{
// if (cert instanceof X509AttributeCertificate)
// {
// X509AttributeCertificate aCert = (X509AttributeCertificate)cert;
//
// throw new NoCRLFoundException("No CRLs found for issuer \"" + aCert.getIssuer().getPrincipals()[0] + "\"");
// }
// else
{
X500Name certIssuer = getIssuer((X509Certificate)cert);
throw new CRLNotFoundException(
"No CRLs found for issuer \"" + RFC4519Style.INSTANCE.toString(certIssuer) + "\"");
}
}
}
public static boolean isIndirectCRL(X509CRL crl) throws CRLException
{
try
{
byte[] idp = crl.getExtensionValue(Extension.issuingDistributionPoint.getId());
return idp != null
&& IssuingDistributionPoint.getInstance(ASN1OctetString.getInstance(idp).getOctets()).isIndirectCRL();
}
catch (Exception e)
{
throw new CRLException("exception reading IssuingDistributionPoint", e);
}
}
private static X500Name getIssuer(X509Certificate cert)
{
return getX500Name(cert.getIssuerX500Principal());
}
private static X500Name getIssuer(X509CRL crl)
{
return getX500Name(crl.getIssuerX500Principal());
}
private static X500Name getX500Name(X500Principal principal)
{
return X500Name.getInstance(principal.getEncoded());
}
}