|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.provider.certpath; |
|
|
|
import java.io.IOException; |
|
import java.security.GeneralSecurityException; |
|
import java.security.InvalidAlgorithmParameterException; |
|
import java.security.PublicKey; |
|
import java.security.cert.*; |
|
import java.security.cert.CertPathValidatorException.BasicReason; |
|
import java.security.cert.PKIXReason; |
|
import java.util.ArrayList; |
|
import java.util.Collection; |
|
import java.util.Collections; |
|
import java.util.List; |
|
import java.util.LinkedList; |
|
import java.util.Set; |
|
import javax.security.auth.x500.X500Principal; |
|
|
|
import sun.security.provider.certpath.PKIX.BuilderParams; |
|
import static sun.security.x509.PKIXExtensions.*; |
|
import sun.security.util.Debug; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class SunCertPathBuilder extends CertPathBuilderSpi { |
|
|
|
private static final Debug debug = Debug.getInstance("certpath"); |
|
|
|
|
|
|
|
*/ |
|
private BuilderParams buildParams; |
|
private CertificateFactory cf; |
|
private boolean pathCompleted = false; |
|
private PolicyNode policyTreeResult; |
|
private TrustAnchor trustAnchor; |
|
private PublicKey finalPublicKey; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SunCertPathBuilder() throws CertPathBuilderException { |
|
try { |
|
cf = CertificateFactory.getInstance("X.509"); |
|
} catch (CertificateException e) { |
|
throw new CertPathBuilderException(e); |
|
} |
|
} |
|
|
|
@Override |
|
public CertPathChecker engineGetRevocationChecker() { |
|
return new RevocationChecker(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public CertPathBuilderResult engineBuild(CertPathParameters params) |
|
throws CertPathBuilderException, InvalidAlgorithmParameterException { |
|
|
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.engineBuild(" + params + ")"); |
|
} |
|
|
|
buildParams = PKIX.checkBuilderParams(params); |
|
return build(); |
|
} |
|
|
|
private PKIXCertPathBuilderResult build() throws CertPathBuilderException { |
|
List<List<Vertex>> adjList = new ArrayList<>(); |
|
PKIXCertPathBuilderResult result = buildCertPath(false, adjList); |
|
if (result == null) { |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " + |
|
"try building again searching all certstores"); |
|
} |
|
|
|
adjList.clear(); |
|
result = buildCertPath(true, adjList); |
|
if (result == null) { |
|
throw new SunCertPathBuilderException("unable to find valid " |
|
+ "certification path to requested target", |
|
new AdjacencyList(adjList)); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
private PKIXCertPathBuilderResult buildCertPath(boolean searchAllCertStores, |
|
List<List<Vertex>> adjList) |
|
throws CertPathBuilderException |
|
{ |
|
|
|
pathCompleted = false; |
|
trustAnchor = null; |
|
finalPublicKey = null; |
|
policyTreeResult = null; |
|
LinkedList<X509Certificate> certPathList = new LinkedList<>(); |
|
try { |
|
buildForward(adjList, certPathList, searchAllCertStores); |
|
} catch (GeneralSecurityException | IOException e) { |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.engineBuild() exception in " |
|
+ "build"); |
|
e.printStackTrace(); |
|
} |
|
throw new SunCertPathBuilderException("unable to find valid " |
|
+ "certification path to requested target", e, |
|
new AdjacencyList(adjList)); |
|
} |
|
|
|
|
|
try { |
|
if (pathCompleted) { |
|
if (debug != null) |
|
debug.println("SunCertPathBuilder.engineBuild() " |
|
+ "pathCompleted"); |
|
|
|
// we must return a certpath which has the target |
|
// as the first cert in the certpath - i.e. reverse |
|
|
|
Collections.reverse(certPathList); |
|
|
|
return new SunCertPathBuilderResult( |
|
cf.generateCertPath(certPathList), trustAnchor, |
|
policyTreeResult, finalPublicKey, |
|
new AdjacencyList(adjList)); |
|
} |
|
} catch (CertificateException e) { |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.engineBuild() exception " |
|
+ "in wrap-up"); |
|
e.printStackTrace(); |
|
} |
|
throw new SunCertPathBuilderException("unable to find valid " |
|
+ "certification path to requested target", e, |
|
new AdjacencyList(adjList)); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void buildForward(List<List<Vertex>> adjacencyList, |
|
LinkedList<X509Certificate> certPathList, |
|
boolean searchAllCertStores) |
|
throws GeneralSecurityException, IOException |
|
{ |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.buildForward()..."); |
|
} |
|
|
|
|
|
ForwardState currentState = new ForwardState(); |
|
currentState.initState(buildParams.certPathCheckers()); |
|
|
|
|
|
adjacencyList.clear(); |
|
adjacencyList.add(new LinkedList<Vertex>()); |
|
|
|
currentState.untrustedChecker = new UntrustedChecker(); |
|
|
|
depthFirstSearchForward(buildParams.targetSubject(), currentState, |
|
new ForwardBuilder(buildParams, |
|
searchAllCertStores), |
|
adjacencyList, certPathList); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void depthFirstSearchForward(X500Principal dN, |
|
ForwardState currentState, |
|
ForwardBuilder builder, |
|
List<List<Vertex>> adjList, |
|
LinkedList<X509Certificate> cpList) |
|
throws GeneralSecurityException, IOException |
|
{ |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN |
|
+ ", " + currentState.toString() + ")"); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
Collection<X509Certificate> certs = |
|
builder.getMatchingCerts(currentState, buildParams.certStores()); |
|
List<Vertex> vertices = addVertices(certs, adjList); |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward(): " |
|
+ "certs.size=" + vertices.size()); |
|
} |
|
|
|
/* |
|
* For each cert in the collection, verify anything |
|
* that hasn't been checked yet (signature, revocation, etc) |
|
* and check for loops. Call depthFirstSearchForward() |
|
* recursively for each good cert. |
|
*/ |
|
|
|
vertices: |
|
for (Vertex vertex : vertices) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
ForwardState nextState = (ForwardState) currentState.clone(); |
|
X509Certificate cert = vertex.getCertificate(); |
|
|
|
try { |
|
builder.verifyCert(cert, nextState, cpList); |
|
} catch (GeneralSecurityException gse) { |
|
if (debug != null) { |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward()" |
|
+ ": validation failed: " + gse); |
|
gse.printStackTrace(); |
|
} |
|
vertex.setThrowable(gse); |
|
continue; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (builder.isPathCompleted(cert)) { |
|
|
|
if (debug != null) |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward()" |
|
+ ": commencing final verification"); |
|
|
|
List<X509Certificate> appendedCerts = new ArrayList<>(cpList); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (builder.trustAnchor.getTrustedCert() == null) { |
|
appendedCerts.add(0, cert); |
|
} |
|
|
|
Set<String> initExpPolSet = |
|
Collections.singleton(PolicyChecker.ANY_POLICY); |
|
|
|
PolicyNodeImpl rootNode = new PolicyNodeImpl(null, |
|
PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false); |
|
|
|
List<PKIXCertPathChecker> checkers = new ArrayList<>(); |
|
PolicyChecker policyChecker |
|
= new PolicyChecker(buildParams.initialPolicies(), |
|
appendedCerts.size(), |
|
buildParams.explicitPolicyRequired(), |
|
buildParams.policyMappingInhibited(), |
|
buildParams.anyPolicyInhibited(), |
|
buildParams.policyQualifiersRejected(), |
|
rootNode); |
|
checkers.add(policyChecker); |
|
|
|
|
|
checkers.add(new AlgorithmChecker(builder.trustAnchor, |
|
buildParams.date(), buildParams.variant())); |
|
|
|
BasicChecker basicChecker = null; |
|
if (nextState.keyParamsNeeded()) { |
|
PublicKey rootKey = cert.getPublicKey(); |
|
if (builder.trustAnchor.getTrustedCert() == null) { |
|
rootKey = builder.trustAnchor.getCAPublicKey(); |
|
if (debug != null) |
|
debug.println( |
|
"SunCertPathBuilder.depthFirstSearchForward " + |
|
"using buildParams public key: " + |
|
rootKey.toString()); |
|
} |
|
TrustAnchor anchor = new TrustAnchor |
|
(cert.getSubjectX500Principal(), rootKey, null); |
|
|
|
|
|
basicChecker = new BasicChecker(anchor, buildParams.date(), |
|
buildParams.sigProvider(), |
|
true); |
|
checkers.add(basicChecker); |
|
} |
|
|
|
buildParams.setCertPath(cf.generateCertPath(appendedCerts)); |
|
|
|
boolean revCheckerAdded = false; |
|
List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers(); |
|
for (PKIXCertPathChecker ckr : ckrs) { |
|
if (ckr instanceof PKIXRevocationChecker) { |
|
if (revCheckerAdded) { |
|
throw new CertPathValidatorException( |
|
"Only one PKIXRevocationChecker can be specified"); |
|
} |
|
revCheckerAdded = true; |
|
|
|
if (ckr instanceof RevocationChecker) { |
|
((RevocationChecker)ckr).init(builder.trustAnchor, |
|
buildParams); |
|
} |
|
} |
|
} |
|
// only add a RevocationChecker if revocation is enabled and |
|
|
|
if (buildParams.revocationEnabled() && !revCheckerAdded) { |
|
checkers.add(new RevocationChecker(builder.trustAnchor, |
|
buildParams)); |
|
} |
|
|
|
checkers.addAll(ckrs); |
|
|
|
// Why we don't need BasicChecker and RevocationChecker |
|
// if nextState.keyParamsNeeded() is false? |
|
|
|
for (int i = 0; i < appendedCerts.size(); i++) { |
|
X509Certificate currCert = appendedCerts.get(i); |
|
if (debug != null) |
|
debug.println("current subject = " |
|
+ currCert.getSubjectX500Principal()); |
|
Set<String> unresCritExts = |
|
currCert.getCriticalExtensionOIDs(); |
|
if (unresCritExts == null) { |
|
unresCritExts = Collections.<String>emptySet(); |
|
} |
|
|
|
for (PKIXCertPathChecker currChecker : checkers) { |
|
if (!currChecker.isForwardCheckingSupported()) { |
|
if (i == 0) { |
|
currChecker.init(false); |
|
|
|
// The user specified |
|
// AlgorithmChecker may not be |
|
|
|
if (currChecker instanceof AlgorithmChecker) { |
|
((AlgorithmChecker)currChecker). |
|
trySetTrustAnchor(builder.trustAnchor); |
|
} |
|
} |
|
|
|
try { |
|
currChecker.check(currCert, unresCritExts); |
|
} catch (CertPathValidatorException cpve) { |
|
if (debug != null) |
|
debug.println |
|
("SunCertPathBuilder.depthFirstSearchForward(): " + |
|
"final verification failed: " + cpve); |
|
// If the target cert itself is revoked, we |
|
|
|
if (buildParams.targetCertConstraints().match(currCert) |
|
&& cpve.getReason() == BasicReason.REVOKED) { |
|
throw cpve; |
|
} |
|
vertex.setThrowable(cpve); |
|
continue vertices; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
for (PKIXCertPathChecker checker : |
|
buildParams.certPathCheckers()) |
|
{ |
|
if (checker.isForwardCheckingSupported()) { |
|
Set<String> suppExts = |
|
checker.getSupportedExtensions(); |
|
if (suppExts != null) { |
|
unresCritExts.removeAll(suppExts); |
|
} |
|
} |
|
} |
|
|
|
if (!unresCritExts.isEmpty()) { |
|
unresCritExts.remove(BasicConstraints_Id.toString()); |
|
unresCritExts.remove(NameConstraints_Id.toString()); |
|
unresCritExts.remove(CertificatePolicies_Id.toString()); |
|
unresCritExts.remove(PolicyMappings_Id.toString()); |
|
unresCritExts.remove(PolicyConstraints_Id.toString()); |
|
unresCritExts.remove(InhibitAnyPolicy_Id.toString()); |
|
unresCritExts.remove( |
|
SubjectAlternativeName_Id.toString()); |
|
unresCritExts.remove(KeyUsage_Id.toString()); |
|
unresCritExts.remove(ExtendedKeyUsage_Id.toString()); |
|
|
|
if (!unresCritExts.isEmpty()) { |
|
throw new CertPathValidatorException |
|
("unrecognized critical extension(s)", null, |
|
null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT); |
|
} |
|
} |
|
} |
|
if (debug != null) |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward()" |
|
+ ": final verification succeeded - path completed!"); |
|
pathCompleted = true; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (builder.trustAnchor.getTrustedCert() == null) |
|
builder.addCertToPath(cert, cpList); |
|
|
|
this.trustAnchor = builder.trustAnchor; |
|
|
|
|
|
|
|
*/ |
|
if (basicChecker != null) { |
|
finalPublicKey = basicChecker.getPublicKey(); |
|
} else { |
|
Certificate finalCert; |
|
if (cpList.isEmpty()) { |
|
finalCert = builder.trustAnchor.getTrustedCert(); |
|
} else { |
|
finalCert = cpList.getLast(); |
|
} |
|
finalPublicKey = finalCert.getPublicKey(); |
|
} |
|
|
|
policyTreeResult = policyChecker.getPolicyTree(); |
|
return; |
|
} else { |
|
builder.addCertToPath(cert, cpList); |
|
} |
|
|
|
|
|
nextState.updateState(cert); |
|
|
|
|
|
|
|
|
|
*/ |
|
adjList.add(new LinkedList<Vertex>()); |
|
vertex.setIndex(adjList.size() - 1); |
|
|
|
|
|
depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, |
|
builder, adjList, cpList); |
|
|
|
|
|
|
|
*/ |
|
if (pathCompleted) { |
|
return; |
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (debug != null) |
|
debug.println("SunCertPathBuilder.depthFirstSearchForward()" |
|
+ ": backtracking"); |
|
builder.removeFinalCertFromPath(cpList); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static List<Vertex> addVertices(Collection<X509Certificate> certs, |
|
List<List<Vertex>> adjList) |
|
{ |
|
List<Vertex> l = adjList.get(adjList.size() - 1); |
|
|
|
for (X509Certificate cert : certs) { |
|
Vertex v = new Vertex(cert); |
|
l.add(v); |
|
} |
|
|
|
return l; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static boolean anchorIsTarget(TrustAnchor anchor, |
|
CertSelector sel) |
|
{ |
|
X509Certificate anchorCert = anchor.getTrustedCert(); |
|
if (anchorCert != null) { |
|
return sel.match(anchorCert); |
|
} |
|
return false; |
|
} |
|
} |