package org.bouncycastle.jce.provider; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.PublicKey; import java.security.cert.CRL; import java.security.cert.CertPath; import java.security.cert.CertPathValidatorException; import java.security.cert.CertSelector; import java.security.cert.CertStore; import java.security.cert.CertStoreException; import java.security.cert.PKIXParameters; import java.security.cert.PolicyQualifierInfo; import java.security.cert.TrustAnchor; 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.Arrays; import java.util.Collection; import java.util.Date; import java.util.Enumeration; 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.ASN1OutputStream; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERObject; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.PolicyInformation; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509Extensions; public class CertPathValidatorUtilities { protected static final String CERTIFICATE_POLICIES = X509Extensions.CertificatePolicies.getId(); protected static final String BASIC_CONSTRAINTS = X509Extensions.BasicConstraints.getId(); protected static final String POLICY_MAPPINGS = X509Extensions.PolicyMappings.getId(); protected static final String SUBJECT_ALTERNATIVE_NAME = X509Extensions.SubjectAlternativeName.getId(); protected static final String NAME_CONSTRAINTS = X509Extensions.NameConstraints.getId(); protected static final String KEY_USAGE = X509Extensions.KeyUsage.getId(); protected static final String INHIBIT_ANY_POLICY = X509Extensions.InhibitAnyPolicy.getId(); protected static final String ISSUING_DISTRIBUTION_POINT = X509Extensions.IssuingDistributionPoint.getId(); protected static final String DELTA_CRL_INDICATOR = X509Extensions.DeltaCRLIndicator.getId(); protected static final String POLICY_CONSTRAINTS = X509Extensions.PolicyConstraints.getId(); protected static final String ANY_POLICY = "2.5.29.32.0"; protected static final String CRL_NUMBER = X509Extensions.CRLNumber.getId(); /* * key usage bits */ protected static final int KEY_CERT_SIGN = 5; protected static final int CRL_SIGN = 6; protected static final String[] crlReasons = new String[] { "unspecified", "keyCompromise", "cACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "unknown", "removeFromCRL", "privilegeWithdrawn", "aACompromise" }; // BEGIN android-changed /** * Search the given Set of TrustAnchor's for one that is the * issuer of the given X509 certificate. * * @param cert the X509 certificate * @param params with trust anchors * * @return the <code>TrustAnchor</code> object if found or * <code>null</code> if not. * * @exception CertPathValidatorException if a TrustAnchor was * found but the signature verification on the given certificate * has thrown an exception. This Exception can be obtainted with * <code>getCause()</code> method. **/ static final TrustAnchor findTrustAnchor( X509Certificate cert, CertPath certPath, int index, PKIXParameters params) throws CertPathValidatorException { // If we have a trust anchor index, use it. if (params instanceof IndexedPKIXParameters) { IndexedPKIXParameters indexed = (IndexedPKIXParameters) params; return indexed.findTrustAnchor(cert, certPath, index); } Iterator iter = params.getTrustAnchors().iterator(); TrustAnchor found = null; PublicKey trustPublicKey = null; Exception invalidKeyEx = null; X509CertSelector certSelectX509 = new X509CertSelector(); try { certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded()); } catch (IOException ex) { throw new CertPathValidatorException(ex); } byte[] certBytes = null; try { certBytes = cert.getEncoded(); } catch (Exception e) { // ignore, just continue } while (iter.hasNext() && found == null) { found = (TrustAnchor) iter.next(); X509Certificate foundCert = found.getTrustedCert(); if (foundCert != null) { // If the trust anchor is identical to the certificate we're // done. Just return the anchor. // There is similar code in PKIXCertPathValidatorSpi. try { byte[] foundBytes = foundCert.getEncoded(); if (certBytes != null && Arrays.equals(foundBytes, certBytes)) { return found; } } catch (Exception e) { // ignore, continue and verify the certificate } if (certSelectX509.match(foundCert)) { trustPublicKey = foundCert.getPublicKey(); } else { found = null; } } else if (found.getCAName() != null && found.getCAPublicKey() != null) { try { X500Principal certIssuer = getEncodedIssuerPrincipal(cert); X500Principal caName = new X500Principal(found.getCAName()); if (certIssuer.equals(caName)) { trustPublicKey = found.getCAPublicKey(); } else { found = null; } } catch (IllegalArgumentException ex) { found = null; } } else { found = null; } if (trustPublicKey != null) { try { cert.verify(trustPublicKey); } catch (Exception ex) { invalidKeyEx = ex; found = null; } } } if (found == null && invalidKeyEx != null) { throw new CertPathValidatorException("TrustAnchor found but certificate validation failed.", invalidKeyEx, certPath, index); } return found; } // END android-changed protected static X500Principal getEncodedIssuerPrincipal(X509Certificate cert) { return cert.getIssuerX500Principal(); } protected static Date getValidDate(PKIXParameters paramsPKIX) { Date validDate = paramsPKIX.getDate(); if (validDate == null) { validDate = new Date(); } return validDate; } protected static X500Principal getSubjectPrincipal(X509Certificate cert) { return cert.getSubjectX500Principal(); } protected static boolean isSelfIssued(X509Certificate cert) { return cert.getSubjectDN().equals(cert.getIssuerDN()); } /** * extract the value of the given extension, if it exists. */ protected static DERObject getExtensionValue( java.security.cert.X509Extension ext, String oid) throws AnnotatedException { byte[] bytes = ext.getExtensionValue(oid); if (bytes == null) { return null; } return getObject(oid, bytes); } private static DERObject getObject( String oid, byte[] ext) throws AnnotatedException { try { ASN1InputStream aIn = new ASN1InputStream(ext); ASN1OctetString octs = (ASN1OctetString)aIn.readObject(); aIn = new ASN1InputStream(octs.getOctets()); return aIn.readObject(); } catch (IOException e) { throw new AnnotatedException("exception processing extension " + oid, e); } } protected static X500Principal getIssuerPrincipal(X509CRL crl) { return crl.getIssuerX500Principal(); } protected static AlgorithmIdentifier getAlgorithmIdentifier( PublicKey key) throws CertPathValidatorException { try { ASN1InputStream aIn = new ASN1InputStream(key.getEncoded()); SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(aIn.readObject()); return info.getAlgorithmId(); } catch (IOException e) { throw new CertPathValidatorException("exception processing public key"); } } // // Utility functions for name constraint checking // private static boolean withinDNSubtree(ASN1Sequence dns, ASN1Sequence subtree) { if (subtree.size() < 1) { return false; } if (subtree.size() > dns.size()) { return false; } for (int j = subtree.size() - 1; j >= 0; j--) { if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j))) { return false; } } return true; } protected static void checkPermittedDN(Set permitted, ASN1Sequence dns) throws CertPathValidatorException { if (permitted.isEmpty()) { return; } Iterator it = permitted.iterator(); while (it.hasNext()) { ASN1Sequence subtree = (ASN1Sequence) it.next(); if (withinDNSubtree(dns, subtree)) { return; } } throw new CertPathValidatorException( "Subject distinguished name is not from a permitted subtree"); } protected static void checkExcludedDN(Set excluded, ASN1Sequence dns) throws CertPathValidatorException { if (excluded.isEmpty()) { return; } Iterator it = excluded.iterator(); while (it.hasNext()) { ASN1Sequence subtree = (ASN1Sequence) it.next(); if (withinDNSubtree(dns, subtree)) { throw new CertPathValidatorException( "Subject distinguished name is from an excluded subtree"); } } } protected static Set intersectDN(Set permitted, ASN1Sequence dn) { if (permitted.isEmpty()) { permitted.add(dn); return permitted; } else { Set intersect = new HashSet(); Iterator _iter = permitted.iterator(); while (_iter.hasNext()) { ASN1Sequence subtree = (ASN1Sequence) _iter.next(); if (withinDNSubtree(dn, subtree)) { intersect.add(dn); } else if (withinDNSubtree(subtree, dn)) { intersect.add(subtree); } } return intersect; } } protected static Set unionDN(Set excluded, ASN1Sequence dn) { if (excluded.isEmpty()) { excluded.add(dn); return excluded; } else { Set intersect = new HashSet(); Iterator _iter = excluded.iterator(); while (_iter.hasNext()) { ASN1Sequence subtree = (ASN1Sequence) _iter.next(); if (withinDNSubtree(dn, subtree)) { intersect.add(subtree); } else if (withinDNSubtree(subtree, dn)) { intersect.add(dn); } else { intersect.add(subtree); intersect.add(dn); } } return intersect; } } protected static Set intersectEmail(Set permitted, String email) { String _sub = email.substring(email.indexOf('@') + 1); if (permitted.isEmpty()) { permitted.add(_sub); return permitted; } else { Set intersect = new HashSet(); Iterator _iter = permitted.iterator(); while (_iter.hasNext()) { String _permitted = (String) _iter.next(); if (_sub.endsWith(_permitted)) { intersect.add(_sub); } else if (_permitted.endsWith(_sub)) { intersect.add(_permitted); } } return intersect; } } protected static Set unionEmail(Set excluded, String email) { String _sub = email.substring(email.indexOf('@') + 1); if (excluded.isEmpty()) { excluded.add(_sub); return excluded; } else { Set intersect = new HashSet(); Iterator _iter = excluded.iterator(); while (_iter.hasNext()) { String _excluded = (String) _iter.next(); if (_sub.endsWith(_excluded)) { intersect.add(_excluded); } else if (_excluded.endsWith(_sub)) { intersect.add(_sub); } else { intersect.add(_excluded); intersect.add(_sub); } } return intersect; } } protected static Set intersectIP(Set permitted, byte[] ip) { // TBD return permitted; } protected static Set unionIP(Set excluded, byte[] ip) { // TBD return excluded; } protected static void checkPermittedEmail(Set permitted, String email) throws CertPathValidatorException { if (permitted.isEmpty()) { return; } String sub = email.substring(email.indexOf('@') + 1); Iterator it = permitted.iterator(); while (it.hasNext()) { String str = (String) it.next(); if (sub.endsWith(str)) { return; } } throw new CertPathValidatorException( "Subject email address is not from a permitted subtree"); } protected static void checkExcludedEmail(Set excluded, String email) throws CertPathValidatorException { if (excluded.isEmpty()) { return; } String sub = email.substring(email.indexOf('@') + 1); Iterator it = excluded.iterator(); while (it.hasNext()) { String str = (String) it.next(); if (sub.endsWith(str)) { throw new CertPathValidatorException( "Subject email address is from an excluded subtree"); } } } protected static void checkPermittedIP(Set permitted, byte[] ip) throws CertPathValidatorException { if (permitted.isEmpty()) { return; } // TODO: ??? Something here } protected static void checkExcludedIP(Set excluded, byte[] ip) throws CertPathValidatorException { if (excluded.isEmpty()) { return; } // TODO, check RFC791 and RFC1883 for IP bytes definition. } // crl checking /** * Return a Collection of all CRLs found in the * CertStore's that are matching the crlSelect criteriums. * * @param crlSelect a {@link CertSelector CertSelector} * object that will be used to select the CRLs * @param crlStores a List containing only {@link CertStore * CertStore} objects. These are used to search for * CRLs * * @return a Collection of all found {@link CRL CRL} * objects. May be empty but never <code>null</code>. */ protected static final Collection findCRLs( X509CRLSelector crlSelect, List crlStores) throws AnnotatedException { Set crls = new HashSet(); Iterator iter = crlStores.iterator(); while (iter.hasNext()) { CertStore certStore = (CertStore)iter.next(); try { crls.addAll(certStore.getCRLs(crlSelect)); } catch (CertStoreException e) { throw new AnnotatedException("cannot extract crl: " + e, e); } } return crls; } // // policy checking // protected static final Set getQualifierSet(ASN1Sequence qualifiers) throws CertPathValidatorException { Set pq = new HashSet(); if (qualifiers == null) { return pq; } ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ASN1OutputStream aOut = new ASN1OutputStream(bOut); Enumeration e = qualifiers.getObjects(); while (e.hasMoreElements()) { try { aOut.writeObject(e.nextElement()); pq.add(new PolicyQualifierInfo(bOut.toByteArray())); } catch (IOException ex) { throw new CertPathValidatorException("exception building qualifier set: " + ex); } bOut.reset(); } return pq; } protected static PKIXPolicyNode removePolicyNode( PKIXPolicyNode validPolicyTree, List [] policyNodes, PKIXPolicyNode _node) { PKIXPolicyNode _parent = (PKIXPolicyNode)_node.getParent(); if (validPolicyTree == null) { return null; } if (_parent == null) { for (int j = 0; j < policyNodes.length; j++) { policyNodes[j] = new ArrayList(); } return null; } else { _parent.removeChild(_node); removePolicyNodeRecurse(policyNodes, _node); return validPolicyTree; } } private static void removePolicyNodeRecurse( List [] policyNodes, PKIXPolicyNode _node) { policyNodes[_node.getDepth()].remove(_node); if (_node.hasChildren()) { Iterator _iter = _node.getChildren(); while (_iter.hasNext()) { PKIXPolicyNode _child = (PKIXPolicyNode)_iter.next(); removePolicyNodeRecurse(policyNodes, _child); } } } protected static boolean processCertD1i( int index, List [] policyNodes, DERObjectIdentifier pOid, Set pq) { List policyNodeVec = policyNodes[index - 1]; for (int j = 0; j < policyNodeVec.size(); j++) { PKIXPolicyNode node = (PKIXPolicyNode)policyNodeVec.get(j); Set expectedPolicies = node.getExpectedPolicies(); if (expectedPolicies.contains(pOid.getId())) { Set childExpectedPolicies = new HashSet(); childExpectedPolicies.add(pOid.getId()); PKIXPolicyNode child = new PKIXPolicyNode(new ArrayList(), index, childExpectedPolicies, node, pq, pOid.getId(), false); node.addChild(child); policyNodes[index].add(child); return true; } } return false; } protected static void processCertD1ii( int index, List [] policyNodes, DERObjectIdentifier _poid, Set _pq) { List policyNodeVec = policyNodes[index - 1]; for (int j = 0; j < policyNodeVec.size(); j++) { PKIXPolicyNode _node = (PKIXPolicyNode)policyNodeVec.get(j); Set _expectedPolicies = _node.getExpectedPolicies(); if (ANY_POLICY.equals(_node.getValidPolicy())) { Set _childExpectedPolicies = new HashSet(); _childExpectedPolicies.add(_poid.getId()); PKIXPolicyNode _child = new PKIXPolicyNode(new ArrayList(), index, _childExpectedPolicies, _node, _pq, _poid.getId(), false); _node.addChild(_child); policyNodes[index].add(_child); return; } } } protected static void prepareNextCertB1( int i, List[] policyNodes, String id_p, Map m_idp, X509Certificate cert ) throws AnnotatedException,CertPathValidatorException { 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)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 = 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; } } } } protected static PKIXPolicyNode prepareNextCertB2( int i, List[] policyNodes, String id_p, PKIXPolicyNode validPolicyTree) { 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 = removePolicyNode(validPolicyTree, policyNodes, node2); if (validPolicyTree == null) { break; } } } } } } return validPolicyTree; } protected static boolean isAnyPolicy( Set policySet) { return policySet == null || policySet.contains(ANY_POLICY) || policySet.isEmpty(); } }