PKIXCertPathValidatorImpl.java [plain text]
package gnu.java.security.provider;
import gnu.java.security.OID;
import gnu.java.security.x509.GnuPKIExtension;
import gnu.java.security.x509.PolicyNodeImpl;
import gnu.java.security.x509.X509CertSelectorImpl;
import gnu.java.security.x509.X509CRLSelectorImpl;
import gnu.java.security.x509.ext.BasicConstraints;
import gnu.java.security.x509.ext.CertificatePolicies;
import gnu.java.security.x509.ext.Extension;
import gnu.java.security.x509.ext.KeyUsage;
import gnu.java.security.x509.ext.PolicyConstraint;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
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.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CRL;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class PKIXCertPathValidatorImpl extends CertPathValidatorSpi
{
private static final boolean DEBUG = false;
private static void debug (String msg)
{
System.err.print (">> PKIXCertPathValidatorImpl: ");
System.err.println (msg);
}
public static final String ANY_POLICY = "2.5.29.32.0";
public PKIXCertPathValidatorImpl()
{
super();
}
public CertPathValidatorResult engineValidate(CertPath path,
CertPathParameters params)
throws CertPathValidatorException, InvalidAlgorithmParameterException
{
if (!(params instanceof PKIXParameters))
throw new InvalidAlgorithmParameterException("not a PKIXParameters object");
PolicyNodeImpl rootNode = new PolicyNodeImpl();
Set initPolicies = ((PKIXParameters) params).getInitialPolicies();
rootNode.setValidPolicy(ANY_POLICY);
rootNode.setCritical(false);
rootNode.setDepth(0);
if (initPolicies != null)
rootNode.addAllExpectedPolicies(initPolicies);
else
rootNode.addExpectedPolicy(ANY_POLICY);
List checks = ((PKIXParameters) params).getCertPathCheckers();
List l = path.getCertificates();
if (l == null || l.size() == 0)
throw new CertPathValidatorException();
X509Certificate[] p = null;
try
{
p = (X509Certificate[]) l.toArray(new X509Certificate[l.size()]);
}
catch (ClassCastException cce)
{
throw new CertPathValidatorException("invalid certificate path");
}
String sigProvider = ((PKIXParameters) params).getSigProvider();
PublicKey prevKey = null;
Date now = ((PKIXParameters) params).getDate();
if (now == null)
now = new Date();
LinkedList policyConstraints = new LinkedList();
for (int i = p.length - 1; i >= 0; i--)
{
try
{
p[i].checkValidity(now);
}
catch (CertificateException ce)
{
throw new CertPathValidatorException(ce.toString());
}
Set uce = getCritExts(p[i]);
for (Iterator check = checks.iterator(); check.hasNext(); )
{
try
{
((PKIXCertPathChecker) check.next()).check(p[i], uce);
}
catch (Exception x)
{
}
}
PolicyConstraint constr = null;
if (p[i] instanceof GnuPKIExtension)
{
Extension pcx =
((GnuPKIExtension) p[i]).getExtension (PolicyConstraint.ID);
if (pcx != null)
constr = (PolicyConstraint) pcx.getValue();
}
else
{
byte[] pcx = p[i].getExtensionValue (PolicyConstraint.ID.toString());
if (pcx != null)
{
try
{
constr = new PolicyConstraint (pcx);
}
catch (Exception x)
{
}
}
}
if (constr != null && constr.getRequireExplicitPolicy() >= 0)
{
policyConstraints.add (new int[]
{ p.length-i, constr.getRequireExplicitPolicy() });
}
updatePolicyTree(p[i], rootNode, p.length-i, (PKIXParameters) params,
checkExplicitPolicy (p.length-i, policyConstraints));
if (i == 0)
break;
basicSanity(p, i);
PublicKey pubKey = null;
try
{
pubKey = p[i].getPublicKey();
if (pubKey instanceof DSAPublicKey)
{
DSAParams dsa = ((DSAPublicKey) pubKey).getParams();
if (dsa == null || dsa.getP() == null || dsa.getG() == null
|| dsa.getQ() == null)
{
if (prevKey == null)
throw new InvalidKeyException("DSA keys not chainable");
if (!(prevKey instanceof DSAPublicKey))
throw new InvalidKeyException("DSA keys not chainable");
dsa = ((DSAPublicKey) prevKey).getParams();
pubKey = new GnuDSAPublicKey(((DSAPublicKey) pubKey).getY(),
dsa.getP(), dsa.getQ(), dsa.getG());
}
}
if (sigProvider == null)
p[i-1].verify(pubKey);
else
p[i-1].verify(pubKey, sigProvider);
prevKey = pubKey;
}
catch (Exception e)
{
throw new CertPathValidatorException(e.toString());
}
if (!p[i].getSubjectDN().equals(p[i-1].getIssuerDN()))
throw new CertPathValidatorException("issuer DN mismatch");
boolean[] issuerUid = p[i-1].getIssuerUniqueID();
boolean[] subjectUid = p[i].getSubjectUniqueID();
if (issuerUid != null && subjectUid != null)
if (!Arrays.equals(issuerUid, subjectUid))
throw new CertPathValidatorException("UID mismatch");
if (((PKIXParameters) params).isRevocationEnabled())
{
X509CRLSelectorImpl selector = new X509CRLSelectorImpl();
try
{
selector.addIssuerName(p[i].getSubjectDN());
}
catch (IOException ioe)
{
throw new CertPathValidatorException("error selecting CRLs");
}
List certStores = ((PKIXParameters) params).getCertStores();
List crls = new LinkedList();
for (Iterator it = certStores.iterator(); it.hasNext(); )
{
CertStore cs = (CertStore) it.next();
try
{
Collection c = cs.getCRLs(selector);
crls.addAll(c);
}
catch (CertStoreException cse)
{
}
}
if (crls.isEmpty())
throw new CertPathValidatorException("no CRLs for issuer");
boolean certOk = false;
for (Iterator it = crls.iterator(); it.hasNext(); )
{
CRL crl = (CRL) it.next();
if (!(crl instanceof X509CRL))
continue;
X509CRL xcrl = (X509CRL) crl;
if (!checkCRL(xcrl, p, now, p[i], pubKey, certStores))
continue;
if (xcrl.isRevoked(p[i-1]))
throw new CertPathValidatorException("certificate is revoked");
else
certOk = true;
}
if (!certOk)
throw new CertPathValidatorException("certificate's validity could not be determined");
}
}
rootNode.setReadOnly();
Exception cause = null;
Set anchors = ((PKIXParameters) params).getTrustAnchors();
for (Iterator i = anchors.iterator(); i.hasNext(); )
{
TrustAnchor anchor = (TrustAnchor) i.next();
X509Certificate anchorCert = null;
PublicKey anchorKey = null;
if (anchor.getTrustedCert() != null)
{
anchorCert = anchor.getTrustedCert();
anchorKey = anchorCert.getPublicKey();
}
else
anchorKey = anchor.getCAPublicKey();
if (anchorKey == null)
continue;
try
{
if (anchorCert == null)
anchorCert.checkValidity(now);
p[p.length-1].verify(anchorKey);
if (anchorCert != null && anchorCert.getBasicConstraints() >= 0
&& anchorCert.getBasicConstraints() < p.length)
continue;
if (((PKIXParameters) params).isRevocationEnabled())
{
X509CRLSelectorImpl selector = new X509CRLSelectorImpl();
if (anchorCert != null)
try
{
selector.addIssuerName(anchorCert.getSubjectDN());
}
catch (IOException ioe)
{
}
else
selector.addIssuerName(anchor.getCAName());
List certStores = ((PKIXParameters) params).getCertStores();
List crls = new LinkedList();
for (Iterator it = certStores.iterator(); it.hasNext(); )
{
CertStore cs = (CertStore) it.next();
try
{
Collection c = cs.getCRLs(selector);
crls.addAll(c);
}
catch (CertStoreException cse)
{
}
}
if (crls.isEmpty())
continue;
for (Iterator it = crls.iterator(); it.hasNext(); )
{
CRL crl = (CRL) it.next();
if (!(crl instanceof X509CRL))
continue;
X509CRL xcrl = (X509CRL) crl;
try
{
xcrl.verify(anchorKey);
}
catch (Exception x)
{
continue;
}
Date nextUpdate = xcrl.getNextUpdate();
if (nextUpdate != null && nextUpdate.compareTo(now) < 0)
continue;
if (xcrl.isRevoked(p[p.length-1]))
throw new CertPathValidatorException("certificate is revoked");
}
}
return new PKIXCertPathValidatorResult(anchor, rootNode,
p[0].getPublicKey());
}
catch (Exception ignored)
{
cause = ignored;
continue;
}
}
CertPathValidatorException cpve =
new CertPathValidatorException("path validation failed");
if (cause != null)
cpve.initCause (cause);
throw cpve;
}
private static boolean checkCRL(X509CRL crl, X509Certificate[] path, Date now,
X509Certificate pubKeyCert, PublicKey pubKey,
List certStores)
{
Date nextUpdate = crl.getNextUpdate();
if (nextUpdate != null && nextUpdate.compareTo(now) < 0)
return false;
if (crl.hasUnsupportedCriticalExtension())
return false;
for (int i = 0; i < path.length; i++)
{
if (!path[i].getSubjectDN().equals(crl.getIssuerDN()))
continue;
boolean[] keyUsage = path[i].getKeyUsage();
if (keyUsage != null)
{
if (!keyUsage[KeyUsage.CRL_SIGN])
continue;
}
try
{
crl.verify(path[i].getPublicKey());
return true;
}
catch (Exception x)
{
}
}
if (crl.getIssuerDN().equals(pubKeyCert.getSubjectDN()))
{
try
{
boolean[] keyUsage = pubKeyCert.getKeyUsage();
if (keyUsage != null)
{
if (!keyUsage[KeyUsage.CRL_SIGN])
throw new Exception();
}
crl.verify(pubKey);
return true;
}
catch (Exception x)
{
}
}
try
{
X509CertSelectorImpl select = new X509CertSelectorImpl();
select.addSubjectName(crl.getIssuerDN());
List certs = new LinkedList();
for (Iterator it = certStores.iterator(); it.hasNext(); )
{
CertStore cs = (CertStore) it.next();
try
{
certs.addAll(cs.getCertificates(select));
}
catch (CertStoreException cse)
{
}
}
for (Iterator it = certs.iterator(); it.hasNext(); )
{
X509Certificate c = (X509Certificate) it.next();
for (int i = 0; i < path.length; i++)
{
if (!c.getIssuerDN().equals(path[i].getSubjectDN()))
continue;
boolean[] keyUsage = c.getKeyUsage();
if (keyUsage != null)
{
if (!keyUsage[KeyUsage.CRL_SIGN])
continue;
}
try
{
c.verify(path[i].getPublicKey());
crl.verify(c.getPublicKey());
return true;
}
catch (Exception x)
{
}
}
if (c.getIssuerDN().equals(pubKeyCert.getSubjectDN()))
{
c.verify(pubKey);
crl.verify(c.getPublicKey());
}
}
}
catch (Exception x)
{
}
return false;
}
private static Set getCritExts(X509Certificate cert)
{
HashSet s = new HashSet();
if (cert instanceof GnuPKIExtension)
{
Collection exts = ((GnuPKIExtension) cert).getExtensions();
for (Iterator it = exts.iterator(); it.hasNext(); )
{
Extension ext = (Extension) it.next();
if (ext.isCritical() && !ext.isSupported())
s.add(ext.getOid().toString());
}
}
else
s.addAll(cert.getCriticalExtensionOIDs());
return s;
}
private static void basicSanity(X509Certificate[] path, int index)
throws CertPathValidatorException
{
X509Certificate cert = path[index];
int pathLen = 0;
for (int i = index - 1; i > 0; i--)
{
if (!path[i].getIssuerDN().equals(path[i].getSubjectDN()))
pathLen++;
}
Extension e = null;
if (cert instanceof GnuPKIExtension)
{
e = ((GnuPKIExtension) cert).getExtension(BasicConstraints.ID);
}
else
{
try
{
e = new Extension(cert.getExtensionValue(BasicConstraints.ID.toString()));
}
catch (Exception x)
{
}
}
if (e == null)
throw new CertPathValidatorException("no basicConstraints");
BasicConstraints bc = (BasicConstraints) e.getValue();
if (!bc.isCA())
throw new CertPathValidatorException("certificate cannot be used to verify signatures");
if (bc.getPathLengthConstraint() >= 0 && bc.getPathLengthConstraint() < pathLen)
throw new CertPathValidatorException("path is too long");
boolean[] keyUsage = cert.getKeyUsage();
if (keyUsage != null)
{
if (!keyUsage[KeyUsage.KEY_CERT_SIGN])
throw new CertPathValidatorException("certificate cannot be used to sign certificates");
}
}
private static void updatePolicyTree(X509Certificate cert, PolicyNodeImpl root,
int depth, PKIXParameters params,
boolean explicitPolicy)
throws CertPathValidatorException
{
if (DEBUG) debug("updatePolicyTree depth == " + depth);
Set nodes = new HashSet();
LinkedList stack = new LinkedList();
Iterator current = null;
stack.addLast(Collections.singleton(root).iterator());
do
{
current = (Iterator) stack.removeLast();
while (current.hasNext())
{
PolicyNodeImpl p = (PolicyNodeImpl) current.next();
if (DEBUG) debug("visiting node == " + p);
if (p.getDepth() == depth - 1)
{
if (DEBUG) debug("added node");
nodes.add(p);
}
else
{
if (DEBUG) debug("skipped node");
stack.addLast(current);
current = p.getChildren();
}
}
}
while (!stack.isEmpty());
Extension e = null;
CertificatePolicies policies = null;
List qualifierInfos = null;
if (cert instanceof GnuPKIExtension)
{
e = ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID);
if (e != null)
policies = (CertificatePolicies) e.getValue();
}
List cp = null;
if (policies != null)
cp = policies.getPolicies();
else
cp = Collections.EMPTY_LIST;
boolean match = false;
if (DEBUG) debug("nodes are == " + nodes);
if (DEBUG) debug("cert policies are == " + cp);
for (Iterator it = nodes.iterator(); it.hasNext(); )
{
PolicyNodeImpl parent = (PolicyNodeImpl) it.next();
if (DEBUG) debug("adding policies to " + parent);
for (Iterator it2 = cp.iterator(); it2.hasNext(); )
{
OID policy = (OID) it2.next();
if (DEBUG) debug("trying to add policy == " + policy);
if (policy.toString().equals(ANY_POLICY) &&
params.isAnyPolicyInhibited())
continue;
PolicyNodeImpl child = new PolicyNodeImpl();
child.setValidPolicy(policy.toString());
child.addExpectedPolicy(policy.toString());
if (parent.getExpectedPolicies().contains(policy.toString()))
{
parent.addChild(child);
match = true;
}
else if (parent.getExpectedPolicies().contains(ANY_POLICY))
{
parent.addChild(child);
match = true;
}
else if (ANY_POLICY.equals (policy.toString()))
{
parent.addChild (child);
match = true;
}
if (match && policies != null)
{
List qualifiers = policies.getPolicyQualifierInfos (policy);
if (qualifiers != null)
child.addAllPolicyQualifiers (qualifiers);
}
}
}
if (!match && (params.isExplicitPolicyRequired() || explicitPolicy))
throw new CertPathValidatorException("policy tree building failed");
}
private boolean checkExplicitPolicy (int depth, List explicitPolicies)
{
if (DEBUG) debug ("checkExplicitPolicy depth=" + depth);
for (Iterator it = explicitPolicies.iterator(); it.hasNext(); )
{
int[] i = (int[]) it.next();
int caDepth = i[0];
int limit = i[1];
if (DEBUG) debug (" caDepth=" + caDepth + " limit=" + limit);
if (depth - caDepth >= limit)
return true;
}
return false;
}
}