After future digging and hacking away at the OpenJDK classes ... I found this is not a bug in Java. Further inspection of RFC shows we should start path checks at the intermediate and things in the trust anchor needn't be checked.
When the trust anchor is provided in the form of a self-signed certificate, this self-signed certificate is not included as part of the prospective certification path. Information about trust anchors is provided as inputs to the certification path validation algorithm (Section 6.1.1).
So all that needs done is to remove the ca from the chain before calling generateCertPath()
I crafted a test case:
[mute@scelastic code]$ java test test.pem
PEM contents: [PEMKeyPair] [X509CertificateHolder] [X509CertificateHolder] [X509CertificateHolder]
cert0: CN=test.XXX.mil, OU=USN, OU=PKI, OU=DoD, O=U.S. Government, C=US
cert1: CN=DOD SW CA-60, OU=PKI, OU=DoD, O=U.S. Government, C=US
cert2: CN=DoD Root CA 3, OU=PKI, OU=DoD, O=U.S. Government, C=US
CA: CN=DoD Root CA 3, OU=PKI, OU=DoD, O=U.S. Government, C=US
java.security.cert.CertPathValidatorException: non-null policy tree required and policy tree is null
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:135)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:233)
at sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:141)
at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:80)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292)
at test.main(test.java:52)
Caused by: java.security.cert.CertPathValidatorException: non-null policy tree required and policy tree is null
at sun.security.provider.certpath.PolicyChecker.processPolicies(PolicyChecker.java:572)
at sun.security.provider.certpath.PolicyChecker.checkPolicy(PolicyChecker.java:225)
at sun.security.provider.certpath.PolicyChecker.check(PolicyChecker.java:180)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125)
... 5 more
org.bouncycastle.jce.exception.ExtCertPathValidatorException: No valid policy tree found when one expected.
at org.bouncycastle.jce.provider.RFC3280CertPathUtilities.processCertF(Unknown Source)
at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(Unknown Source)
at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292)
at test.main(test.java:53)
java.lang.NullPointerException
at test.main(test.java:54)
I simply put in a chain.remove(ca);
and
[mute@scelastic code]$ vi test.java
[mute@scelastic code]$ javac test.java && java test test.pem # go
PEM contents: [PEMKeyPair] [X509CertificateHolder] [X509CertificateHolder] [X509CertificateHolder]
cert0: CN=test.XXX.mil, OU=USN, OU=PKI, OU=DoD, O=U.S. Government, C=US
cert1: CN=DOD SW CA-60, OU=PKI, OU=DoD, O=U.S. Government, C=US
CA: CN=DoD Root CA 3, OU=PKI, OU=DoD, O=U.S. Government, C=US
JAVA: anyPolicy ROOT
2.16.840.1.101.2.1.11.36 CRIT: false EP: 2.16.840.1.101.2.1.11.36 (1)
2.16.840.1.101.2.1.11.36 CRIT: false EP: 2.16.840.1.101.2.1.11.36 (2)
BCP: 2.5.29.32.0 {
2.16.840.1.101.2.1.11.36 {
2.16.840.1.101.2.1.11.36 {
}
}
2.16.840.1.101.2.1.11.42 {
}
}
The relevant half of the test code:
public class test {
public static void main(String[] args) throws FileNotFoundException, CertificateException {
try {
List<X509Certificate> chain = createCertificateChain(args[0]);
X509Certificate ca = (X509Certificate)chain.get(chain.size() - 1);
PKIXCertPathValidatorResult r1 = null, r2 = null;
chain.remove(ca);
CertPath certPath = CertificateFactory.getInstance("X.509").generateCertPath(chain);
CertPathValidator v1 = CertPathValidator.getInstance("PKIX");
CertPathValidator v2 = CertPathValidator.getInstance("PKIX", new BouncyCastleProvider());
TrustAnchor trustAnchor = new TrustAnchor((X509Certificate)ca, null);
PKIXParameters params = new PKIXParameters(Collections.singleton(trustAnchor));
// params.setInitialPolicies(Collections.singleton("2.16.840.1.101.2.1.11.36"));
params.setRevocationEnabled(false);
for (int i = 0; i < chain.size(); i++) {
System.out.println("cert" + i + ": " + chain.get(i).getSubjectX500Principal());
}
System.out.println("CA: " + ca.getSubjectX500Principal());
try { r1 = (PKIXCertPathValidatorResult)v1.validate(certPath, params); } catch (Exception e) { e.printStackTrace(); }
try { r2 = (PKIXCertPathValidatorResult)v2.validate(certPath, params); } catch (Exception e) { e.printStackTrace(); }
System.out.println("JAVA: " + r1.getPolicyTree());
System.out.println("BCP: " + r2.getPolicyTree());
} catch (Exception e) {
e.printStackTrace();
}
}
But while I was hacking at the OpenJDK classes I commented out the Policy Checker and installed my cert into ECE and it's working OK.