8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option
authormullan
Thu, 25 Jul 2013 10:58:00 -0400
changeset 19045 bc9a25fff6c5
parent 18161 d9558f046caa
child 19046 6f60d5707c89
8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option Reviewed-by: vinnie
jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java
jdk/src/share/classes/sun/security/provider/certpath/OCSP.java
jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java
jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java
jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java
jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java
jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java
jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.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.
  *
  * <p>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}.
  *
  * <p>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 <a href="http://www.ietf.org/rfc/rfc2560.txt"><i>RFC&nbsp;2560: X.509
+ * Internet Public Key Infrastructure Online Certificate Status Protocol -
+ * OCSP</i></a>, <br><a
+ * href="http://www.ietf.org/rfc/rfc5280.txt"><i>RFC&nbsp;5280: Internet X.509
+ * Public Key Infrastructure Certificate and Certificate Revocation List (CRL)
+ * Profile</i></a>
  */
 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<Extension> extensions)
+    public void setOcspExtensions(List<Extension> extensions)
     {
         this.ocspExtensions = (extensions == null)
                               ? Collections.<Extension>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<Extension> getOCSPExtensions() {
+    public List<Extension> 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<X509Certificate, byte[]> responses)
+    public void setOcspResponses(Map<X509Certificate, byte[]> responses)
     {
         if (responses == null) {
             this.ocspResponses = Collections.<X509Certificate, byte[]>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<X509Certificate, byte[]> getOCSPResponses() {
+    public Map<X509Certificate, byte[]> getOcspResponses() {
         Map<X509Certificate, byte[]> copy = new HashMap<>(ocspResponses.size());
         for (Map.Entry<X509Certificate, byte[]> 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<Option> getOptions() {
         return Collections.unmodifiableSet(options);
     }
 
+    /**
+     * Returns a list containing the exceptions that are ignored by the
+     * revocation checker when the {@link Option#SOFT_FAIL SOFT_FAIL} option
+     * is set. The list is cleared each time {@link #init init} is called.
+     * The list is ordered in ascending order according to the certificate
+     * index returned by {@link CertPathValidatorException#getIndex getIndex}
+     * method of each entry.
+     * <p>
+     * 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<CertPathValidatorException> 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:
+         * <p><ul>
+         *  <li>The CRL or OCSP response cannot be obtained because of a
+         *      network error.
+         *  <li>The OCSP responder returns one of the following errors
+         *      specified in section 2.3 of RFC 2560: internalError, tryLater,
+         *      or unauthorized.
+         * </ul><br>
+         * 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
     }
--- 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<String, Extension> 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;
-        }
-    }
 }
--- 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
--- 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<PKIXCertPathChecker> 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
--- 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;
             }
         }
--- 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<X509Certificate, byte[]> ocspResponses;
     private List<Extension> ocspExtensions;
     private boolean legacy;
+    private LinkedList<CertPathValidatorException> 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<CertPathValidatorException> getSoftFailExceptions() {
+        return Collections.unmodifiableList(softFailExceptions);
+    }
+
+    @Override
     public void check(Certificate cert, Collection<String> 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<String> unresolvedCritExts,
-               PublicKey pubKey, boolean crlSignFlag)
+    private void check(X509Certificate xcert,
+                       Collection<String> 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<X509CRL> 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<AccessDescription> 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.
--- 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<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 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
--- 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<Extension> 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<Option> options = EnumSet.of(Option.ONLY_END_ENTITY);
         prc.setOptions(options);
@@ -88,14 +79,14 @@
 
         System.out.println("Testing that parameters are re-initialized to " +
                            "default values if null is passed to set methods");
-        prc.setOCSPResponder(null);
-        requireNull(prc.getOCSPResponder(), "getOCSPResponder()");
-        prc.setOCSPResponderCert(null);
-        requireNull(prc.getOCSPResponderCert(), "getOCSPResponderCert()");
-        prc.setOCSPExtensions(null);
-        requireEmpty(prc.getOCSPExtensions(), "getOCSPExtensions()");
-        prc.setOCSPResponses(null);
-        requireEmpty(prc.getOCSPResponses(), "getOCSPResponses()");
+        prc.setOcspResponder(null);
+        requireNull(prc.getOcspResponder(), "getOcspResponder()");
+        prc.setOcspResponderCert(null);
+        requireNull(prc.getOcspResponderCert(), "getOcspResponderCert()");
+        prc.setOcspExtensions(null);
+        requireEmpty(prc.getOcspExtensions(), "getOcspExtensions()");
+        prc.setOcspResponses(null);
+        requireEmpty(prc.getOcspResponses(), "getOcspResponses()");
         prc.setOptions(null);
         requireEmpty(prc.getOptions(), "getOptions()");