# HG changeset patch # User mullan # Date 1374764280 14400 # Node ID bc9a25fff6c58f05737d5675166586c53959a121 # Parent d9558f046caab605d6e0194854b478fcf12eced6 8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option Reviewed-by: vinnie diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java --- a/jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java Thu Jul 25 10:58:00 2013 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,21 +50,26 @@ * status of certificates with OCSP and CRLs. By default, OCSP is the * preferred mechanism for checking revocation status, with CRLs as the * fallback mechanism. However, this preference can be switched to CRLs with - * the {@link Option#PREFER_CRLS PREFER_CRLS} option. + * the {@link Option#PREFER_CRLS PREFER_CRLS} option. In addition, the fallback + * mechanism can be disabled with the {@link Option#NO_FALLBACK NO_FALLBACK} + * option. * *

A {@code PKIXRevocationChecker} is obtained by calling the * {@link CertPathValidator#getRevocationChecker getRevocationChecker} method * of a PKIX {@code CertPathValidator}. Additional parameters and options - * specific to revocation can be set (by calling {@link #setOCSPResponder} - * method for instance). The {@code PKIXRevocationChecker} is added to - * a {@code PKIXParameters} object using the - * {@link PKIXParameters#addCertPathChecker addCertPathChecker} + * specific to revocation can be set (by calling the + * {@link #setOcspResponder setOcspResponder} method for instance). The + * {@code PKIXRevocationChecker} is added to a {@code PKIXParameters} object + * using the {@link PKIXParameters#addCertPathChecker addCertPathChecker} * or {@link PKIXParameters#setCertPathCheckers setCertPathCheckers} method, * and then the {@code PKIXParameters} is passed along with the {@code CertPath} * to be validated to the {@link CertPathValidator#validate validate} method * of a PKIX {@code CertPathValidator}. When supplying a revocation checker in * this manner, it will be used to check revocation irrespective of the setting * of the {@link PKIXParameters#isRevocationEnabled RevocationEnabled} flag. + * Similarly, a {@code PKIXRevocationChecker} may be added to a + * {@code PKIXBuilderParameters} object for use with a PKIX + * {@code CertPathBuilder}. * *

Note that when a {@code PKIXRevocationChecker} is added to * {@code PKIXParameters}, it clones the {@code PKIXRevocationChecker}; @@ -83,6 +88,13 @@ * need not synchronize. * * @since 1.8 + * + * @see RFC 2560: X.509 + * Internet Public Key Infrastructure Online Certificate Status Protocol - + * OCSP,
RFC 5280: Internet X.509 + * Public Key Infrastructure Certificate and Certificate Revocation List (CRL) + * Profile */ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker { private URI ocspResponder; @@ -101,7 +113,7 @@ * * @param uri the responder URI */ - public void setOCSPResponder(URI uri) { + public void setOcspResponder(URI uri) { this.ocspResponder = uri; } @@ -114,7 +126,7 @@ * * @return the responder URI, or {@code null} if not set */ - public URI getOCSPResponder() { + public URI getOcspResponder() { return ocspResponder; } @@ -126,7 +138,7 @@ * * @param cert the responder's certificate */ - public void setOCSPResponderCert(X509Certificate cert) { + public void setOcspResponderCert(X509Certificate cert) { this.ocspResponderCert = cert; } @@ -140,7 +152,7 @@ * * @return the responder's certificate, or {@code null} if not set */ - public X509Certificate getOCSPResponderCert() { + public X509Certificate getOcspResponderCert() { return ocspResponderCert; } @@ -151,7 +163,7 @@ * @param extensions a list of extensions. The list is copied to protect * against subsequent modification. */ - public void setOCSPExtensions(List extensions) + public void setOcspExtensions(List extensions) { this.ocspExtensions = (extensions == null) ? Collections.emptyList() @@ -161,10 +173,10 @@ /** * Gets the optional OCSP request extensions. * - * @return an unmodifiable list of extensions. Returns an empty list if no + * @return an unmodifiable list of extensions. The list is empty if no * extensions have been specified. */ - public List getOCSPExtensions() { + public List getOcspExtensions() { return Collections.unmodifiableList(ocspExtensions); } @@ -177,7 +189,7 @@ * DER-encoded OCSP response for that certificate. A deep copy of * the map is performed to protect against subsequent modification. */ - public void setOCSPResponses(Map responses) + public void setOcspResponses(Map responses) { if (responses == null) { this.ocspResponses = Collections.emptyMap(); @@ -200,7 +212,7 @@ * the map is returned to protect against subsequent modification. * Returns an empty map if no responses have been specified. */ - public Map getOCSPResponses() { + public Map getOcspResponses() { Map copy = new HashMap<>(ocspResponses.size()); for (Map.Entry e : ocspResponses.entrySet()) { copy.put(e.getKey(), e.getValue().clone()); @@ -223,15 +235,31 @@ /** * Gets the revocation options. * - * @return an unmodifiable set of revocation options, or an empty set if - * none are specified + * @return an unmodifiable set of revocation options. The set is empty if + * no options have been specified. */ public Set

+ * An implementation of {@code PKIXRevocationChecker} is responsible for + * adding the ignored exceptions to the list. + * + * @return an unmodifiable list containing the ignored exceptions. The list + * is empty if no exceptions have been ignored. + */ + public abstract List getSoftFailExceptions(); + @Override - public Object clone() { + public PKIXRevocationChecker clone() { PKIXRevocationChecker copy = (PKIXRevocationChecker)super.clone(); copy.ocspExtensions = new ArrayList<>(ocspExtensions); copy.ocspResponses = new HashMap<>(ocspResponses); @@ -262,9 +290,26 @@ */ PREFER_CRLS, /** - * Ignore network failures. The default behavior is to consider it a - * failure if the revocation status of a certificate cannot be obtained - * due to a network error. This option applies to both OCSP and CRLs. + * Disable the fallback mechanism. + */ + NO_FALLBACK, + /** + * Allow revocation check to succeed if the revocation status cannot be + * determined for one of the following reasons: + *


+ * Note that these conditions apply to both OCSP and CRLs, and unless + * the {@code NO_FALLBACK} option is set, the revocation check is + * allowed to succeed only if both mechanisms fail under one of the + * conditions as stated above. + * Exceptions that cause the network errors are ignored but can be + * later retrieved by calling the + * {@link #getSoftFailExceptions getSoftFailExceptions} method. */ SOFT_FAIL } diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/OCSP.java --- a/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Thu Jul 25 10:58:00 2013 -0400 @@ -255,7 +255,9 @@ } response = Arrays.copyOf(response, total); } catch (IOException ioe) { - throw new NetworkFailureException(ioe); + throw new CertPathValidatorException( + "Unable to determine revocation status due to network error", + ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } finally { if (in != null) { try { @@ -355,17 +357,4 @@ */ Map getSingleExtensions(); } - - static class NetworkFailureException extends CertPathValidatorException { - private static final long serialVersionUID = 0l; - - NetworkFailureException(Throwable t) { - super(t); - } - - @Override - public CertPathValidatorException.Reason getReason() { - return BasicReason.UNDETERMINED_REVOCATION_STATUS; - } - } } diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java --- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Thu Jul 25 10:58:00 2013 -0400 @@ -30,6 +30,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.CRLReason; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; @@ -121,8 +122,8 @@ public enum ResponseStatus { SUCCESSFUL, // Response has valid confirmations - MALFORMED_REQUEST, // Illegal confirmation request - INTERNAL_ERROR, // Internal error in issuer + MALFORMED_REQUEST, // Illegal request + INTERNAL_ERROR, // Internal error in responder TRY_LATER, // Try again later UNUSED, // is not used SIG_REQUIRED, // Must sign the request @@ -381,9 +382,18 @@ Date date, byte[] nonce) throws CertPathValidatorException { - if (responseStatus != ResponseStatus.SUCCESSFUL) { - throw new CertPathValidatorException - ("OCSP response error: " + responseStatus); + switch (responseStatus) { + case SUCCESSFUL: + break; + case UNAUTHORIZED: + case TRY_LATER: + case INTERNAL_ERROR: + throw new CertPathValidatorException( + "OCSP response error: " + responseStatus, null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); + default: + throw new CertPathValidatorException("OCSP response error: " + + responseStatus); } // Check that the response includes a response for all of the diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java --- a/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Thu Jul 25 10:58:00 2013 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -193,10 +193,15 @@ List checkers = params.certPathCheckers(); for (PKIXCertPathChecker checker : checkers) { if (checker instanceof PKIXRevocationChecker) { + if (revCheckerAdded) { + throw new CertPathValidatorException( + "Only one PKIXRevocationChecker can be specified"); + } revCheckerAdded = true; // if it's our own, initialize it - if (checker instanceof RevocationChecker) + if (checker instanceof RevocationChecker) { ((RevocationChecker)checker).init(anchor, params); + } } } // only add a RevocationChecker if revocation is enabled and diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java --- a/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Thu Jul 25 10:58:00 2013 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertPathValidatorException; import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXRevocationChecker; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -235,9 +236,16 @@ for (PKIXCertPathChecker checker : userCheckers) { if (checker instanceof AlgorithmChecker) { ((AlgorithmChecker)checker).trySetTrustAnchor(anchor); - } else if (checker instanceof RevocationChecker) { - ((RevocationChecker)checker).init(anchor, buildParams); - ((RevocationChecker)checker).init(false); + } else if (checker instanceof PKIXRevocationChecker) { + if (revCheckerAdded) { + throw new CertPathValidatorException( + "Only one PKIXRevocationChecker can be specified"); + } + // if it's our own, initialize it + if (checker instanceof RevocationChecker) { + ((RevocationChecker)checker).init(anchor, buildParams); + } + ((PKIXRevocationChecker)checker).init(false); revCheckerAdded = true; } } diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java --- a/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Thu Jul 25 10:58:00 2013 -0400 @@ -38,14 +38,7 @@ import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.Extension; import java.security.cert.*; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import javax.security.auth.x500.X500Principal; import static sun.security.provider.certpath.OCSP.*; @@ -70,13 +63,16 @@ private Map ocspResponses; private List ocspExtensions; private boolean legacy; + private LinkedList softFailExceptions = + new LinkedList<>(); // state variables private X509Certificate issuerCert; private PublicKey prevPubKey; private boolean crlSignFlag; + private int certIndex; - private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS }; + private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP }; private Mode mode = Mode.PREFER_OCSP; private static class RevocationProperties { @@ -104,9 +100,9 @@ throws CertPathValidatorException { RevocationProperties rp = getRevocationProperties(); - URI uri = getOCSPResponder(); + URI uri = getOcspResponder(); responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri; - X509Certificate cert = getOCSPResponderCert(); + X509Certificate cert = getOcspResponderCert(); responderCert = (cert == null) ? getResponderCert(rp, params.trustAnchors(), params.certStores()) @@ -117,31 +113,38 @@ case ONLY_END_ENTITY: case PREFER_CRLS: case SOFT_FAIL: + case NO_FALLBACK: break; default: throw new CertPathValidatorException( "Unrecognized revocation parameter option: " + option); } } + softFail = options.contains(Option.SOFT_FAIL); // set mode, only end entity flag if (legacy) { mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS; onlyEE = rp.onlyEE; } else { - if (options.contains(Option.PREFER_CRLS)) { + if (options.contains(Option.NO_FALLBACK)) { + if (options.contains(Option.PREFER_CRLS)) { + mode = Mode.ONLY_CRLS; + } else { + mode = Mode.ONLY_OCSP; + } + } else if (options.contains(Option.PREFER_CRLS)) { mode = Mode.PREFER_CRLS; } onlyEE = options.contains(Option.ONLY_END_ENTITY); } - softFail = options.contains(Option.SOFT_FAIL); if (legacy) { crlDP = rp.crlDPEnabled; } else { crlDP = true; } - ocspResponses = getOCSPResponses(); - ocspExtensions = getOCSPExtensions(); + ocspResponses = getOcspResponses(); + ocspExtensions = getOcspExtensions(); this.anchor = anchor; this.params = params; @@ -297,14 +300,19 @@ if (forward) { throw new CertPathValidatorException("forward checking not supported"); + } + if (anchor != null) { + issuerCert = anchor.getTrustedCert(); + prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey() + : anchor.getCAPublicKey(); + } + crlSignFlag = true; + if (params.certPath() != null) { + certIndex = params.certPath().getCertificates().size() - 1; } else { - if (anchor != null) { - issuerCert = anchor.getTrustedCert(); - prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey() - : anchor.getCAPublicKey(); - } - crlSignFlag = true; + certIndex = -1; } + softFailExceptions.clear(); } @Override @@ -318,27 +326,34 @@ } @Override + public List getSoftFailExceptions() { + return Collections.unmodifiableList(softFailExceptions); + } + + @Override public void check(Certificate cert, Collection unresolvedCritExts) throws CertPathValidatorException { - X509Certificate xcert = (X509Certificate)cert; - if (onlyEE && xcert.getBasicConstraints() != -1) { - if (debug != null) { - debug.println("Skipping revocation check, not end entity cert"); - } - } else { - check(xcert, unresolvedCritExts, prevPubKey, crlSignFlag); - } - updateState(xcert); + check((X509Certificate)cert, unresolvedCritExts, + prevPubKey, crlSignFlag); } - void check(X509Certificate xcert, Collection unresolvedCritExts, - PublicKey pubKey, boolean crlSignFlag) + private void check(X509Certificate xcert, + Collection unresolvedCritExts, + PublicKey pubKey, boolean crlSignFlag) throws CertPathValidatorException { try { + if (onlyEE && xcert.getBasicConstraints() != -1) { + if (debug != null) { + debug.println("Skipping revocation check, not end " + + "entity cert"); + } + return; + } switch (mode) { case PREFER_OCSP: + case ONLY_OCSP: checkOCSP(xcert, unresolvedCritExts); break; case PREFER_CRLS: @@ -351,14 +366,17 @@ if (e.getReason() == BasicReason.REVOKED) { throw e; } - CertPathValidatorException cause = e; - if (softFail && e instanceof NetworkFailureException) { - if (mode == Mode.ONLY_CRLS) return; + boolean eSoftFail = isSoftFailException(e); + if (eSoftFail) { + if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) { + return; + } + } else { + if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) { + throw e; + } } - // Rethrow the exception if ONLY_CRLS - if (mode == Mode.ONLY_CRLS) { - throw e; - } + CertPathValidatorException cause = e; // Otherwise, failover if (debug != null) { debug.println("RevocationChecker.check() " + e.getMessage()); @@ -382,22 +400,35 @@ if (x.getReason() == BasicReason.REVOKED) { throw x; } - if (cause != null) { - if (softFail && cause instanceof NetworkFailureException) { - return; - } else { - cause.addSuppressed(x); + if (!isSoftFailException(x)) { + cause.addSuppressed(x); + throw cause; + } else { + // only pass if both exceptions were soft failures + if (!eSoftFail) { throw cause; } } - if (softFail && x instanceof NetworkFailureException) { - return; - } - throw x; } + } finally { + updateState(xcert); } } + private boolean isSoftFailException(CertPathValidatorException e) { + if (softFail && + e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS) + { + // recreate exception with correct index + CertPathValidatorException e2 = new CertPathValidatorException( + e.getMessage(), e.getCause(), params.certPath(), certIndex, + e.getReason()); + softFailExceptions.addFirst(e2); + return true; + } + return false; + } + private void updateState(X509Certificate cert) throws CertPathValidatorException { @@ -411,6 +442,9 @@ } prevPubKey = pubKey; crlSignFlag = certCanSignCrl(cert); + if (certIndex > 0) { + certIndex--; + } } // Maximum clock skew in milliseconds (15 minutes) allowed when checking @@ -446,8 +480,8 @@ " circular dependency"); } throw new CertPathValidatorException - ("Could not determine revocation status", null, null, -1, - BasicReason.UNDETERMINED_REVOCATION_STATUS); + ("Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } Set possibleCRLs = new HashSet<>(); @@ -457,7 +491,7 @@ CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW); // First, check user-specified CertStores - NetworkFailureException nfe = null; + CertPathValidatorException networkFailureException = null; for (CertStore store : certStores) { try { for (CRL crl : store.getCRLs(sel)) { @@ -468,10 +502,13 @@ debug.println("RevocationChecker.checkCRLs() " + "CertStoreException: " + e.getMessage()); } - if (softFail && nfe == null && + if (networkFailureException == null && CertStoreHelper.isCausedByNetworkIssue(store.getType(),e)) { // save this exception, we may need to throw it later - nfe = new NetworkFailureException(e); + networkFailureException = new CertPathValidatorException( + "Unable to determine revocation status due to " + + "network error", e, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } } } @@ -508,14 +545,17 @@ approvedCRLs.addAll(DistributionPointFetcher.getCRLs( sel, signFlag, prevKey, params.sigProvider(), certStores, - reasonsMask, anchors, params.date())); + reasonsMask, anchors, null)); } } catch (CertStoreException e) { - if (softFail && e instanceof CertStoreTypeException) { + if (e instanceof CertStoreTypeException) { CertStoreTypeException cste = (CertStoreTypeException)e; if (CertStoreHelper.isCausedByNetworkIssue(cste.getType(), e)) { - throw new NetworkFailureException(e); + throw new CertPathValidatorException( + "Unable to determine revocation status due to " + + "network error", e, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } } throw new CertPathValidatorException(e); @@ -531,26 +571,26 @@ stackedCerts); return; } catch (CertPathValidatorException cpve) { - if (nfe != null) { + if (networkFailureException != null) { // if a network issue previously prevented us from // retrieving a CRL from one of the user-specified - // CertStores and SOFT_FAIL is enabled, throw it now - // so it can be handled appropriately - throw nfe; + // CertStores, throw it now so it can be handled + // appropriately + throw networkFailureException; } throw cpve; } } else { - if (nfe != null) { + if (networkFailureException != null) { // if a network issue previously prevented us from // retrieving a CRL from one of the user-specified - // CertStores and SOFT_FAIL is enabled, throw it now - // so it can be handled appropriately - throw nfe; + // CertStores, throw it now so it can be handled + // appropriately + throw networkFailureException; } - throw new CertPathValidatorException - ("Could not determine revocation status", null, null, -1, - BasicReason.UNDETERMINED_REVOCATION_STATUS); + throw new CertPathValidatorException( + "Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } } } @@ -595,14 +635,9 @@ unresCritExts.remove(ReasonCode_Id.toString()); unresCritExts.remove(CertificateIssuer_Id.toString()); if (!unresCritExts.isEmpty()) { - if (debug != null) { - debug.println("Unrecognized " - + "critical extension(s) in revoked CRL entry: " - + unresCritExts); - } - throw new CertPathValidatorException - ("Could not determine revocation status", null, null, - -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); + throw new CertPathValidatorException( + "Unrecognized critical extension(s) in revoked " + + "CRL entry"); } } @@ -610,11 +645,14 @@ if (reasonCode == null) { reasonCode = CRLReason.UNSPECIFIED; } - Throwable t = new CertificateRevokedException - (entry.getRevocationDate(), reasonCode, - crl.getIssuerX500Principal(), entry.getExtensions()); - throw new CertPathValidatorException(t.getMessage(), t, - null, -1, BasicReason.REVOKED); + Date revocationDate = entry.getRevocationDate(); + if (revocationDate.before(params.date())) { + Throwable t = new CertificateRevokedException( + revocationDate, reasonCode, + crl.getIssuerX500Principal(), entry.getExtensions()); + throw new CertPathValidatorException( + t.getMessage(), t, null, -1, BasicReason.REVOKED); + } } } } @@ -630,9 +668,6 @@ throw new CertPathValidatorException(ce); } - URI responderURI = (this.responderURI != null) - ? this.responderURI : getOCSPServerURI(currCert); - X509Certificate respCert = (responderCert == null) ? issuerCert : responderCert; @@ -671,23 +706,38 @@ params.date(), nonce); } else { + URI responderURI = (this.responderURI != null) + ? this.responderURI + : OCSP.getResponderURI(currCert); + if (responderURI == null) { + throw new CertPathValidatorException( + "Certificate does not specify OCSP responder", null, + null, -1); + } + response = OCSP.check(Collections.singletonList(certId), - responderURI, respCert, params.date(), + responderURI, respCert, null, ocspExtensions); } } catch (IOException e) { - throw new CertPathValidatorException(e); + throw new CertPathValidatorException( + "Unable to determine revocation status due to network error", + e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } RevocationStatus rs = (RevocationStatus)response.getSingleResponse(certId); RevocationStatus.CertStatus certStatus = rs.getCertStatus(); if (certStatus == RevocationStatus.CertStatus.REVOKED) { - Throwable t = new CertificateRevokedException( - rs.getRevocationTime(), rs.getRevocationReason(), - respCert.getSubjectX500Principal(), rs.getSingleExtensions()); - throw new CertPathValidatorException(t.getMessage(), t, null, - -1, BasicReason.REVOKED); + Date revocationTime = rs.getRevocationTime(); + if (revocationTime.before(params.date())) { + Throwable t = new CertificateRevokedException( + revocationTime, rs.getRevocationReason(), + respCert.getSubjectX500Principal(), + rs.getSingleExtensions()); + throw new CertPathValidatorException(t.getMessage(), t, null, + -1, BasicReason.REVOKED); + } } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) { throw new CertPathValidatorException( "Certificate's revocation status is unknown", null, @@ -711,34 +761,6 @@ return hexNumber.toString(); } - private static URI getOCSPServerURI(X509CertImpl cert) - throws CertPathValidatorException - { - // Examine the certificate's AuthorityInfoAccess extension - AuthorityInfoAccessExtension aia = - cert.getAuthorityInfoAccessExtension(); - if (aia == null) { - throw new CertPathValidatorException( - "Must specify the location of an OCSP Responder"); - } - - List descriptions = aia.getAccessDescriptions(); - for (AccessDescription description : descriptions) { - if (description.getAccessMethod().equals((Object) - AccessDescription.Ad_OCSP_Id)) { - - GeneralName generalName = description.getAccessLocation(); - if (generalName.getType() == GeneralNameInterface.NAME_URI) { - URIName uri = (URIName)generalName.getName(); - return uri.getURI(); - } - } - } - - throw new CertPathValidatorException( - "Cannot find the location of the OCSP Responder"); - } - /** * Checks that a cert can be used to verify a CRL. * @@ -870,8 +892,8 @@ " circular dependency"); } throw new CertPathValidatorException - ("Could not determine revocation status", null, null, - -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); + ("Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } // Try to find another key that might be able to sign @@ -1067,6 +1089,15 @@ } } + @Override + public RevocationChecker clone() { + RevocationChecker copy = (RevocationChecker)super.clone(); + // we don't deep-copy the exceptions, but that is ok because they + // are never modified after they are instantiated + copy.softFailExceptions = new LinkedList<>(softFailExceptions); + return copy; + } + /* * This inner class extends the X509CertSelector to add an additional * check to make sure the subject public key isn't on a particular list. diff -r d9558f046caa -r bc9a25fff6c5 jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java --- a/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Thu Jul 25 10:58:00 2013 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -422,7 +422,6 @@ buildParams.anyPolicyInhibited(), buildParams.policyQualifiersRejected(), rootNode); - checkers.add(policyChecker); // add the algorithm checker @@ -455,11 +454,16 @@ List 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 it's our own, initialize it - if (ckr instanceof RevocationChecker) + if (ckr instanceof RevocationChecker) { ((RevocationChecker)ckr).init(builder.trustAnchor, buildParams); + } } } // only add a RevocationChecker if revocation is enabled and diff -r d9558f046caa -r bc9a25fff6c5 jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.java --- a/jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.java Tue Jun 11 14:09:06 2013 +0100 +++ b/jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.java Thu Jul 25 10:58:00 2013 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /** * @test - * @bug 6854712 7171570 + * @bug 6854712 7171570 8010748 * @summary Basic unit test for PKIXRevocationChecker */ @@ -32,19 +32,9 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URI; -import java.security.cert.CertificateFactory; -import java.security.cert.CertPathBuilder; -import java.security.cert.CertPathChecker; -import java.security.cert.CertPathValidator; -import java.security.cert.Extension; -import java.security.cert.PKIXRevocationChecker; +import java.security.cert.*; import java.security.cert.PKIXRevocationChecker.Option; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class UnitTest { @@ -56,22 +46,23 @@ System.out.println("Testing that get methods return null or " + "empty lists/sets/maps"); - requireNull(prc.getOCSPResponder(), "getOCSPResponder()"); - requireNull(prc.getOCSPResponderCert(), "getOCSPResponderCert()"); - requireEmpty(prc.getOCSPExtensions(), "getOCSPExtensions()"); - requireEmpty(prc.getOCSPResponses(), "getOCSPResponses()"); + requireNull(prc.getOcspResponder(), "getOcspResponder()"); + requireNull(prc.getOcspResponderCert(), "getOcspResponderCert()"); + requireEmpty(prc.getOcspExtensions(), "getOcspExtensions()"); + requireEmpty(prc.getOcspResponses(), "getOcspResponses()"); requireEmpty(prc.getOptions(), "getOptions()"); + requireEmpty(prc.getSoftFailExceptions(), "getSoftFailExceptions()"); System.out.println("Testing that get methods return same parameters " + "that are passed to set methods"); URI uri = new URI("http://localhost"); - prc.setOCSPResponder(uri); - requireEquals(uri, prc.getOCSPResponder(), "getOCSPResponder()"); + prc.setOcspResponder(uri); + requireEquals(uri, prc.getOcspResponder(), "getOcspResponder()"); X509Certificate cert = getCert(); - prc.setOCSPResponderCert(cert); - requireEquals(cert, prc.getOCSPResponderCert(), - "getOCSPResponderCert()"); + prc.setOcspResponderCert(cert); + requireEquals(cert, prc.getOcspResponderCert(), + "getOcspResponderCert()"); List exts = new ArrayList<>(); for (String oid : cert.getNonCriticalExtensionOIDs()) { @@ -79,8 +70,8 @@ exts.add(new ExtensionImpl(oid, cert.getExtensionValue(oid), false)); } - prc.setOCSPExtensions(exts); - requireEquals(exts, prc.getOCSPExtensions(), "getOCSPExtensions()"); + prc.setOcspExtensions(exts); + requireEquals(exts, prc.getOcspExtensions(), "getOcspExtensions()"); Set