8225436: Stapled OCSPResponses should be added to PKIXRevocationChecker irrespective of revocationEnabled flag
authorjnimeh
Thu, 22 Aug 2019 14:09:36 -0700
changeset 57849 e1269de19aa5
parent 57847 bc14eec6f4bc
child 57850 b2b77f6922dc
8225436: Stapled OCSPResponses should be added to PKIXRevocationChecker irrespective of revocationEnabled flag Reviewed-by: mullan
src/java.base/share/classes/sun/security/validator/PKIXValidator.java
test/jdk/sun/security/validator/PKIXValAndRevCheckTests.java
--- a/src/java.base/share/classes/sun/security/validator/PKIXValidator.java	Thu Aug 22 20:22:29 2019 +0200
+++ b/src/java.base/share/classes/sun/security/validator/PKIXValidator.java	Thu Aug 22 14:09:36 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -143,6 +143,7 @@
         return subjectMap;
     }
 
+    @Override
     public Collection<X509Certificate> getTrustedCertificates() {
         return trustedCerts;
     }
@@ -399,55 +400,67 @@
      */
     private static void addResponses(PKIXBuilderParameters pkixParams,
             X509Certificate[] chain, List<byte[]> responseList) {
+        try {
+            boolean createdRevChk = false;
 
-        if (pkixParams.isRevocationEnabled()) {
-            try {
-                // Make a modifiable copy of the CertPathChecker list
-                PKIXRevocationChecker revChecker = null;
-                List<PKIXCertPathChecker> checkerList =
-                        new ArrayList<>(pkixParams.getCertPathCheckers());
+            // Obtain the current CertPathChecker list
+            PKIXRevocationChecker revChecker = null;
+            List<PKIXCertPathChecker> checkerList =
+                    pkixParams.getCertPathCheckers();
 
-                // Find the first PKIXRevocationChecker in the list
-                for (PKIXCertPathChecker checker : checkerList) {
-                    if (checker instanceof PKIXRevocationChecker) {
-                        revChecker = (PKIXRevocationChecker)checker;
-                        break;
-                    }
+            // Find the first PKIXRevocationChecker in the list
+            for (PKIXCertPathChecker checker : checkerList) {
+                if (checker instanceof PKIXRevocationChecker) {
+                    revChecker = (PKIXRevocationChecker)checker;
+                    break;
                 }
+            }
 
-                // If we still haven't found one, make one
-                if (revChecker == null) {
+            // If we still haven't found one, make one, unless revocation
+            // is disabled - then there's no point adding OCSP responses.
+            // If a PKIXRevocationChecker was added externally, then we
+            // must add the responses since revocation checking is performed
+            // independent of the revocation flag (per the
+            // PKIXRevocationChecker spec).
+            if (revChecker == null) {
+                if (pkixParams.isRevocationEnabled()) {
                     revChecker = (PKIXRevocationChecker)CertPathValidator.
                             getInstance("PKIX").getRevocationChecker();
-                    checkerList.add(revChecker);
+                    createdRevChk = true;
+                } else {
+                    return;
                 }
+            }
 
-                // Each response in the list should be in parallel with
-                // the certificate list.  If there is a zero-length response
-                // treat it as being absent.  If the user has provided their
-                // own PKIXRevocationChecker with pre-populated responses, do
-                // not overwrite them with the ones from the handshake.
-                Map<X509Certificate, byte[]> responseMap =
-                        revChecker.getOcspResponses();
-                int limit = Integer.min(chain.length, responseList.size());
-                for (int idx = 0; idx < limit; idx++) {
-                    byte[] respBytes = responseList.get(idx);
-                    if (respBytes != null && respBytes.length > 0 &&
-                            !responseMap.containsKey(chain[idx])) {
-                        responseMap.put(chain[idx], respBytes);
-                    }
+            // Each response in the list should be in parallel with
+            // the certificate list.  If there is a zero-length response
+            // treat it as being absent.  If the user has provided their
+            // own PKIXRevocationChecker with pre-populated responses, do
+            // not overwrite them with the ones from the handshake.
+            Map<X509Certificate, byte[]> responseMap =
+                    revChecker.getOcspResponses();
+            int limit = Integer.min(chain.length, responseList.size());
+            for (int idx = 0; idx < limit; idx++) {
+                byte[] respBytes = responseList.get(idx);
+                if (respBytes != null && respBytes.length > 0 &&
+                        !responseMap.containsKey(chain[idx])) {
+                    responseMap.put(chain[idx], respBytes);
                 }
+            }
+            revChecker.setOcspResponses(responseMap);
 
-                // Add the responses and push it all back into the
-                // PKIXBuilderParameters
-                revChecker.setOcspResponses(responseMap);
+            // Add the responses and push it all back into the
+            // PKIXBuilderParameters
+            if (createdRevChk) {
+                pkixParams.addCertPathChecker(revChecker);
+            } else {
                 pkixParams.setCertPathCheckers(checkerList);
-            } catch (NoSuchAlgorithmException exc) {
-                // This should not occur, but if it does happen then
-                // stapled OCSP responses won't be part of revocation checking.
-                // Clients can still fall back to other means of revocation
-                // checking.
             }
+        } catch (NoSuchAlgorithmException exc) {
+            // This should not occur, but if it does happen then
+            // stapled OCSP responses won't be part of revocation checking.
+            // Clients can still fall back to other means of revocation
+            // checking.
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/validator/PKIXValAndRevCheckTests.java	Thu Aug 22 14:09:36 2019 -0700
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8225436
+ * @summary Stapled OCSPResponses should be added to PKIXRevocationChecker
+ *          irrespective of revocationEnabled flag
+ * @library /test/lib
+ * @modules java.base/sun.security.validator
+ * @build jdk.test.lib.Convert
+ * @run main PKIXValAndRevCheckTests
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import sun.security.validator.Validator;
+
+public class PKIXValAndRevCheckTests {
+
+    // subject: CN=Good Server,O=TestPKI
+    // issuer: CN=CA1 Intermediate,O=TestPKI
+    // serial: 01000015
+    // notBefore: Aug 16 02:42:32 2019 GMT
+    // notAfter: Aug 15 02:42:32 2020 GMT
+    static final String GOOD_SERVER_PEM =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIDjTCCAnWgAwIBAgIEAQAAFTANBgkqhkiG9w0BAQsFADAtMRAwDgYDVQQKDAdU\n" +
+        "ZXN0UEtJMRkwFwYDVQQDDBBDQTEgSW50ZXJtZWRpYXRlMB4XDTE5MDgxNjAyNDIz\n" +
+        "MloXDTIwMDgxNTAyNDIzMlowKDEQMA4GA1UECgwHVGVzdFBLSTEUMBIGA1UEAwwL\n" +
+        "R29vZCBTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSQSUF\n" +
+        "L5th5P21Vijy5pm0WnC0AWCSHX5003f9um70k/IdaAg2rsj/aKHnsm+r4xXGD236\n" +
+        "S7DxBR2w8NTnAofgRWlsAn74lWQhV2p3SU/JKEtFbJV1YAnNOUPKsCnVKDfe3Gev\n" +
+        "zxOLpZ/VKSx9u20bOUbh6QxqlIdIuJ6AW/cgyjdvuN16sIWGWzl17lm81T1cy89x\n" +
+        "TvvsHHqfAh+y3jMwqvIRxoaNQoOjcmxSldRnCwBfhg8xHxB4wKa4z+6Y3gndzne1\n" +
+        "Ms0itbtdYlSF3ADOtwoBrftYDpvsG8VhA4x4QqFAAKx1FPO6OJBYGNfZvnoDDi9g\n" +
+        "i0PgDNftm0l/6FGlAgMBAAGjgbkwgbYwHQYDVR0OBBYEFJNBzLRxgb0znmeuYXc3\n" +
+        "UaFGd9m3MB8GA1UdIwQYMBaAFJraQNM+W62lwqzcSEc6VjNXAaSaMA4GA1UdDwEB\n" +
+        "/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEwYDVR0RBAww\n" +
+        "CoIIdGVyaXlha2kwMAYIKwYBBQUHAQEEJDAiMCAGCCsGAQUFBzABhhRodHRwOi8v\n" +
+        "dGVyaXlha2k6NTIwMDANBgkqhkiG9w0BAQsFAAOCAQEAadl0EQdBJw6dBBelxtox\n" +
+        "id18HMt+MsXssHun1nx8My0VZ3uQBNJ4GgKipNFa+s8nPZIzRr0Ls65dIUiBcg3R\n" +
+        "ep0he/gnkaowRRxGqMkALl3VzUz8INSRzdCIVm0EBeDCaHGLzE6G3uIqWwldei8k\n" +
+        "IOHtiVLESAJvCvSEOAnoJHRVD8+tbEIxRsSFkoKGqc5U7bsCVC5uSXOkiHEP/3zm\n" +
+        "6YixiT+hLk6QKegkQxQPZ+irGBeN2q2PAq5vTh1hJDciwqE3h8GxZ15iR3WIedc8\n" +
+        "6EHJ7+N27nWZLtFgcLKNXEsm1Eh/YNIrpeN0OQBGSLD3lIju5IO0mD3oQfA4miqT\n" +
+        "wQ==\n" +
+        "-----END CERTIFICATE-----";
+
+    // subject: CN=Bad Server,O=TestPKI
+    // issuer: CN=CA1 Intermediate,O=TestPKI
+    // serial: 01000016
+    // notBefore: Aug 16 02:43:11 2019 GMT
+    // notAfter: Aug 15 02:43:11 2020 GMT
+    static final String BAD_SERVER_PEM =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIDjDCCAnSgAwIBAgIEAQAAFjANBgkqhkiG9w0BAQsFADAtMRAwDgYDVQQKDAdU\n" +
+        "ZXN0UEtJMRkwFwYDVQQDDBBDQTEgSW50ZXJtZWRpYXRlMB4XDTE5MDgxNjAyNDMx\n" +
+        "MVoXDTIwMDgxNTAyNDMxMVowJzEQMA4GA1UECgwHVGVzdFBLSTETMBEGA1UEAwwK\n" +
+        "QmFkIFNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9syEok\n" +
+        "K/8E/hm8Q/cLhSwirDIGFC9nqS8p1bVNTClkMsqxkQAcQptP1zLZiMBdgLjOH3cF\n" +
+        "60UAaz2Y+7WYU5MB6AE8IloDgUUKKUTUmXHzM31OiSVu21+ooo59XzV/cCEu+Qlu\n" +
+        "AiaDuTDhIEtM58zs/3RZN0h+v8M2NXUU4bwYmYVeqP8UW9BEjgznIIrvGpqpHKz5\n" +
+        "EwctL+u/h5Z/DoCOnVq3irMCpInY5/VbIuxfkdfawsFROzUWl6fZ3+CTfQfHhKSM\n" +
+        "sz1/zY/BtQLDTKY120M2FaLmmIoOLrqZo8Pi+JL8IVentNfSHvUX5rrnPKB2/JVS\n" +
+        "8Jc2qvLPk4PWbwECAwEAAaOBuTCBtjAdBgNVHQ4EFgQU8z9qWpJ/FDmKOgQI2vY7\n" +
+        "0OwCNFEwHwYDVR0jBBgwFoAUmtpA0z5braXCrNxIRzpWM1cBpJowDgYDVR0PAQH/\n" +
+        "BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjATBgNVHREEDDAK\n" +
+        "ggh0ZXJpeWFraTAwBggrBgEFBQcBAQQkMCIwIAYIKwYBBQUHMAGGFGh0dHA6Ly90\n" +
+        "ZXJpeWFraTo1MjAwMA0GCSqGSIb3DQEBCwUAA4IBAQBzi8U/3b6hfGwE/przqyha\n" +
+        "Y40Nhh1uCm1rz4bZ27z2Q3vzlg2ay4V3I2NaR4eY/wsuO8AW0qdBJExmYqgi+l9U\n" +
+        "S6i9WqyI22jAKUPsx9WmCZltyU589VDU40h2g6C4+8VnOZm6OKKKTjkKrDn/IFJF\n" +
+        "jU4yIvXrEBHNJr/tcQW0+dF2okIBAnVLUNs8CZZJyWesQtu6J0OBj4tE8s0ET4ep\n" +
+        "XC/3mZkGjziEZw8/dDZ0/+CQbrkDP2vs6iNjz/LUIA9dVXUs9sNeqW+VEHI3vZvJ\n" +
+        "gYVDJn5tWZSIY/O2zV97dz9VeDH3aukuoEm5aAxxhazxRDntcnl2DYrrr2bGuS2Y\n" +
+        "-----END CERTIFICATE-----";
+
+    // subject: CN=CA1 Intermediate,O=TestPKI
+    // issuer: CN=TestRoot,O=TestPKI
+    // serial: 0100
+    // notBefore: May  6 06:00:00 2015 GMT
+    // notAfter: Jan 21 12:00:00 2025 GMT
+    static final String INT_CA_PEM =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIEbTCCAlWgAwIBAgICAQAwDQYJKoZIhvcNAQELBQAwJTEQMA4GA1UECgwHVGVz\n" +
+        "dFBLSTERMA8GA1UEAwwIVGVzdFJvb3QwHhcNMTUwNTA2MDYwMDAwWhcNMjUwMTIx\n" +
+        "MTIwMDAwWjAtMRAwDgYDVQQKDAdUZXN0UEtJMRkwFwYDVQQDDBBDQTEgSW50ZXJt\n" +
+        "ZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtdKjBpeuJJEv\n" +
+        "di4wMGHE5y7inXuDvMCjkjFRv9XOH20BVAIDMTMeIByk6NQJYeeaTRGXTawZN8/c\n" +
+        "aXtQuqsRGz/q2va/I+A5HIvtu+vujdVksu2baafGM0Ql8Gdzj8MdLGb+kGFji/FX\n" +
+        "f+2PL8UfpnmUikLN728lF9bzcA046I8B43SriFJeYOlLPfE/yjNg5eccdMPDBw7h\n" +
+        "KQPVbXfpcmWRJm/vGlCR38Rd7ceYF3/ctf/0J8Dab7q98ITpH9q5NFD+o2NJZoFq\n" +
+        "7HBPdGTIJ73m3WPzLRrU+JPD7xs9wgmuuRq6hU/lPSd5IJSkJ/cyXkma1RwBO4Lm\n" +
+        "rU2aWDGhNwIDAQABo4GeMIGbMB0GA1UdDgQWBBSa2kDTPlutpcKs3EhHOlYzVwGk\n" +
+        "mjAfBgNVHSMEGDAWgBTwWIIuUEAneAXJeud3ioakmTg32zAPBgNVHRMBAf8EBTAD\n" +
+        "AQH/MA4GA1UdDwEB/wQEAwIBhjA4BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGG\n" +
+        "HGh0dHA6Ly9qaWFuLm9zdGFwbGUub3JnOjcxMDAwDQYJKoZIhvcNAQELBQADggIB\n" +
+        "ADRoginKFigLOKz1NJN86w66eP3r4D/5Qq8+G9DiasmThLQfaVYBvuaR9kL3D9Vr\n" +
+        "1EEXRGmCxMTHetW0SQ/SsMeRbBf8Ihck4MOeAC9cMysvtNfjpwxaAh6zF5bX4pjj\n" +
+        "33gJpjPLNAZru09rSF0GIo9CxPh9rBOkmttrnPDX7rLR9962i/P4KHyHknGM7gY0\n" +
+        "U88ddugkANiFIiAfBRGFz3AqMiMi3VP5STCP0k0ab/frkev6C/qq3th4gQ/Bog/5\n" +
+        "YaoWvzGAs7QoQ7+r0BIRZhG71WQKD4Yx1a43RnG3tFPLFznk0odeh8sr/CI3H/+b\n" +
+        "eyyJLd02ApujZoAfMHzTcq/27mO1ZvA5qSt4wsb7gswnIYwXbJZBBRoixGFD7VP0\n" +
+        "NEXREljpEuGIIy2lkHb5wNV3OEMmAmoKwx1GXWXRfQRHqn1f2/XLYInDg0u9u+G6\n" +
+        "UX3edn6rwP+vlIX2Cx4qC/yX4zg7YxMXCwrol91/7wugkUGPjmU6qmK+TtuwZNQG\n" +
+        "2wtCB4FJXa0YZyDd7U/FH7nWZtG9BgzpLit90hC4+m5V4E/7I6slvwxpkE7y0Nju\n" +
+        "tjy/qcuil6imrOR/apuwT1ecAmyjm1UmpKPLLzYnE6AtSKOTndGa2iNyPDrseFLy\n" +
+        "7TUF/fg/dvZ46OmouSX3upAFRnvpXYXwSQRQ2S+wEnbp\n" +
+        "-----END CERTIFICATE-----";
+
+    // subject: CN=TestRoot,O=TestPKI
+    // issuer: CN=TestRoot,O=TestPKI
+    // serial: 01
+    // notBefore: May  6 00:36:03 2015 GMT
+    // notAfter: Jan 21 00:36:03 2035 GMT
+    static final String ROOT_CA_PEM =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIFKDCCAxCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAlMRAwDgYDVQQKDAdUZXN0\n" +
+        "UEtJMREwDwYDVQQDDAhUZXN0Um9vdDAeFw0xNTA1MDYwMDM2MDNaFw0zNTAxMjEw\n" +
+        "MDM2MDNaMCUxEDAOBgNVBAoMB1Rlc3RQS0kxETAPBgNVBAMMCFRlc3RSb290MIIC\n" +
+        "IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLCcVhyIaPV5CHjvnyAvK978\n" +
+        "TUC2YY5wZ8e21L8C+SvxCoE5U66H+wMsNIC90i1ynlz49G4oKR67GXcijJpVD1fA\n" +
+        "Dq3Hpc3WDY9/5jRKWZOC0qLmXMPEF8wrwyC3aQ81sytDJOhEfxEf3KvwFDI9NUQb\n" +
+        "tFdWB+IDEvaDCTJgOt/jIJAzLTxzvwPBzP/JHdRCwKdmlQStRp20AmDtpgIlm2RH\n" +
+        "v8ywabI/UqncZHe/LVYdmDNxztziM98Zs1I7vsO2/yebWE/QH3g3k9ZgaT6UnBAq\n" +
+        "gvV2TQhZOGMmps7RrfNdVEHeeRXmJTFAtmbi/o6Ou7xli+3bDuY5Faxk7uOpC54H\n" +
+        "iyyH2Htoyc9A0M9qwkwnrKxlWe594uD9LbWMNBMMTv4nUtf1ZE1swHg/L9XATDa/\n" +
+        "ZB5hL6p/oS2CxloLL982CIbSuV1TcI6s4naTyZ3HxnIKCaOijAK+IDo9qbTFkt9w\n" +
+        "4toc09fWGRV/pgm3p6YptP48JDYTHQK8GvjzQIdALXee28BmM496cV49uo1O6ia0\n" +
+        "Ht1MFMDKav2g9Cr5SYKIFkpZjJ2T0aJ4dLeft+nQCwDP4odHRBTQbqK9oMw6qYav\n" +
+        "PVuZJWwW3ilZtke2D28N4bF2X1nMYFM2obnB/TLkpreNSiyV6M0D2DW8tpGLTXOp\n" +
+        "yZEJqAx2dEhfxRNE7sECAwEAAaNjMGEwHQYDVR0OBBYEFPBYgi5QQCd4Bcl653eK\n" +
+        "hqSZODfbMB8GA1UdIwQYMBaAFPBYgi5QQCd4Bcl653eKhqSZODfbMA8GA1UdEwEB\n" +
+        "/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBjCVYc\n" +
+        "0PmSjzxYGfXtR6+JaDrA/1jhkTPTVTqVniKV4beqoX90BE9krvic7nfvxoYecNux\n" +
+        "b1ZZkXMZ0NVadjxySaFmHo9E5eoPWp2N0Jb5rV+imlofz+U9/lANTb9QJ4L+qUwL\n" +
+        "s40zai1i5yvt4ZcowBRy2BohF2qgaFy8rd+amqVd8LnG06HIOJjZYKgKd2EJyQR6\n" +
+        "F6SPPuSfK5wpkBnkTUvtsvx4+8MKUfKRThGQkovSbXxfusAWehe9XT5hCgG2s04J\n" +
+        "7rL1LVrviBXPFaQgbIGov0PubCJM6V6GFwNJxqVLxbFS0mN+z9M6JzJM5SRF/Ki5\n" +
+        "daczIOGNELVbCct/4aaWeHYwXfnJo/EghAdbS2DPvESXQhNsuW6KYTl6Qhzu3UHw\n" +
+        "yaEIOh2LYENhKJq91Ww6Xhk9seGuwIsj6HXS30lrRztDM+GPU44WQxhmSUwY0C9g\n" +
+        "+KydH1c71eH5vBG3ODKsqBtFkHVD0qhm3Oa33uyUOdvNeRyIQzXSH9QJPXbJURqD\n" +
+        "TRNWmLG4eEIGIFCYyuaBSeCKrvPyiUXR0p9XQjOJVuCQPr8pfW483/BtlzAa6v3r\n" +
+        "jDOoB5v4FaC57HFt8aMrf/B3KGtH/PBpdRSAAIWAIwt9sbTq8nzhCIFhxJTiRWxQ\n" +
+        "uvSM40WEaUsmfpxU+tF2LJvWmNNbDDtEmbFsQQ==\n" +
+        "-----END CERTIFICATE-----";
+
+    // OCSP Response Status: successful (0x0)
+    // Response Type: Basic OCSP Response
+    // Version: 1 (0x0)
+    // Responder Id: O = TestPKI, CN = TestRoot
+    // Produced At: Aug 16 06:06:27 2019 GMT
+    // Responses:
+    // Certificate ID:
+    //   Hash Algorithm: sha1
+    //   Issuer Name Hash: 622C4B816C42E2E99FF41B5CED388DAA33A6B9B3
+    //   Issuer Key Hash: F058822E5040277805C97AE7778A86A4993837DB
+    //   Serial Number: 0100
+    // Cert Status: good
+    // This Update: Aug 16 06:06:27 2019 GMT
+    // Next Update: Aug 17 06:06:27 2019 GMT
+    static final String INT_CA_OCSP_PEM =
+        "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";
+
+    // OCSP Response Status: successful (0x0)
+    // Response Type: Basic OCSP Response
+    // Version: 1 (0x0)
+    // Responder Id: O = TestPKI, CN = CA1 Intermediate
+    // Produced At: Aug 16 05:03:09 2019 GMT
+    // Responses:
+    // Certificate ID:
+    //   Hash Algorithm: sha1
+    //   Issuer Name Hash: FE48D59BAF624773549AE209AA14FD20DCE6B8F4
+    //   Issuer Key Hash: 9ADA40D33E5BADA5C2ACDC48473A56335701A49A
+    //   Serial Number: 01000015
+    // Cert Status: good
+    // This Update: Aug 16 05:03:09 2019 GMT
+    // Next Update: Aug 17 05:03:09 2019 GMT
+    static final String GOOD_GUY_OCSP_PEM =
+        "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";
+
+    // OCSP Response Status: successful (0x0)
+    // Response Type: Basic OCSP Response
+    // Version: 1 (0x0)
+    // Responder Id: O = TestPKI, CN = CA1 Intermediate
+    // Produced At: Aug 16 05:03:27 2019 GMT
+    // Responses:
+    // Certificate ID:
+    //   Hash Algorithm: sha1
+    //   Issuer Name Hash: FE48D59BAF624773549AE209AA14FD20DCE6B8F4
+    //   Issuer Key Hash: 9ADA40D33E5BADA5C2ACDC48473A56335701A49A
+    //   Serial Number: 01000016
+    // Cert Status: revoked
+    // Revocation Time: Aug 16 04:59:31 2019 GMT
+    // Revocation Reason: keyCompromise (0x1)
+    // This Update: Aug 16 05:03:27 2019 GMT
+    // Next Update: Aug 17 05:03:27 2019 GMT
+    static final String BAD_GUY_OCSP_PEM =
+        "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\n" +
+        "BFDZL7ASduk=";
+
+    // Saturday, August 17, 2019 2:00:00 AM GMT
+    static final Date VALID_DATE = new Date(1566007200000L);
+
+    public static void main(String[] args) throws Exception {
+        CertificateFactory certFac = CertificateFactory.getInstance("X.509");
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
+        X509Certificate goodGuyCert = getCert(certFac, GOOD_SERVER_PEM);
+        X509Certificate badGuyCert = getCert(certFac, BAD_SERVER_PEM);
+        X509Certificate intCACert = getCert(certFac, INT_CA_PEM);
+        X509Certificate rootCACert = getCert(certFac, ROOT_CA_PEM);
+        byte[] goodOcspDer = pemToDer(GOOD_GUY_OCSP_PEM);
+        byte[] badOcspDer = pemToDer(BAD_GUY_OCSP_PEM);
+        byte[] intCAOcspDer = pemToDer(INT_CA_OCSP_PEM);
+        Set<TrustAnchor> trustAnchors =
+                Set.of(new TrustAnchor(rootCACert, null));
+        PKIXRevocationChecker pkrc;
+        PKIXBuilderParameters pkixParams;
+
+        X509Certificate[] goodPath = { goodGuyCert, intCACert,
+            rootCACert };
+        X509Certificate[] badPath = { badGuyCert, intCACert,
+            rootCACert };
+
+        List<byte[]> goodResponses = List.of(goodOcspDer, intCAOcspDer);
+        List<byte[]> badResponses = List.of(badOcspDer, intCAOcspDer);
+
+        // Test 1: Path validation with revocation explicitly turned
+        // off, expected to pass.
+        pkixParams = new PKIXBuilderParameters(trustAnchors, null);
+        pkixParams.setRevocationEnabled(false);
+        validatePath(goodPath, Collections.emptyList(), pkixParams, null);
+
+        // Test 2: Path validation with revocation turned on using the
+        // good path.  Should fail due to no responses.
+        pkixParams = new PKIXBuilderParameters(trustAnchors, null);
+        pkixParams.setDate(VALID_DATE);
+        validatePath(goodPath, Collections.emptyList(), pkixParams,
+            new CertPathValidatorException("KABOOM! No OCSP Responses!", null,
+            certFac.generateCertPath(List.of(goodGuyCert, intCACert)), 1,
+            BasicReason.UNDETERMINED_REVOCATION_STATUS));
+
+        // Test 3: Path validation of the good path with revocation turned
+        // on and available valid OCSP responses delivered through the
+        // List<byte[]> rather than via the PKIXRevocationChecker.
+        pkixParams = new PKIXBuilderParameters(trustAnchors, null);
+        pkixParams.setDate(VALID_DATE);
+        pkrc = (PKIXRevocationChecker)cpv.getRevocationChecker();
+        pkixParams.addCertPathChecker(pkrc);
+        validatePath(goodPath, goodResponses, pkixParams, null);
+
+        // Test 4: Path validation of the bad path with revocation explicitly
+        // disabled and valid OCSP responses provided through the PKIXRevocationChecker
+        // indicating a revoked certificate.  Even with the
+        // setRevocationEnabled method set to false this should perform
+        // revocation checking and catch the revoked certificate.
+        pkixParams = new PKIXBuilderParameters(trustAnchors, null);
+        pkixParams.setDate(VALID_DATE);
+        pkixParams.setRevocationEnabled(false);
+        pkrc = (PKIXRevocationChecker)cpv.getRevocationChecker();
+        pkrc.setOcspResponses(Map.of(badGuyCert, badOcspDer,
+                intCACert, intCAOcspDer));
+        pkixParams.addCertPathChecker(pkrc);
+        validatePath(badPath, Collections.emptyList(), pkixParams,
+                new CertPathValidatorException("Ouch!", null,
+                certFac.generateCertPath(List.of(badGuyCert, intCACert)), 0,
+                BasicReason.REVOKED));
+
+        // Test 5: This is the same basic setup as test 4, but instead of
+        // delivering the OCSP responses via the PKIXRevocationChecker use
+        // the third parameter (List<byte[]>) for the Validator.validate()
+        // call.  Revocation checking should be performed.
+        pkixParams = new PKIXBuilderParameters(trustAnchors, null);
+        pkixParams.setDate(VALID_DATE);
+        pkixParams.setRevocationEnabled(false);
+        pkrc = (PKIXRevocationChecker)cpv.getRevocationChecker();
+        pkixParams.addCertPathChecker(pkrc);
+        validatePath(badPath, badResponses, pkixParams,
+                new CertPathValidatorException("Ouch!", null,
+                certFac.generateCertPath(List.of(badGuyCert, intCACert)), 0,
+                BasicReason.REVOKED));
+    }
+
+    static void validatePath(X509Certificate[] path, List<byte[]> responses,
+            PKIXBuilderParameters params, Exception expectedExc) {
+        try {
+            Validator val = Validator.getInstance(Validator.TYPE_PKIX,
+                    Validator.VAR_TLS_SERVER, params);
+            val.validate(path, null, responses, null, "RSA");
+            if (expectedExc != null) {
+                // We expected to receive an exception here
+                throw new RuntimeException("Did not receive expected " +
+                        expectedExc.getClass().getName());
+            }
+        } catch (CertificateException certExc) {
+            if (expectedExc == null) {
+                // This test was supposed to pass, so wrap it in a Runtime
+                throw new RuntimeException("Received unexpected exception: ",
+                        certExc);
+            } else {
+                Throwable cause = certExc.getCause();
+                if (cause == null) {
+                    throw new RuntimeException("Missing expected cause: " +
+                            expectedExc.getClass().getName(),
+                            certExc);
+                } else {
+                    verifyCause(cause, expectedExc);
+                }
+            }
+        }
+    }
+
+    static void verifyCause(Throwable cause, Throwable expectedExc) {
+        if (cause.getClass() != expectedExc.getClass()) {
+            throw new RuntimeException("Exception class mismatch: expected = " +
+                    expectedExc.getClass().getName() + ", actual = " +
+                    cause.getClass().getName());
+        } else if (cause instanceof CertPathValidatorException) {
+            CertPathValidatorException actual =
+                    (CertPathValidatorException)cause;
+            CertPathValidatorException expected =
+                    (CertPathValidatorException)expectedExc;
+            // The failure index and reason should be the same
+            if (actual.getIndex() != expected.getIndex() ||
+                    actual.getReason() != expected.getReason()) {
+                throw new RuntimeException("CertPathValidatorException " +
+                        "differs from expected.  Expected: index = " +
+                        expected.getIndex() + ", reason = " +
+                        expected.getReason() + ", Actual: index = " +
+                        actual.getIndex() + ", reason = " +
+                        actual.getReason(), actual);
+            }
+        }
+    }
+
+    static X509Certificate getCert(CertificateFactory fac, String pemCert) {
+        try {
+            ByteArrayInputStream bais =
+                    new ByteArrayInputStream(pemCert.getBytes("UTF-8"));
+            return (X509Certificate)fac.generateCertificate(bais);
+        } catch (UnsupportedEncodingException | CertificateException exc) {
+            throw new RuntimeException(exc);
+        }
+    }
+
+    static byte[] pemToDer(String pemData) {
+        Base64.Decoder b64Dec = Base64.getMimeDecoder();
+        return b64Dec.decode(pemData);
+    }
+}