| package org.bouncycastle.jce.provider; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.security.GeneralSecurityException; |
| import java.security.InvalidAlgorithmParameterException; |
| import java.security.PublicKey; |
| import java.security.cert.CertPath; |
| import java.security.cert.CertPathParameters; |
| import java.security.cert.CertPathValidatorException; |
| import java.security.cert.CertPathValidatorResult; |
| import java.security.cert.CertPathValidatorSpi; |
| import java.security.cert.CertificateExpiredException; |
| import java.security.cert.CertificateNotYetValidException; |
| import java.security.cert.PKIXCertPathChecker; |
| import java.security.cert.PKIXCertPathValidatorResult; |
| import java.security.cert.PKIXParameters; |
| import java.security.cert.TrustAnchor; |
| import java.security.cert.X509CRL; |
| import java.security.cert.X509CRLEntry; |
| import java.security.cert.X509CRLSelector; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.security.auth.x500.X500Principal; |
| |
| import org.bouncycastle.asn1.ASN1InputStream; |
| import org.bouncycastle.asn1.ASN1OctetString; |
| import org.bouncycastle.asn1.ASN1Sequence; |
| import org.bouncycastle.asn1.ASN1TaggedObject; |
| import org.bouncycastle.asn1.DEREncodable; |
| import org.bouncycastle.asn1.DEREnumerated; |
| import org.bouncycastle.asn1.DERIA5String; |
| import org.bouncycastle.asn1.DERInteger; |
| import org.bouncycastle.asn1.DERObject; |
| import org.bouncycastle.asn1.DERObjectIdentifier; |
| import org.bouncycastle.asn1.x509.AlgorithmIdentifier; |
| import org.bouncycastle.asn1.x509.BasicConstraints; |
| import org.bouncycastle.asn1.x509.GeneralName; |
| import org.bouncycastle.asn1.x509.GeneralSubtree; |
| import org.bouncycastle.asn1.x509.IssuingDistributionPoint; |
| import org.bouncycastle.asn1.x509.NameConstraints; |
| import org.bouncycastle.asn1.x509.PolicyInformation; |
| import org.bouncycastle.asn1.x509.X509Extensions; |
| |
| /** |
| * CertPathValidatorSpi implemenation for X.509 Certificate validation ala rfc 3280<br /> |
| **/ |
| public class PKIXCertPathValidatorSpi extends CertPathValidatorSpi |
| { |
| private static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId(); |
| private static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId(); |
| private static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId(); |
| private static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId(); |
| private static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId(); |
| private static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId(); |
| private static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId(); |
| private static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId(); |
| private static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId(); |
| private static final String KEY_USAGE = X509Extensions.KeyUsage.getId(); |
| |
| private static final String CRL_NUMBER = X509Extensions.CRLNumber.getId(); |
| |
| private static final String ANY_POLICY = "2.5.29.32.0"; |
| |
| |
| /* |
| * key usage bits |
| */ |
| private static final int KEY_CERT_SIGN = 5; |
| private static final int CRL_SIGN = 6; |
| |
| private static final String[] crlReasons = new String[] { |
| "unspecified", |
| "keyCompromise", |
| "cACompromise", |
| "affiliationChanged", |
| "superseded", |
| "cessationOfOperation", |
| "certificateHold", |
| "unknown", |
| "removeFromCRL", |
| "privilegeWithdrawn", |
| "aACompromise" }; |
| |
| |
| public CertPathValidatorResult engineValidate( |
| CertPath certPath, |
| CertPathParameters params) |
| throws CertPathValidatorException, InvalidAlgorithmParameterException |
| { |
| if (!(params instanceof PKIXParameters)) |
| { |
| throw new InvalidAlgorithmParameterException("params must be a PKIXParameters instance"); |
| } |
| |
| PKIXParameters paramsPKIX = (PKIXParameters)params; |
| if (paramsPKIX.getTrustAnchors() == null) |
| { |
| throw new InvalidAlgorithmParameterException("trustAnchors is null, this is not allowed for path validation"); |
| } |
| |
| // |
| // 6.1.1 - inputs |
| // |
| |
| // |
| // (a) |
| // |
| List certs = certPath.getCertificates(); |
| int n = certs.size(); |
| |
| if (certs.isEmpty()) |
| { |
| throw new CertPathValidatorException("CertPath is empty", null, certPath, 0); |
| } |
| |
| // |
| // (b) |
| // |
| Date validDate = CertPathValidatorUtilities.getValidDate(paramsPKIX); |
| |
| // |
| // (c) |
| // |
| Set userInitialPolicySet = paramsPKIX.getInitialPolicies(); |
| |
| // |
| // (d) |
| // |
| X509Certificate lastCert = (X509Certificate)certs.get(certs.size() - 1); |
| |
| // BEGIN android-changed |
| TrustAnchor trust = CertPathValidatorUtilities.findTrustAnchor(lastCert, |
| certPath, certs.size() - 1, paramsPKIX); |
| // END android-changed |
| |
| if (trust == null) |
| { |
| throw new CertPathValidatorException("TrustAnchor for CertPath not found.", null, certPath, -1); |
| } |
| |
| // |
| // (e), (f), (g) are part of the paramsPKIX object. |
| // |
| |
| Iterator certIter; |
| int index = 0; |
| int i; |
| |
| // |
| // 6.1.2 - setup |
| // |
| |
| // |
| // (a) |
| // |
| List [] policyNodes = new ArrayList[n + 1]; |
| for (int j = 0; j < policyNodes.length; j++) |
| { |
| policyNodes[j] = new ArrayList(); |
| } |
| |
| Set policySet = new HashSet(); |
| |
| policySet.add(ANY_POLICY); |
| |
| PKIXPolicyNode validPolicyTree = new PKIXPolicyNode(new ArrayList(), 0, policySet, null, new HashSet(), ANY_POLICY, false); |
| |
| policyNodes[0].add(validPolicyTree); |
| |
| // |
| // (b) |
| // |
| Set permittedSubtreesDN = new HashSet(); |
| Set permittedSubtreesEmail = new HashSet(); |
| Set permittedSubtreesIP = new HashSet(); |
| |
| // |
| // (c) |
| // |
| Set excludedSubtreesDN = new HashSet(); |
| Set excludedSubtreesEmail = new HashSet(); |
| Set excludedSubtreesIP = new HashSet(); |
| |
| // |
| // (d) |
| // |
| int explicitPolicy; |
| Set acceptablePolicies = null; |
| |
| if (paramsPKIX.isExplicitPolicyRequired()) |
| { |
| explicitPolicy = 0; |
| } |
| else |
| { |
| explicitPolicy = n + 1; |
| } |
| |
| // |
| // (e) |
| // |
| int inhibitAnyPolicy; |
| |
| if (paramsPKIX.isAnyPolicyInhibited()) |
| { |
| inhibitAnyPolicy = 0; |
| } |
| else |
| { |
| inhibitAnyPolicy = n + 1; |
| } |
| |
| // |
| // (f) |
| // |
| int policyMapping; |
| |
| if (paramsPKIX.isPolicyMappingInhibited()) |
| { |
| policyMapping = 0; |
| } |
| else |
| { |
| policyMapping = n + 1; |
| } |
| |
| // |
| // (g), (h), (i), (j) |
| // |
| PublicKey workingPublicKey; |
| X500Principal workingIssuerName; |
| |
| X509Certificate sign = trust.getTrustedCert(); |
| boolean trustAnchorInChain = false; |
| try |
| { |
| if (sign != null) |
| { |
| workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign); |
| workingPublicKey = sign.getPublicKey(); |
| |
| // There is similar code in CertPathValidatorUtilities. |
| try { |
| byte[] trustBytes = sign.getEncoded(); |
| byte[] certBytes = lastCert.getEncoded(); |
| trustAnchorInChain = Arrays.equals(trustBytes, certBytes); |
| } catch(Exception e) { |
| // ignore, continue with trustAnchorInChain being false |
| } |
| } |
| else |
| { |
| workingIssuerName = new X500Principal(trust.getCAName()); |
| workingPublicKey = trust.getCAPublicKey(); |
| } |
| } |
| catch (IllegalArgumentException ex) |
| { |
| throw new CertPathValidatorException("TrustAnchor subjectDN: " + ex.toString()); |
| } |
| |
| AlgorithmIdentifier workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey); |
| DERObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.getObjectId(); |
| DEREncodable workingPublicKeyParameters = workingAlgId.getParameters(); |
| |
| // |
| // (k) |
| // |
| int maxPathLength = n; |
| |
| // |
| // 6.1.3 |
| // |
| Iterator tmpIter; |
| int tmpInt; |
| |
| if (paramsPKIX.getTargetCertConstraints() != null |
| && !paramsPKIX.getTargetCertConstraints().match((X509Certificate)certs.get(0))) |
| { |
| throw new CertPathValidatorException("target certificate in certpath does not match targetcertconstraints", null, certPath, 0); |
| } |
| |
| |
| // |
| // initialise CertPathChecker's |
| // |
| List pathCheckers = paramsPKIX.getCertPathCheckers(); |
| certIter = pathCheckers.iterator(); |
| while (certIter.hasNext()) |
| { |
| ((PKIXCertPathChecker)certIter.next()).init(false); |
| } |
| |
| X509Certificate cert = null; |
| |
| for (index = certs.size() - 1; index >= 0 ; index--) |
| { |
| try |
| { |
| // |
| // i as defined in the algorithm description |
| // |
| i = n - index; |
| |
| // |
| // set certificate to be checked in this round |
| // sign and workingPublicKey and workingIssuerName are set |
| // at the end of the for loop and initialied the |
| // first time from the TrustAnchor |
| // |
| cert = (X509Certificate)certs.get(index); |
| |
| // |
| // 6.1.3 |
| // |
| |
| // |
| // (a) verify |
| // |
| try |
| { |
| // (a) (1) |
| // |
| if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate |
| { |
| cert.verify(workingPublicKey, "BC"); |
| } |
| } |
| catch (GeneralSecurityException e) |
| { |
| throw new CertPathValidatorException("Could not validate certificate signature.", e, certPath, index); |
| } |
| |
| try |
| { |
| // (a) (2) |
| // |
| cert.checkValidity(validDate); |
| } |
| catch (CertificateExpiredException e) |
| { |
| throw new CertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index); |
| } |
| catch (CertificateNotYetValidException e) |
| { |
| throw new CertPathValidatorException("Could not validate certificate: " + e.getMessage(), e, certPath, index); |
| } |
| |
| // |
| // (a) (3) |
| // |
| if (paramsPKIX.isRevocationEnabled()) |
| { |
| checkCRLs(paramsPKIX, cert, validDate, sign, workingPublicKey); |
| } |
| |
| // |
| // (a) (4) name chaining |
| // |
| if (!CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).equals(workingIssuerName)) |
| { |
| throw new CertPathValidatorException( |
| "IssuerName(" + CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert) + |
| ") does not match SubjectName(" + workingIssuerName + |
| ") of signing certificate", null, certPath, index); |
| } |
| |
| // |
| // (b), (c) permitted and excluded subtree checking. |
| // |
| if (!(CertPathValidatorUtilities.isSelfIssued(cert) && (i < n))) |
| { |
| X500Principal principal = CertPathValidatorUtilities.getSubjectPrincipal(cert); |
| ASN1InputStream aIn = new ASN1InputStream(principal.getEncoded()); |
| ASN1Sequence dns; |
| |
| try |
| { |
| dns = (ASN1Sequence)aIn.readObject(); |
| } |
| catch (IOException e) |
| { |
| throw new CertPathValidatorException("exception extracting subject name when checking subtrees"); |
| } |
| |
| CertPathValidatorUtilities.checkPermittedDN(permittedSubtreesDN, dns); |
| |
| CertPathValidatorUtilities.checkExcludedDN(excludedSubtreesDN, dns); |
| |
| ASN1Sequence altName = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, SUBJECT_ALTERNATIVE_NAME); |
| if (altName != null) |
| { |
| for (int j = 0; j < altName.size(); j++) |
| { |
| ASN1TaggedObject o = (ASN1TaggedObject)altName.getObjectAt(j); |
| |
| switch(o.getTagNo()) |
| { |
| case 1: |
| String email = DERIA5String.getInstance(o, true).getString(); |
| |
| CertPathValidatorUtilities.checkPermittedEmail(permittedSubtreesEmail, email); |
| CertPathValidatorUtilities.checkExcludedEmail(excludedSubtreesEmail, email); |
| break; |
| case 4: |
| ASN1Sequence altDN = ASN1Sequence.getInstance(o, true); |
| |
| CertPathValidatorUtilities.checkPermittedDN(permittedSubtreesDN, altDN); |
| CertPathValidatorUtilities.checkExcludedDN(excludedSubtreesDN, altDN); |
| break; |
| case 7: |
| byte[] ip = ASN1OctetString.getInstance(o, true).getOctets(); |
| |
| CertPathValidatorUtilities.checkPermittedIP(permittedSubtreesIP, ip); |
| CertPathValidatorUtilities.checkExcludedIP(excludedSubtreesIP, ip); |
| } |
| } |
| } |
| } |
| |
| // |
| // (d) policy Information checking against initial policy and |
| // policy mapping |
| // |
| ASN1Sequence certPolicies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, CERTIFICATE_POLICIES); |
| if (certPolicies != null && validPolicyTree != null) |
| { |
| // |
| // (d) (1) |
| // |
| Enumeration e = certPolicies.getObjects(); |
| Set pols = new HashSet(); |
| |
| while (e.hasMoreElements()) |
| { |
| PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement()); |
| DERObjectIdentifier pOid = pInfo.getPolicyIdentifier(); |
| |
| pols.add(pOid.getId()); |
| |
| if (!ANY_POLICY.equals(pOid.getId())) |
| { |
| Set pq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers()); |
| |
| boolean match = CertPathValidatorUtilities.processCertD1i(i, policyNodes, pOid, pq); |
| |
| if (!match) |
| { |
| CertPathValidatorUtilities.processCertD1ii(i, policyNodes, pOid, pq); |
| } |
| } |
| } |
| |
| if (acceptablePolicies == null || acceptablePolicies.contains(ANY_POLICY)) |
| { |
| acceptablePolicies = pols; |
| } |
| else |
| { |
| Iterator it = acceptablePolicies.iterator(); |
| Set t1 = new HashSet(); |
| |
| while (it.hasNext()) |
| { |
| Object o = it.next(); |
| |
| if (pols.contains(o)) |
| { |
| t1.add(o); |
| } |
| } |
| |
| acceptablePolicies = t1; |
| } |
| |
| // |
| // (d) (2) |
| // |
| if ((inhibitAnyPolicy > 0) || ((i < n) && CertPathValidatorUtilities.isSelfIssued(cert))) |
| { |
| e = certPolicies.getObjects(); |
| |
| while (e.hasMoreElements()) |
| { |
| PolicyInformation pInfo = PolicyInformation.getInstance(e.nextElement()); |
| |
| if (ANY_POLICY.equals(pInfo.getPolicyIdentifier().getId())) |
| { |
| Set _apq = CertPathValidatorUtilities.getQualifierSet(pInfo.getPolicyQualifiers()); |
| List _nodes = policyNodes[i - 1]; |
| |
| for (int k = 0; k < _nodes.size(); k++) |
| { |
| PKIXPolicyNode _node = (PKIXPolicyNode)_nodes.get(k); |
| |
| Iterator _policySetIter = _node.getExpectedPolicies().iterator(); |
| while (_policySetIter.hasNext()) |
| { |
| Object _tmp = _policySetIter.next(); |
| |
| String _policy; |
| if (_tmp instanceof String) |
| { |
| _policy = (String)_tmp; |
| } |
| else if (_tmp instanceof DERObjectIdentifier) |
| { |
| _policy = ((DERObjectIdentifier)_tmp).getId(); |
| } |
| else |
| { |
| continue; |
| } |
| |
| boolean _found = false; |
| Iterator _childrenIter = _node.getChildren(); |
| |
| while (_childrenIter.hasNext()) |
| { |
| PKIXPolicyNode _child = (PKIXPolicyNode)_childrenIter.next(); |
| |
| if (_policy.equals(_child.getValidPolicy())) |
| { |
| _found = true; |
| } |
| } |
| |
| if (!_found) |
| { |
| Set _newChildExpectedPolicies = new HashSet(); |
| _newChildExpectedPolicies.add(_policy); |
| |
| PKIXPolicyNode _newChild = new PKIXPolicyNode(new ArrayList(), |
| i, |
| _newChildExpectedPolicies, |
| _node, |
| _apq, |
| _policy, |
| false); |
| _node.addChild(_newChild); |
| policyNodes[i].add(_newChild); |
| } |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| // |
| // (d) (3) |
| // |
| for (int j = (i - 1); j >= 0; j--) |
| { |
| List nodes = policyNodes[j]; |
| |
| for (int k = 0; k < nodes.size(); k++) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); |
| if (!node.hasChildren()) |
| { |
| validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node); |
| if (validPolicyTree == null) |
| { |
| break; |
| } |
| } |
| } |
| } |
| |
| // |
| // d (4) |
| // |
| Set criticalExtensionOids = cert.getCriticalExtensionOIDs(); |
| |
| if (criticalExtensionOids != null) |
| { |
| boolean critical = criticalExtensionOids.contains(CERTIFICATE_POLICIES); |
| |
| List nodes = policyNodes[i]; |
| for (int j = 0; j < nodes.size(); j++) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(j); |
| node.setCritical(critical); |
| } |
| } |
| } |
| |
| // |
| // (e) |
| // |
| if (certPolicies == null) |
| { |
| validPolicyTree = null; |
| } |
| |
| // |
| // (f) |
| // |
| if (explicitPolicy <= 0 && validPolicyTree == null) |
| { |
| throw new CertPathValidatorException("No valid policy tree found when one expected."); |
| } |
| |
| // |
| // 6.1.4 |
| // |
| |
| if (i != n) // if not at the end-entity certificate |
| { |
| if (cert != null && cert.getVersion() == 1) |
| { |
| if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate |
| { |
| throw new CertPathValidatorException( |
| "Version 1 certs can't be used as intermediate certificates"); |
| } |
| } |
| |
| // |
| // |
| // (a) check the policy mappings |
| // |
| DERObject pm = CertPathValidatorUtilities.getExtensionValue(cert, POLICY_MAPPINGS); |
| if (pm != null) |
| { |
| ASN1Sequence mappings = (ASN1Sequence)pm; |
| |
| for (int j = 0; j < mappings.size(); j++) |
| { |
| ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j); |
| |
| DERObjectIdentifier issuerDomainPolicy = (DERObjectIdentifier)mapping.getObjectAt(0); |
| DERObjectIdentifier subjectDomainPolicy = (DERObjectIdentifier)mapping.getObjectAt(1); |
| |
| if (ANY_POLICY.equals(issuerDomainPolicy.getId())) |
| { |
| |
| throw new CertPathValidatorException("IssuerDomainPolicy is anyPolicy"); |
| } |
| |
| if (ANY_POLICY.equals(subjectDomainPolicy.getId())) |
| { |
| |
| throw new CertPathValidatorException("SubjectDomainPolicy is anyPolicy"); |
| } |
| } |
| } |
| |
| // (b) |
| // |
| if (pm != null) |
| { |
| ASN1Sequence mappings = (ASN1Sequence)pm; |
| Map m_idp = new HashMap(); |
| Set s_idp = new HashSet(); |
| |
| for (int j = 0; j < mappings.size(); j++) |
| { |
| ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j); |
| String id_p = ((DERObjectIdentifier)mapping.getObjectAt(0)).getId(); |
| String sd_p = ((DERObjectIdentifier)mapping.getObjectAt(1)).getId(); |
| Set tmp; |
| |
| if (!m_idp.containsKey(id_p)) |
| { |
| tmp = new HashSet(); |
| tmp.add(sd_p); |
| m_idp.put(id_p, tmp); |
| s_idp.add(id_p); |
| } |
| else |
| { |
| tmp = (Set)m_idp.get(id_p); |
| tmp.add(sd_p); |
| } |
| } |
| |
| Iterator it_idp = s_idp.iterator(); |
| while (it_idp.hasNext()) |
| { |
| String id_p = (String)it_idp.next(); |
| |
| // |
| // (1) |
| // |
| if (policyMapping > 0) |
| { |
| boolean idp_found = false; |
| Iterator nodes_i = policyNodes[i].iterator(); |
| while (nodes_i.hasNext()) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); |
| if (node.getValidPolicy().equals(id_p)) |
| { |
| idp_found = true; |
| node.expectedPolicies = (Set)m_idp.get(id_p); |
| break; |
| } |
| } |
| |
| if (!idp_found) |
| { |
| nodes_i = policyNodes[i].iterator(); |
| while (nodes_i.hasNext()) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); |
| if (ANY_POLICY.equals(node.getValidPolicy())) |
| { |
| Set pq = null; |
| ASN1Sequence policies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue( |
| cert, CERTIFICATE_POLICIES); |
| Enumeration e = policies.getObjects(); |
| while (e.hasMoreElements()) |
| { |
| PolicyInformation pinfo = PolicyInformation.getInstance(e.nextElement()); |
| if (ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId())) |
| { |
| pq = CertPathValidatorUtilities.getQualifierSet(pinfo.getPolicyQualifiers()); |
| break; |
| } |
| } |
| boolean ci = false; |
| if (cert.getCriticalExtensionOIDs() != null) |
| { |
| ci = cert.getCriticalExtensionOIDs().contains(CERTIFICATE_POLICIES); |
| } |
| |
| PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); |
| if (ANY_POLICY.equals(p_node.getValidPolicy())) |
| { |
| PKIXPolicyNode c_node = new PKIXPolicyNode( |
| new ArrayList(), i, |
| (Set)m_idp.get(id_p), |
| p_node, pq, id_p, ci); |
| p_node.addChild(c_node); |
| policyNodes[i].add(c_node); |
| } |
| break; |
| } |
| } |
| } |
| |
| // |
| // (2) |
| // |
| } |
| else if (policyMapping <= 0) |
| { |
| Iterator nodes_i = policyNodes[i].iterator(); |
| while (nodes_i.hasNext()) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); |
| if (node.getValidPolicy().equals(id_p)) |
| { |
| PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); |
| p_node.removeChild(node); |
| nodes_i.remove(); |
| for (int k = (i - 1); k >= 0; k--) |
| { |
| List nodes = policyNodes[k]; |
| for (int l = 0; l < nodes.size(); l++) |
| { |
| PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l); |
| if (!node2.hasChildren()) |
| { |
| validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node2); |
| if (validPolicyTree == null) |
| { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // |
| // (g) handle the name constraints extension |
| // |
| ASN1Sequence ncSeq = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, NAME_CONSTRAINTS); |
| if (ncSeq != null) |
| { |
| NameConstraints nc = new NameConstraints(ncSeq); |
| |
| // |
| // (g) (1) permitted subtrees |
| // |
| ASN1Sequence permitted = nc.getPermittedSubtrees(); |
| if (permitted != null) |
| { |
| Enumeration e = permitted.getObjects(); |
| while (e.hasMoreElements()) |
| { |
| GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement()); |
| GeneralName base = subtree.getBase(); |
| |
| switch(base.getTagNo()) |
| { |
| case 1: |
| permittedSubtreesEmail = CertPathValidatorUtilities.intersectEmail(permittedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString()); |
| break; |
| case 4: |
| permittedSubtreesDN = CertPathValidatorUtilities.intersectDN(permittedSubtreesDN, (ASN1Sequence)base.getName()); |
| break; |
| case 7: |
| permittedSubtreesIP = CertPathValidatorUtilities.intersectIP(permittedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets()); |
| break; |
| } |
| } |
| } |
| |
| // |
| // (g) (2) excluded subtrees |
| // |
| ASN1Sequence excluded = nc.getExcludedSubtrees(); |
| if (excluded != null) |
| { |
| Enumeration e = excluded.getObjects(); |
| while (e.hasMoreElements()) |
| { |
| GeneralSubtree subtree = GeneralSubtree.getInstance(e.nextElement()); |
| GeneralName base = subtree.getBase(); |
| |
| switch(base.getTagNo()) |
| { |
| case 1: |
| excludedSubtreesEmail = CertPathValidatorUtilities.unionEmail(excludedSubtreesEmail, DERIA5String.getInstance(base.getName()).getString()); |
| break; |
| case 4: |
| excludedSubtreesDN = CertPathValidatorUtilities.unionDN(excludedSubtreesDN, (ASN1Sequence)base.getName()); |
| break; |
| case 7: |
| excludedSubtreesIP = CertPathValidatorUtilities.unionIP(excludedSubtreesIP, ASN1OctetString.getInstance(base.getName()).getOctets()); |
| break; |
| } |
| } |
| } |
| } |
| |
| // |
| // (h) |
| // |
| if (!CertPathValidatorUtilities.isSelfIssued(cert)) |
| { |
| // |
| // (1) |
| // |
| if (explicitPolicy != 0) |
| { |
| explicitPolicy--; |
| } |
| |
| // |
| // (2) |
| // |
| if (policyMapping != 0) |
| { |
| policyMapping--; |
| } |
| |
| // |
| // (3) |
| // |
| if (inhibitAnyPolicy != 0) |
| { |
| inhibitAnyPolicy--; |
| } |
| } |
| |
| // |
| // (i) |
| // |
| ASN1Sequence pc = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, POLICY_CONSTRAINTS); |
| |
| if (pc != null) |
| { |
| Enumeration policyConstraints = pc.getObjects(); |
| |
| while (policyConstraints.hasMoreElements()) |
| { |
| ASN1TaggedObject constraint = (ASN1TaggedObject)policyConstraints.nextElement(); |
| switch (constraint.getTagNo()) |
| { |
| case 0: |
| tmpInt = DERInteger.getInstance(constraint).getValue().intValue(); |
| if (tmpInt < explicitPolicy) |
| { |
| explicitPolicy = tmpInt; |
| } |
| break; |
| case 1: |
| tmpInt = DERInteger.getInstance(constraint).getValue().intValue(); |
| if (tmpInt < policyMapping) |
| { |
| policyMapping = tmpInt; |
| } |
| break; |
| } |
| } |
| } |
| |
| // |
| // (j) |
| // |
| DERInteger iap = (DERInteger)CertPathValidatorUtilities.getExtensionValue(cert, INHIBIT_ANY_POLICY); |
| |
| if (iap != null) |
| { |
| int _inhibitAnyPolicy = iap.getValue().intValue(); |
| |
| if (_inhibitAnyPolicy < inhibitAnyPolicy) |
| { |
| inhibitAnyPolicy = _inhibitAnyPolicy; |
| } |
| } |
| |
| // |
| // (k) |
| // |
| BasicConstraints bc = BasicConstraints.getInstance( |
| CertPathValidatorUtilities.getExtensionValue(cert, BASIC_CONSTRAINTS)); |
| if (bc != null) |
| { |
| if (!(bc.isCA())) |
| { |
| throw new CertPathValidatorException("Not a CA certificate"); |
| } |
| } |
| else |
| { |
| if (!(i == 1 && trustAnchorInChain)) // if not at the root certificate |
| { |
| throw new CertPathValidatorException("Intermediate certificate lacks BasicConstraints"); |
| } |
| } |
| |
| // |
| // (l) |
| // |
| if (!CertPathValidatorUtilities.isSelfIssued(cert)) |
| { |
| if (maxPathLength <= 0) |
| { |
| throw new CertPathValidatorException("Max path length not greater than zero"); |
| } |
| |
| maxPathLength--; |
| } |
| |
| // |
| // (m) |
| // |
| if (bc != null) |
| { |
| BigInteger _pathLengthConstraint = bc.getPathLenConstraint(); |
| |
| if (_pathLengthConstraint != null) |
| { |
| int _plc = _pathLengthConstraint.intValue(); |
| |
| if (_plc < maxPathLength) |
| { |
| maxPathLength = _plc; |
| } |
| } |
| } |
| |
| // |
| // (n) |
| // |
| boolean[] _usage = cert.getKeyUsage(); |
| |
| if ((_usage != null) && !_usage[5]) |
| { |
| throw new CertPathValidatorException( |
| "Issuer certificate keyusage extension is critical an does not permit key signing.\n", |
| null, certPath, index); |
| } |
| |
| // |
| // (o) |
| // |
| if (cert.getCriticalExtensionOIDs() != null) |
| { |
| Set criticalExtensions = new HashSet(cert.getCriticalExtensionOIDs()); |
| // these extensions are handle by the algorithem |
| criticalExtensions.remove(KEY_USAGE); |
| criticalExtensions.remove(CERTIFICATE_POLICIES); |
| criticalExtensions.remove(POLICY_MAPPINGS); |
| criticalExtensions.remove(INHIBIT_ANY_POLICY); |
| criticalExtensions.remove(ISSUING_DISTRIBUTION_POINT); |
| criticalExtensions.remove(DELTA_CRL_INDICATOR); |
| criticalExtensions.remove(POLICY_CONSTRAINTS); |
| criticalExtensions.remove(BASIC_CONSTRAINTS); |
| criticalExtensions.remove(SUBJECT_ALTERNATIVE_NAME); |
| criticalExtensions.remove(NAME_CONSTRAINTS); |
| |
| tmpIter = pathCheckers.iterator(); |
| while (tmpIter.hasNext()) |
| { |
| try |
| { |
| ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions); |
| } |
| catch (CertPathValidatorException e) |
| { |
| throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index); |
| } |
| } |
| if (!criticalExtensions.isEmpty()) |
| { |
| throw new CertPathValidatorException( |
| "Certificate has unsupported critical extension", null, certPath, index); |
| } |
| } |
| } |
| |
| // set signing certificate for next round |
| sign = cert; |
| workingPublicKey = sign.getPublicKey(); |
| try |
| { |
| workingIssuerName = CertPathValidatorUtilities.getSubjectPrincipal(sign); |
| } |
| catch (IllegalArgumentException ex) |
| { |
| throw new CertPathValidatorException(sign.getSubjectDN().getName() + " :" + ex.toString()); |
| } |
| workingAlgId = CertPathValidatorUtilities.getAlgorithmIdentifier(workingPublicKey); |
| workingPublicKeyAlgorithm = workingAlgId.getObjectId(); |
| workingPublicKeyParameters = workingAlgId.getParameters(); |
| } |
| catch (AnnotatedException e) |
| { |
| throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, index); |
| } |
| } |
| |
| // |
| // 6.1.5 Wrap-up procedure |
| // |
| |
| // |
| // (a) |
| // |
| if (!CertPathValidatorUtilities.isSelfIssued(cert) && (explicitPolicy != 0)) |
| { |
| explicitPolicy--; |
| } |
| |
| // |
| // (b) |
| // |
| try |
| { |
| ASN1Sequence pc = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, POLICY_CONSTRAINTS); |
| if (pc != null) |
| { |
| Enumeration policyConstraints = pc.getObjects(); |
| |
| while (policyConstraints.hasMoreElements()) |
| { |
| ASN1TaggedObject constraint = (ASN1TaggedObject)policyConstraints.nextElement(); |
| switch (constraint.getTagNo()) |
| { |
| case 0: |
| tmpInt = DERInteger.getInstance(constraint).getValue().intValue(); |
| if (tmpInt == 0) |
| { |
| explicitPolicy = 0; |
| } |
| break; |
| } |
| } |
| } |
| } |
| catch (AnnotatedException e) |
| { |
| throw new CertPathValidatorException(e.getMessage(), e.getUnderlyingException(), certPath, index); |
| } |
| |
| // |
| // (c) (d) and (e) are already done |
| // |
| |
| // |
| // (f) |
| // |
| Set criticalExtensions = cert.getCriticalExtensionOIDs(); |
| |
| if (criticalExtensions != null) |
| { |
| criticalExtensions = new HashSet(criticalExtensions); |
| // these extensions are handle by the algorithm |
| criticalExtensions.remove(KEY_USAGE); |
| criticalExtensions.remove(CERTIFICATE_POLICIES); |
| criticalExtensions.remove(POLICY_MAPPINGS); |
| criticalExtensions.remove(INHIBIT_ANY_POLICY); |
| criticalExtensions.remove(ISSUING_DISTRIBUTION_POINT); |
| criticalExtensions.remove(DELTA_CRL_INDICATOR); |
| criticalExtensions.remove(POLICY_CONSTRAINTS); |
| criticalExtensions.remove(BASIC_CONSTRAINTS); |
| criticalExtensions.remove(SUBJECT_ALTERNATIVE_NAME); |
| criticalExtensions.remove(NAME_CONSTRAINTS); |
| } |
| else |
| { |
| criticalExtensions = new HashSet(); |
| } |
| |
| tmpIter = pathCheckers.iterator(); |
| while (tmpIter.hasNext()) |
| { |
| try |
| { |
| ((PKIXCertPathChecker)tmpIter.next()).check(cert, criticalExtensions); |
| } |
| catch (CertPathValidatorException e) |
| { |
| throw new CertPathValidatorException(e.getMessage(), e.getCause(), certPath, index); |
| } |
| } |
| |
| if (!criticalExtensions.isEmpty()) |
| { |
| throw new CertPathValidatorException( |
| "Certificate has unsupported critical extension", null, certPath, index); |
| } |
| |
| // |
| // (g) |
| // |
| PKIXPolicyNode intersection; |
| |
| |
| // |
| // (g) (i) |
| // |
| if (validPolicyTree == null) |
| { |
| if (paramsPKIX.isExplicitPolicyRequired()) |
| { |
| throw new CertPathValidatorException("Explicit policy requested but none available."); |
| } |
| intersection = null; |
| } |
| else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) (ii) |
| { |
| if (paramsPKIX.isExplicitPolicyRequired()) |
| { |
| if (acceptablePolicies.isEmpty()) |
| { |
| throw new CertPathValidatorException("Explicit policy requested but none available."); |
| } |
| else |
| { |
| Set _validPolicyNodeSet = new HashSet(); |
| |
| for (int j = 0; j < policyNodes.length; j++) |
| { |
| List _nodeDepth = policyNodes[j]; |
| |
| for (int k = 0; k < _nodeDepth.size(); k++) |
| { |
| PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k); |
| |
| if (ANY_POLICY.equals(_node.getValidPolicy())) |
| { |
| Iterator _iter = _node.getChildren(); |
| while (_iter.hasNext()) |
| { |
| _validPolicyNodeSet.add(_iter.next()); |
| } |
| } |
| } |
| } |
| |
| Iterator _vpnsIter = _validPolicyNodeSet.iterator(); |
| while (_vpnsIter.hasNext()) |
| { |
| PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next(); |
| String _validPolicy = _node.getValidPolicy(); |
| |
| if (!acceptablePolicies.contains(_validPolicy)) |
| { |
| //validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, _node); |
| } |
| } |
| if (validPolicyTree != null) |
| { |
| for (int j = (n - 1); j >= 0; j--) |
| { |
| List nodes = policyNodes[j]; |
| |
| for (int k = 0; k < nodes.size(); k++) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); |
| if (!node.hasChildren()) |
| { |
| validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| intersection = validPolicyTree; |
| } |
| else |
| { |
| // |
| // (g) (iii) |
| // |
| // This implementation is not exactly same as the one described in RFC3280. |
| // However, as far as the validation result is concerned, both produce |
| // adequate result. The only difference is whether AnyPolicy is remain |
| // in the policy tree or not. |
| // |
| // (g) (iii) 1 |
| // |
| Set _validPolicyNodeSet = new HashSet(); |
| |
| for (int j = 0; j < policyNodes.length; j++) |
| { |
| List _nodeDepth = policyNodes[j]; |
| |
| for (int k = 0; k < _nodeDepth.size(); k++) |
| { |
| PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k); |
| |
| if (ANY_POLICY.equals(_node.getValidPolicy())) |
| { |
| Iterator _iter = _node.getChildren(); |
| while (_iter.hasNext()) |
| { |
| PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next(); |
| if (!ANY_POLICY.equals(_c_node.getValidPolicy())) |
| { |
| _validPolicyNodeSet.add(_c_node); |
| } |
| } |
| } |
| } |
| } |
| |
| // |
| // (g) (iii) 2 |
| // |
| Iterator _vpnsIter = _validPolicyNodeSet.iterator(); |
| while (_vpnsIter.hasNext()) |
| { |
| PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next(); |
| String _validPolicy = _node.getValidPolicy(); |
| |
| if (!userInitialPolicySet.contains(_validPolicy)) |
| { |
| validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, _node); |
| } |
| } |
| |
| // |
| // (g) (iii) 4 |
| // |
| if (validPolicyTree != null) |
| { |
| for (int j = (n - 1); j >= 0; j--) |
| { |
| List nodes = policyNodes[j]; |
| |
| for (int k = 0; k < nodes.size(); k++) |
| { |
| PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); |
| if (!node.hasChildren()) |
| { |
| validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, node); |
| } |
| } |
| } |
| } |
| |
| intersection = validPolicyTree; |
| } |
| |
| if ((explicitPolicy > 0) || (intersection != null)) |
| { |
| return new PKIXCertPathValidatorResult(trust, intersection, workingPublicKey); |
| } |
| |
| throw new CertPathValidatorException("Path processing failed on policy.", null, certPath, index); |
| } |
| |
| private void checkCRLs(PKIXParameters paramsPKIX, X509Certificate cert, Date validDate, X509Certificate sign, PublicKey workingPublicKey) |
| throws AnnotatedException |
| { |
| X509CRLSelector crlselect; |
| crlselect = new X509CRLSelector(); |
| |
| try |
| { |
| crlselect.addIssuerName(CertPathValidatorUtilities.getEncodedIssuerPrincipal(cert).getEncoded()); |
| } |
| catch (IOException e) |
| { |
| throw new AnnotatedException("Cannot extract issuer from certificate: " + e, e); |
| } |
| |
| crlselect.setCertificateChecking(cert); |
| |
| Iterator crl_iter = CertPathValidatorUtilities.findCRLs(crlselect, paramsPKIX.getCertStores()).iterator(); |
| boolean validCrlFound = false; |
| X509CRLEntry crl_entry; |
| while (crl_iter.hasNext()) |
| { |
| X509CRL crl = (X509CRL)crl_iter.next(); |
| |
| if (cert.getNotAfter().after(crl.getThisUpdate())) |
| { |
| if (crl.getNextUpdate() == null |
| || validDate.before(crl.getNextUpdate())) |
| { |
| validCrlFound = true; |
| } |
| |
| if (sign != null) |
| { |
| boolean[] keyusage = sign.getKeyUsage(); |
| |
| if (keyusage != null |
| && (keyusage.length < 7 || !keyusage[CRL_SIGN])) |
| { |
| throw new AnnotatedException( |
| "Issuer certificate keyusage extension does not permit crl signing.\n" + sign); |
| } |
| } |
| |
| try |
| { |
| crl.verify(workingPublicKey, "BC"); |
| } |
| catch (Exception e) |
| { |
| throw new AnnotatedException("can't verify CRL: " + e, e); |
| } |
| |
| crl_entry = crl.getRevokedCertificate(cert.getSerialNumber()); |
| if (crl_entry != null |
| && !validDate.before(crl_entry.getRevocationDate())) |
| { |
| String reason = null; |
| |
| if (crl_entry.hasExtensions()) |
| { |
| DEREnumerated reasonCode = DEREnumerated.getInstance(CertPathValidatorUtilities.getExtensionValue(crl_entry, X509Extensions.ReasonCode.getId())); |
| if (reasonCode != null) |
| { |
| reason = crlReasons[reasonCode.getValue().intValue()]; |
| } |
| } |
| |
| String message = "Certificate revocation after " + crl_entry.getRevocationDate(); |
| |
| if (reason != null) |
| { |
| message += ", reason: " + reason; |
| } |
| |
| throw new AnnotatedException(message); |
| } |
| |
| // |
| // check the DeltaCRL indicator, base point and the issuing distribution point |
| // |
| DERObject idp = CertPathValidatorUtilities.getExtensionValue(crl, ISSUING_DISTRIBUTION_POINT); |
| DERObject dci = CertPathValidatorUtilities.getExtensionValue(crl, DELTA_CRL_INDICATOR); |
| |
| if (dci != null) |
| { |
| X509CRLSelector baseSelect = new X509CRLSelector(); |
| |
| try |
| { |
| baseSelect.addIssuerName(CertPathValidatorUtilities.getIssuerPrincipal(crl).getEncoded()); |
| } |
| catch (IOException e) |
| { |
| throw new AnnotatedException("can't extract issuer from certificate: " + e, e); |
| } |
| |
| baseSelect.setMinCRLNumber(((DERInteger)dci).getPositiveValue()); |
| baseSelect.setMaxCRLNumber(((DERInteger)CertPathValidatorUtilities.getExtensionValue(crl, CRL_NUMBER)).getPositiveValue().subtract(BigInteger.valueOf(1))); |
| |
| boolean foundBase = false; |
| Iterator it = CertPathValidatorUtilities.findCRLs(baseSelect, paramsPKIX.getCertStores()).iterator(); |
| while (it.hasNext()) |
| { |
| X509CRL base = (X509CRL)it.next(); |
| |
| DERObject baseIdp = CertPathValidatorUtilities.getExtensionValue(base, ISSUING_DISTRIBUTION_POINT); |
| |
| if (idp == null) |
| { |
| if (baseIdp == null) |
| { |
| foundBase = true; |
| break; |
| } |
| } |
| else |
| { |
| if (idp.equals(baseIdp)) |
| { |
| foundBase = true; |
| break; |
| } |
| } |
| } |
| |
| if (!foundBase) |
| { |
| throw new AnnotatedException("No base CRL for delta CRL"); |
| } |
| } |
| |
| if (idp != null) |
| { |
| IssuingDistributionPoint p = IssuingDistributionPoint.getInstance(idp); |
| BasicConstraints bc = BasicConstraints.getInstance(CertPathValidatorUtilities.getExtensionValue(cert, BASIC_CONSTRAINTS)); |
| |
| if (p.onlyContainsUserCerts() && (bc != null && bc.isCA())) |
| { |
| throw new AnnotatedException("CA Cert CRL only contains user certificates"); |
| } |
| |
| if (p.onlyContainsCACerts() && (bc == null || !bc.isCA())) |
| { |
| throw new AnnotatedException("End CRL only contains CA certificates"); |
| } |
| |
| if (p.onlyContainsAttributeCerts()) |
| { |
| throw new AnnotatedException("onlyContainsAttributeCerts boolean is asserted"); |
| } |
| } |
| } |
| } |
| |
| if (!validCrlFound) |
| { |
| throw new AnnotatedException("no valid CRL found"); |
| } |
| } |
| } |