unresCritExts = entry.getCriticalExtensionOIDs();
if (unresCritExts != null && !unresCritExts.isEmpty()) {
/* remove any that we will process */
unresCritExts.remove
(PKIXExtensions.ReasonCode_Id.toString());
unresCritExts.remove
(PKIXExtensions.CertificateIssuer_Id.toString());
if (!unresCritExts.isEmpty()) {
if (debug != null) {
debug.println("Unrecognized "
+ "critical extension(s) in revoked CRL entry: "
+ unresCritExts);
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null,
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
reasonCode = entry.getRevocationReason();
if (reasonCode == null) {
reasonCode = CRLReason.UNSPECIFIED;
}
Throwable t = new CertificateRevokedException
(entry.getRevocationDate(), reasonCode,
crl.getIssuerX500Principal(), entry.getExtensions());
throw new CertPathValidatorException(t.getMessage(), t,
null, -1, BasicReason.REVOKED);
}
}
}
/**
* We have a cert whose revocation status couldn't be verified by
* a CRL issued by the cert that issued the CRL. See if we can
* find a valid CRL issued by a separate key that can verify the
* revocation status of this certificate.
*
* Note that this does not provide support for indirect CRLs,
* only CRLs signed with a different key (but the same issuer
* name) as the certificate being checked.
*
* @param currCert the X509Certificate
to be checked
* @param prevKey the PublicKey
that failed
* @param signFlag true
if that key was trusted to sign CRLs
* @param stackedCerts a Set
of X509Certificate
s>
* whose revocation status depends on the
* non-revoked status of this cert. To avoid
* circular dependencies, we assume they're
* revoked while checking the revocation
* status of this cert.
* @throws CertPathValidatorException if the cert's revocation status
* cannot be verified successfully with another key
*/
private void verifyWithSeparateSigningKey(X509Certificate currCert,
PublicKey prevKey, boolean signFlag, Set stackedCerts)
throws CertPathValidatorException {
String msg = "revocation status";
if (debug != null) {
debug.println(
"CrlRevocationChecker.verifyWithSeparateSigningKey()" +
" ---checking " + msg + "...");
}
// reject circular dependencies - RFC 3280 is not explicit on how
// to handle this, so we feel it is safest to reject them until
// the issue is resolved in the PKIX WG.
if ((stackedCerts != null) && stackedCerts.contains(currCert)) {
if (debug != null) {
debug.println(
"CrlRevocationChecker.verifyWithSeparateSigningKey()" +
" circular dependency");
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null,
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
// If prevKey wasn't trusted, maybe we just didn't have the right
// path to it. Don't rule that key out.
if (!signFlag) {
prevKey = null;
}
// Try to find another key that might be able to sign
// CRLs vouching for this cert.
buildToNewKey(currCert, prevKey, stackedCerts);
}
/**
* Tries to find a CertPath that establishes a key that can be
* used to verify the revocation status of a given certificate.
* Ignores keys that have previously been tried. Throws a
* CertPathValidatorException if no such key could be found.
*
* @param currCert the X509Certificate
to be checked
* @param prevKey the PublicKey
of the certificate whose key
* cannot be used to vouch for the CRL and should be ignored
* @param stackedCerts a Set
of X509Certificate
s>
* whose revocation status depends on the
* establishment of this path.
* @throws CertPathValidatorException on failure
*/
private void buildToNewKey(X509Certificate currCert,
PublicKey prevKey, Set stackedCerts)
throws CertPathValidatorException {
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey()" +
" starting work");
}
Set badKeys = new HashSet();
if (prevKey != null) {
badKeys.add(prevKey);
}
X509CertSelector certSel = new RejectKeySelector(badKeys);
certSel.setSubject(currCert.getIssuerX500Principal());
certSel.setKeyUsage(mCrlSignUsage);
Set newAnchors =
(mAnchor == null ? mParams.getTrustAnchors() :
Collections.singleton(mAnchor));
PKIXBuilderParameters builderParams;
if (mParams instanceof PKIXBuilderParameters) {
builderParams = (PKIXBuilderParameters) mParams.clone();
builderParams.setTargetCertConstraints(certSel);
// Policy qualifiers must be rejected, since we don't have
// any way to convey them back to the application.
builderParams.setPolicyQualifiersRejected(true);
try {
builderParams.setTrustAnchors(newAnchors);
} catch (InvalidAlgorithmParameterException iape) {
throw new RuntimeException(iape); // should never occur
}
} else {
// It's unfortunate that there's no easy way to make a
// PKIXBuilderParameters object from a PKIXParameters
// object. This might miss some things if parameters
// are added in the future or the validatorParams object
// is a custom class derived from PKIXValidatorParameters.
try {
builderParams = new PKIXBuilderParameters(newAnchors, certSel);
} catch (InvalidAlgorithmParameterException iape) {
throw new RuntimeException(iape); // should never occur
}
builderParams.setInitialPolicies(mParams.getInitialPolicies());
builderParams.setCertStores(mStores);
builderParams.setExplicitPolicyRequired
(mParams.isExplicitPolicyRequired());
builderParams.setPolicyMappingInhibited
(mParams.isPolicyMappingInhibited());
builderParams.setAnyPolicyInhibited(mParams.isAnyPolicyInhibited());
// Policy qualifiers must be rejected, since we don't have
// any way to convey them back to the application.
// That's the default, so no need to write code.
builderParams.setDate(mParams.getDate());
builderParams.setCertPathCheckers(mParams.getCertPathCheckers());
builderParams.setSigProvider(mParams.getSigProvider());
}
// Skip revocation during this build to detect circular
// references. But check revocation afterwards, using the
// key (or any other that works).
builderParams.setRevocationEnabled(false);
// check for AuthorityInformationAccess extension
if (Builder.USE_AIA == true) {
X509CertImpl currCertImpl = null;
try {
currCertImpl = X509CertImpl.toImpl(currCert);
} catch (CertificateException ce) {
// ignore but log it
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey: " +
"error decoding cert: " + ce);
}
}
AuthorityInfoAccessExtension aiaExt = null;
if (currCertImpl != null) {
aiaExt = currCertImpl.getAuthorityInfoAccessExtension();
}
if (aiaExt != null) {
List adList = aiaExt.getAccessDescriptions();
if (adList != null) {
for (AccessDescription ad : adList) {
CertStore cs = URICertStore.getInstance(ad);
if (cs != null) {
if (debug != null) {
debug.println("adding AIAext CertStore");
}
builderParams.addCertStore(cs);
}
}
}
}
}
CertPathBuilder builder = null;
try {
builder = CertPathBuilder.getInstance("PKIX");
} catch (NoSuchAlgorithmException nsae) {
throw new CertPathValidatorException(nsae);
}
while (true) {
try {
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey()" +
" about to try build ...");
}
PKIXCertPathBuilderResult cpbr =
(PKIXCertPathBuilderResult) builder.build(builderParams);
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey()" +
" about to check revocation ...");
}
// Now check revocation of all certs in path, assuming that
// the stackedCerts are revoked.
if (stackedCerts == null) {
stackedCerts = new HashSet();
}
stackedCerts.add(currCert);
TrustAnchor ta = cpbr.getTrustAnchor();
PublicKey prevKey2 = ta.getCAPublicKey();
if (prevKey2 == null) {
prevKey2 = ta.getTrustedCert().getPublicKey();
}
boolean signFlag = true;
List extends Certificate> cpList =
cpbr.getCertPath().getCertificates();
try {
for (int i = cpList.size()-1; i >= 0; i-- ) {
X509Certificate cert = (X509Certificate) cpList.get(i);
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey()"
+ " index " + i + " checking " + cert);
}
verifyRevocationStatus(cert, prevKey2, signFlag, true,
stackedCerts, newAnchors);
signFlag = certCanSignCrl(cert);
prevKey2 = cert.getPublicKey();
}
} catch (CertPathValidatorException cpve) {
// ignore it and try to get another key
badKeys.add(cpbr.getPublicKey());
continue;
}
if (debug != null) {
debug.println("CrlRevocationChecker.buildToNewKey()" +
" got key " + cpbr.getPublicKey());
}
// Now check revocation on the current cert using that key.
// If it doesn't check out, try to find a different key.
// And if we can't find a key, then return false.
PublicKey newKey = cpbr.getPublicKey();
try {
verifyRevocationStatus(currCert, newKey, true, false);
// If that passed, the cert is OK!
return;
} catch (CertPathValidatorException cpve) {
// If it is revoked, rethrow exception
if (cpve.getReason() == BasicReason.REVOKED) {
throw cpve;
}
// Otherwise, ignore the exception and
// try to get another key.
}
badKeys.add(newKey);
} catch (InvalidAlgorithmParameterException iape) {
throw new CertPathValidatorException(iape);
} catch (CertPathBuilderException cpbe) {
throw new CertPathValidatorException
("Could not determine revocation status", null, null,
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
}
/*
* This inner class extends the X509CertSelector to add an additional
* check to make sure the subject public key isn't on a particular list.
* This class is used by buildToNewKey() to make sure the builder doesn't
* end up with a CertPath to a public key that has already been rejected.
*/
private static class RejectKeySelector extends X509CertSelector {
private final Set badKeySet;
/**
* Creates a new RejectKeySelector
.
*
* @param badPublicKeys a Set
of
* PublicKey
s that
* should be rejected (or null
* if no such check should be done)
*/
RejectKeySelector(Set badPublicKeys) {
this.badKeySet = badPublicKeys;
}
/**
* Decides whether a Certificate
should be selected.
*
* @param cert the Certificate
to be checked
* @return true
if the Certificate
should be
* selected, false
otherwise
*/
public boolean match(Certificate cert) {
if (!super.match(cert))
return(false);
if (badKeySet.contains(cert.getPublicKey())) {
if (debug != null)
debug.println("RejectCertSelector.match: bad key");
return false;
}
if (debug != null)
debug.println("RejectCertSelector.match: returning true");
return true;
}
/**
* Return a printable representation of the CertSelector
.
*
* @return a String
describing the contents of the
* CertSelector
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("RejectCertSelector: [\n");
sb.append(super.toString());
sb.append(badKeySet);
sb.append("]");
return sb.toString();
}
}
/**
* Internal method that verifies a set of possible_crls,
* and sees if each is approved, based on the cert.
*
* @param crls a set of possible CRLs to test for acceptability
* @param cert the certificate whose revocation status is being checked
* @param signFlag true
if prevKey was trusted to sign CRLs
* @param prevKey the public key of the issuer of cert
* @param reasonsMask the reason code mask
* @param trustAnchors a Set
of TrustAnchor
s>
* @return a collection of approved crls (or an empty collection)
*/
private Collection verifyPossibleCRLs(Set crls,
X509Certificate cert, boolean signFlag, PublicKey prevKey,
boolean[] reasonsMask,
Set trustAnchors) throws CertPathValidatorException {
try {
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
if (debug != null) {
debug.println("CRLRevocationChecker.verifyPossibleCRLs: " +
"Checking CRLDPs for "
+ certImpl.getSubjectX500Principal());
}
CRLDistributionPointsExtension ext =
certImpl.getCRLDistributionPointsExtension();
List points = null;
if (ext == null) {
// assume a DP with reasons and CRLIssuer fields omitted
// and a DP name of the cert issuer.
// TODO add issuerAltName too
X500Name certIssuer = (X500Name)certImpl.getIssuerDN();
DistributionPoint point = new DistributionPoint
(new GeneralNames().add(new GeneralName(certIssuer)),
null, null);
points = Collections.singletonList(point);
} else {
points = (List)ext.get(
CRLDistributionPointsExtension.POINTS);
}
Set results = new HashSet();
for (Iterator t = points.iterator();
t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) {
DistributionPoint point = t.next();
for (X509CRL crl : crls) {
if (DistributionPointFetcher.verifyCRL(certImpl, point, crl,
reasonsMask, signFlag, prevKey, mSigProvider,
trustAnchors, mStores, mParams.getDate())) {
results.add(crl);
}
}
}
return results;
} catch (Exception e) {
if (debug != null) {
debug.println("Exception while verifying CRL: "+e.getMessage());
e.printStackTrace();
}
return Collections.emptySet();
}
}
}