# HG changeset patch # User jnimeh # Date 1566508176 25200 # Node ID e1269de19aa536f8f376032d17167f225763562b # Parent bc14eec6f4bce4c967c86d824189764d2f996988 8225436: Stapled OCSPResponses should be added to PKIXRevocationChecker irrespective of revocationEnabled flag Reviewed-by: mullan diff -r bc14eec6f4bc -r e1269de19aa5 src/java.base/share/classes/sun/security/validator/PKIXValidator.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 getTrustedCertificates() { return trustedCerts; } @@ -399,55 +400,67 @@ */ private static void addResponses(PKIXBuilderParameters pkixParams, X509Certificate[] chain, List responseList) { + try { + boolean createdRevChk = false; - if (pkixParams.isRevocationEnabled()) { - try { - // Make a modifiable copy of the CertPathChecker list - PKIXRevocationChecker revChecker = null; - List checkerList = - new ArrayList<>(pkixParams.getCertPathCheckers()); + // Obtain the current CertPathChecker list + PKIXRevocationChecker revChecker = null; + List 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 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 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. } } } diff -r bc14eec6f4bc -r e1269de19aa5 test/jdk/sun/security/validator/PKIXValAndRevCheckTests.java --- /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 trustAnchors = + Set.of(new TrustAnchor(rootCACert, null)); + PKIXRevocationChecker pkrc; + PKIXBuilderParameters pkixParams; + + X509Certificate[] goodPath = { goodGuyCert, intCACert, + rootCACert }; + X509Certificate[] badPath = { badGuyCert, intCACert, + rootCACert }; + + List goodResponses = List.of(goodOcspDer, intCAOcspDer); + List 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 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) 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 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); + } +}