blob: 8383b9862edb254f5cfdc02782715c3922330e2f [file] [log] [blame]
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();
}
}