test/jdk/security/infra/java/security/cert/CertPathValidator/certification/ValidatePathWithParams.java
changeset 48256 472f74fb6c6b
child 54803 293d45e5108b
equal deleted inserted replaced
48255:971d83666b23 48256:472f74fb6c6b
       
     1 /*
       
     2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.io.ByteArrayInputStream;
       
    25 import java.io.FileInputStream;
       
    26 import java.io.IOException;
       
    27 import java.io.PrintStream;
       
    28 import java.net.URI;
       
    29 import java.net.URISyntaxException;
       
    30 import java.security.InvalidAlgorithmParameterException;
       
    31 import java.security.KeyStore;
       
    32 import java.security.KeyStoreException;
       
    33 import java.security.NoSuchAlgorithmException;
       
    34 import java.security.Security;
       
    35 import java.security.cert.CertPath;
       
    36 import java.security.cert.CertPathValidator;
       
    37 import java.security.cert.CertPathValidatorException;
       
    38 import java.security.cert.CertificateException;
       
    39 import java.security.cert.CertificateExpiredException;
       
    40 import java.security.cert.CertificateFactory;
       
    41 import java.security.cert.CertificateRevokedException;
       
    42 import java.security.cert.PKIXParameters;
       
    43 import java.security.cert.PKIXRevocationChecker;
       
    44 import java.security.cert.X509Certificate;
       
    45 import java.text.DateFormat;
       
    46 import java.text.ParseException;
       
    47 import java.text.SimpleDateFormat;
       
    48 import java.util.ArrayList;
       
    49 import java.util.Date;
       
    50 import java.util.EnumSet;
       
    51 import java.util.Locale;
       
    52 
       
    53 /**
       
    54  * Utility class to validate certificate path. It supports OCSP and/or CRL
       
    55  * validation.
       
    56  */
       
    57 public class ValidatePathWithParams {
       
    58 
       
    59     private static final String FS = System.getProperty("file.separator");
       
    60     private static final String CACERTS_STORE = System.getProperty("test.jdk")
       
    61             + FS + "lib" + FS + "security" + FS + "cacerts";
       
    62 
       
    63     private final String[] trustedRootCerts;
       
    64 
       
    65     // use this for expired cert validation
       
    66     private Date validationDate = null;
       
    67 
       
    68     // expected certificate status
       
    69     private Status expectedStatus = Status.UNKNOWN;
       
    70     private Date expectedRevDate = null;
       
    71 
       
    72     private final CertPathValidator certPathValidator;
       
    73     private final PKIXRevocationChecker certPathChecker;
       
    74     private final CertificateFactory cf;
       
    75 
       
    76     /**
       
    77      * Possible status values supported for EE certificate
       
    78      */
       
    79     public static enum Status {
       
    80         UNKNOWN, GOOD, REVOKED, EXPIRED;
       
    81     }
       
    82 
       
    83     /**
       
    84      * Constructor
       
    85      *
       
    86      * @param additionalTrustRoots trusted root certificates
       
    87      * @throws IOException
       
    88      * @throws CertificateException
       
    89      * @throws NoSuchAlgorithmException
       
    90      */
       
    91     public ValidatePathWithParams(String[] additionalTrustRoots)
       
    92             throws IOException, CertificateException, NoSuchAlgorithmException {
       
    93 
       
    94         cf = CertificateFactory.getInstance("X509");
       
    95         certPathValidator = CertPathValidator.getInstance("PKIX");
       
    96         certPathChecker
       
    97                 = (PKIXRevocationChecker) certPathValidator.getRevocationChecker();
       
    98 
       
    99         if ((additionalTrustRoots == null) || (additionalTrustRoots[0] == null)) {
       
   100             trustedRootCerts = null;
       
   101         } else {
       
   102             trustedRootCerts = additionalTrustRoots.clone();
       
   103         }
       
   104     }
       
   105 
       
   106     /**
       
   107      * Validate certificates
       
   108      *
       
   109      * @param certsToValidate Certificates to validate
       
   110      * @param st expected certificate status
       
   111      * @param revDate if revoked, expected revocation date
       
   112      * @param out PrintStream to log messages
       
   113      * @throws IOException
       
   114      * @throws CertificateException
       
   115      * @throws InvalidAlgorithmParameterException
       
   116      * @throws ParseException
       
   117      * @throws NoSuchAlgorithmException
       
   118      * @throws KeyStoreException
       
   119      */
       
   120     public void validate(String[] certsToValidate,
       
   121             Status st,
       
   122             String revDate,
       
   123             PrintStream out)
       
   124             throws IOException, CertificateException,
       
   125             InvalidAlgorithmParameterException, ParseException,
       
   126             NoSuchAlgorithmException, KeyStoreException {
       
   127 
       
   128         expectedStatus = st;
       
   129         if (expectedStatus == Status.REVOKED) {
       
   130             if (revDate != null) {
       
   131                 expectedRevDate = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy",
       
   132                         Locale.US).parse(revDate);
       
   133             }
       
   134         }
       
   135 
       
   136         Status certStatus = null;
       
   137         Date revocationDate = null;
       
   138 
       
   139         logSettings(out);
       
   140 
       
   141         try {
       
   142             doCertPathValidate(certsToValidate, out);
       
   143             certStatus = Status.GOOD;
       
   144         } catch (IOException ioe) {
       
   145             // Some machines don't have network setup correctly to be able to
       
   146             // reach outside world, skip such failures
       
   147             out.println("WARNING: Network setup issue, skip this test");
       
   148             ioe.printStackTrace(System.err);
       
   149             return;
       
   150         } catch (CertPathValidatorException cpve) {
       
   151             out.println("Received exception: " + cpve);
       
   152 
       
   153             if (cpve.getCause() instanceof IOException) {
       
   154                 out.println("WARNING: CertPathValidatorException caused by IO"
       
   155                         + " error, skip this test");
       
   156                 return;
       
   157             }
       
   158 
       
   159             if (cpve.getReason() == CertPathValidatorException.BasicReason.ALGORITHM_CONSTRAINED) {
       
   160                 out.println("WARNING: CertPathValidatorException caused by"
       
   161                         + " restricted algorithm, skip this test");
       
   162                 return;
       
   163             }
       
   164 
       
   165             if (cpve.getReason() == CertPathValidatorException.BasicReason.REVOKED
       
   166                     || cpve.getCause() instanceof CertificateRevokedException) {
       
   167                 certStatus = Status.REVOKED;
       
   168                 if (cpve.getCause() instanceof CertificateRevokedException) {
       
   169                     CertificateRevokedException cre
       
   170                             = (CertificateRevokedException) cpve.getCause();
       
   171                     revocationDate = cre.getRevocationDate();
       
   172                 }
       
   173             } else if (cpve.getReason() == CertPathValidatorException.BasicReason.EXPIRED
       
   174                     || cpve.getCause() instanceof CertificateExpiredException) {
       
   175                 certStatus = Status.EXPIRED;
       
   176             } else {
       
   177                 throw new RuntimeException(
       
   178                         "TEST FAILED: couldn't determine EE certificate status");
       
   179             }
       
   180         }
       
   181 
       
   182         out.println("Expected Certificate status: " + expectedStatus);
       
   183         out.println("Certificate status after validation: " + certStatus.name());
       
   184 
       
   185         // Don't want test to fail in case certificate is expired when not expected
       
   186         // Simply skip the test.
       
   187         if (expectedStatus != Status.EXPIRED && certStatus == Status.EXPIRED) {
       
   188             out.println("WARNING: Certificate expired, skip the test");
       
   189             return;
       
   190         }
       
   191 
       
   192         if (certStatus != expectedStatus) {
       
   193             throw new RuntimeException(
       
   194                     "TEST FAILED: unexpected status of EE certificate");
       
   195         }
       
   196 
       
   197         if (certStatus == Status.REVOKED) {
       
   198             // Check revocation date
       
   199             if (revocationDate != null) {
       
   200                 out.println(
       
   201                         "Certificate revocation date:" + revocationDate.toString());
       
   202                 if (expectedRevDate != null) {
       
   203                     out.println(
       
   204                             "Expected revocation date:" + expectedRevDate.toString());
       
   205                     if (!expectedRevDate.equals(revocationDate)) {
       
   206                         throw new RuntimeException(
       
   207                                 "TEST FAILED: unexpected revocation date");
       
   208                     }
       
   209                 }
       
   210             } else {
       
   211                 throw new RuntimeException("TEST FAILED: no revocation date");
       
   212             }
       
   213         }
       
   214     }
       
   215 
       
   216     private void logSettings(PrintStream out) {
       
   217         out.println();
       
   218         out.println("=====================================================");
       
   219         out.println("CONFIGURATION");
       
   220         out.println("=====================================================");
       
   221         out.println("http.proxyHost :" + System.getProperty("http.proxyHost"));
       
   222         out.println("http.proxyPort :" + System.getProperty("http.proxyPort"));
       
   223         out.println("https.proxyHost :" + System.getProperty("https.proxyHost"));
       
   224         out.println("https.proxyPort :" + System.getProperty("https.proxyPort"));
       
   225         out.println("https.socksProxyHost :"
       
   226                 + System.getProperty("https.socksProxyHost"));
       
   227         out.println("https.socksProxyPort :"
       
   228                 + System.getProperty("https.socksProxyPort"));
       
   229         out.println("jdk.certpath.disabledAlgorithms :"
       
   230                 + Security.getProperty("jdk.certpath.disabledAlgorithms"));
       
   231         out.println("Revocation options :" + certPathChecker.getOptions());
       
   232         out.println("OCSP responder set :" + certPathChecker.getOcspResponder());
       
   233         out.println("Trusted root set: " + (trustedRootCerts != null));
       
   234 
       
   235         if (validationDate != null) {
       
   236             out.println("Validation Date:" + validationDate.toString());
       
   237         }
       
   238         out.println("Expected EE Status:" + expectedStatus.name());
       
   239         if (expectedStatus == Status.REVOKED && expectedRevDate != null) {
       
   240             out.println(
       
   241                     "Expected EE Revocation Date:" + expectedRevDate.toString());
       
   242         }
       
   243         out.println("=====================================================");
       
   244     }
       
   245 
       
   246     private void doCertPathValidate(String[] certsToValidate, PrintStream out)
       
   247             throws IOException, CertificateException,
       
   248             InvalidAlgorithmParameterException, ParseException,
       
   249             NoSuchAlgorithmException, CertPathValidatorException, KeyStoreException {
       
   250 
       
   251         if (certsToValidate == null) {
       
   252             throw new RuntimeException("Require atleast one cert to validate");
       
   253         }
       
   254 
       
   255         // Generate CertPath with certsToValidate
       
   256         ArrayList<X509Certificate> certs = new ArrayList();
       
   257         for (String cert : certsToValidate) {
       
   258             if (cert != null) {
       
   259                 certs.add(getCertificate(cert));
       
   260             }
       
   261         }
       
   262         CertPath certPath = (CertPath) cf.generateCertPath(certs);
       
   263 
       
   264         // Set cacerts as anchor
       
   265         KeyStore cacerts = KeyStore.getInstance("JKS");
       
   266         try (FileInputStream fis = new FileInputStream(CACERTS_STORE)) {
       
   267             cacerts.load(fis, "changeit".toCharArray());
       
   268         } catch (IOException | NoSuchAlgorithmException | CertificateException ex) {
       
   269             throw new RuntimeException(ex);
       
   270         }
       
   271 
       
   272         // Set additional trust certificates
       
   273         if (trustedRootCerts != null) {
       
   274             for (int i = 0; i < trustedRootCerts.length; i++) {
       
   275                 X509Certificate rootCACert = getCertificate(trustedRootCerts[i]);
       
   276                 cacerts.setCertificateEntry("tempca" + i, rootCACert);
       
   277             }
       
   278         }
       
   279 
       
   280         PKIXParameters params;
       
   281         params = new PKIXParameters(cacerts);
       
   282         params.addCertPathChecker(certPathChecker);
       
   283 
       
   284         // Set backdated validation if requested, if null, current date is set
       
   285         params.setDate(validationDate);
       
   286 
       
   287         // Validate
       
   288         certPathValidator.validate(certPath, params);
       
   289         out.println("Successful CertPath validation");
       
   290     }
       
   291 
       
   292     private X509Certificate getCertificate(String encodedCert)
       
   293             throws IOException, CertificateException {
       
   294         ByteArrayInputStream is
       
   295                 = new ByteArrayInputStream(encodedCert.getBytes());
       
   296         X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
       
   297         return cert;
       
   298     }
       
   299 
       
   300     /**
       
   301      * Set list of disabled algorithms
       
   302      *
       
   303      * @param algos algorithms to disable
       
   304      */
       
   305     public static void setDisabledAlgorithms(String algos) {
       
   306         Security.setProperty("jdk.certpath.disabledAlgorithms", algos);
       
   307     }
       
   308 
       
   309     /**
       
   310      * Enable OCSP only revocation checks, treat network error as success
       
   311      */
       
   312     public void enableOCSPCheck() {
       
   313         // OCSP is by default, disable fallback to CRL
       
   314         certPathChecker.setOptions(EnumSet.of(
       
   315                 PKIXRevocationChecker.Option.NO_FALLBACK));
       
   316     }
       
   317 
       
   318     /**
       
   319      * Enable CRL only revocation check, treat network error as success
       
   320      */
       
   321     public void enableCRLCheck() {
       
   322         certPathChecker.setOptions(EnumSet.of(
       
   323                 PKIXRevocationChecker.Option.PREFER_CRLS,
       
   324                 PKIXRevocationChecker.Option.NO_FALLBACK));
       
   325     }
       
   326 
       
   327     /**
       
   328      * Overrides OCSP responder URL in AIA extension of certificate
       
   329      *
       
   330      * @param url OCSP responder
       
   331      * @throws URISyntaxException
       
   332      */
       
   333     public void setOCSPResponderURL(String url) throws URISyntaxException {
       
   334         certPathChecker.setOcspResponder(new URI(url));
       
   335     }
       
   336 
       
   337     /**
       
   338      * Set validation date for EE certificate
       
   339      *
       
   340      * @param vDate string formatted date
       
   341      * @throws ParseException if vDate is incorrect
       
   342      */
       
   343     public void setValidationDate(String vDate) throws ParseException {
       
   344         validationDate = DateFormat.getDateInstance(DateFormat.MEDIUM,
       
   345                 Locale.US).parse(vDate);
       
   346     }
       
   347 
       
   348     /**
       
   349      * Reset validation date for EE certificate to current date
       
   350      */
       
   351     public void resetValidationDate() {
       
   352         validationDate = null;
       
   353     }
       
   354 }