--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/ValidatePathWithParams.java Tue Dec 12 19:20:39 2017 -0800
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateRevokedException;
+import java.security.cert.PKIXParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.Locale;
+
+/**
+ * Utility class to validate certificate path. It supports OCSP and/or CRL
+ * validation.
+ */
+public class ValidatePathWithParams {
+
+ private static final String FS = System.getProperty("file.separator");
+ private static final String CACERTS_STORE = System.getProperty("test.jdk")
+ + FS + "lib" + FS + "security" + FS + "cacerts";
+
+ private final String[] trustedRootCerts;
+
+ // use this for expired cert validation
+ private Date validationDate = null;
+
+ // expected certificate status
+ private Status expectedStatus = Status.UNKNOWN;
+ private Date expectedRevDate = null;
+
+ private final CertPathValidator certPathValidator;
+ private final PKIXRevocationChecker certPathChecker;
+ private final CertificateFactory cf;
+
+ /**
+ * Possible status values supported for EE certificate
+ */
+ public static enum Status {
+ UNKNOWN, GOOD, REVOKED, EXPIRED;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param additionalTrustRoots trusted root certificates
+ * @throws IOException
+ * @throws CertificateException
+ * @throws NoSuchAlgorithmException
+ */
+ public ValidatePathWithParams(String[] additionalTrustRoots)
+ throws IOException, CertificateException, NoSuchAlgorithmException {
+
+ cf = CertificateFactory.getInstance("X509");
+ certPathValidator = CertPathValidator.getInstance("PKIX");
+ certPathChecker
+ = (PKIXRevocationChecker) certPathValidator.getRevocationChecker();
+
+ if ((additionalTrustRoots == null) || (additionalTrustRoots[0] == null)) {
+ trustedRootCerts = null;
+ } else {
+ trustedRootCerts = additionalTrustRoots.clone();
+ }
+ }
+
+ /**
+ * Validate certificates
+ *
+ * @param certsToValidate Certificates to validate
+ * @param st expected certificate status
+ * @param revDate if revoked, expected revocation date
+ * @param out PrintStream to log messages
+ * @throws IOException
+ * @throws CertificateException
+ * @throws InvalidAlgorithmParameterException
+ * @throws ParseException
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreException
+ */
+ public void validate(String[] certsToValidate,
+ Status st,
+ String revDate,
+ PrintStream out)
+ throws IOException, CertificateException,
+ InvalidAlgorithmParameterException, ParseException,
+ NoSuchAlgorithmException, KeyStoreException {
+
+ expectedStatus = st;
+ if (expectedStatus == Status.REVOKED) {
+ if (revDate != null) {
+ expectedRevDate = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy",
+ Locale.US).parse(revDate);
+ }
+ }
+
+ Status certStatus = null;
+ Date revocationDate = null;
+
+ logSettings(out);
+
+ try {
+ doCertPathValidate(certsToValidate, out);
+ certStatus = Status.GOOD;
+ } catch (IOException ioe) {
+ // Some machines don't have network setup correctly to be able to
+ // reach outside world, skip such failures
+ out.println("WARNING: Network setup issue, skip this test");
+ ioe.printStackTrace(System.err);
+ return;
+ } catch (CertPathValidatorException cpve) {
+ out.println("Received exception: " + cpve);
+
+ if (cpve.getCause() instanceof IOException) {
+ out.println("WARNING: CertPathValidatorException caused by IO"
+ + " error, skip this test");
+ return;
+ }
+
+ if (cpve.getReason() == CertPathValidatorException.BasicReason.ALGORITHM_CONSTRAINED) {
+ out.println("WARNING: CertPathValidatorException caused by"
+ + " restricted algorithm, skip this test");
+ return;
+ }
+
+ if (cpve.getReason() == CertPathValidatorException.BasicReason.REVOKED
+ || cpve.getCause() instanceof CertificateRevokedException) {
+ certStatus = Status.REVOKED;
+ if (cpve.getCause() instanceof CertificateRevokedException) {
+ CertificateRevokedException cre
+ = (CertificateRevokedException) cpve.getCause();
+ revocationDate = cre.getRevocationDate();
+ }
+ } else if (cpve.getReason() == CertPathValidatorException.BasicReason.EXPIRED
+ || cpve.getCause() instanceof CertificateExpiredException) {
+ certStatus = Status.EXPIRED;
+ } else {
+ throw new RuntimeException(
+ "TEST FAILED: couldn't determine EE certificate status");
+ }
+ }
+
+ out.println("Expected Certificate status: " + expectedStatus);
+ out.println("Certificate status after validation: " + certStatus.name());
+
+ // Don't want test to fail in case certificate is expired when not expected
+ // Simply skip the test.
+ if (expectedStatus != Status.EXPIRED && certStatus == Status.EXPIRED) {
+ out.println("WARNING: Certificate expired, skip the test");
+ return;
+ }
+
+ if (certStatus != expectedStatus) {
+ throw new RuntimeException(
+ "TEST FAILED: unexpected status of EE certificate");
+ }
+
+ if (certStatus == Status.REVOKED) {
+ // Check revocation date
+ if (revocationDate != null) {
+ out.println(
+ "Certificate revocation date:" + revocationDate.toString());
+ if (expectedRevDate != null) {
+ out.println(
+ "Expected revocation date:" + expectedRevDate.toString());
+ if (!expectedRevDate.equals(revocationDate)) {
+ throw new RuntimeException(
+ "TEST FAILED: unexpected revocation date");
+ }
+ }
+ } else {
+ throw new RuntimeException("TEST FAILED: no revocation date");
+ }
+ }
+ }
+
+ private void logSettings(PrintStream out) {
+ out.println();
+ out.println("=====================================================");
+ out.println("CONFIGURATION");
+ out.println("=====================================================");
+ out.println("http.proxyHost :" + System.getProperty("http.proxyHost"));
+ out.println("http.proxyPort :" + System.getProperty("http.proxyPort"));
+ out.println("https.proxyHost :" + System.getProperty("https.proxyHost"));
+ out.println("https.proxyPort :" + System.getProperty("https.proxyPort"));
+ out.println("https.socksProxyHost :"
+ + System.getProperty("https.socksProxyHost"));
+ out.println("https.socksProxyPort :"
+ + System.getProperty("https.socksProxyPort"));
+ out.println("jdk.certpath.disabledAlgorithms :"
+ + Security.getProperty("jdk.certpath.disabledAlgorithms"));
+ out.println("Revocation options :" + certPathChecker.getOptions());
+ out.println("OCSP responder set :" + certPathChecker.getOcspResponder());
+ out.println("Trusted root set: " + (trustedRootCerts != null));
+
+ if (validationDate != null) {
+ out.println("Validation Date:" + validationDate.toString());
+ }
+ out.println("Expected EE Status:" + expectedStatus.name());
+ if (expectedStatus == Status.REVOKED && expectedRevDate != null) {
+ out.println(
+ "Expected EE Revocation Date:" + expectedRevDate.toString());
+ }
+ out.println("=====================================================");
+ }
+
+ private void doCertPathValidate(String[] certsToValidate, PrintStream out)
+ throws IOException, CertificateException,
+ InvalidAlgorithmParameterException, ParseException,
+ NoSuchAlgorithmException, CertPathValidatorException, KeyStoreException {
+
+ if (certsToValidate == null) {
+ throw new RuntimeException("Require atleast one cert to validate");
+ }
+
+ // Generate CertPath with certsToValidate
+ ArrayList<X509Certificate> certs = new ArrayList();
+ for (String cert : certsToValidate) {
+ if (cert != null) {
+ certs.add(getCertificate(cert));
+ }
+ }
+ CertPath certPath = (CertPath) cf.generateCertPath(certs);
+
+ // Set cacerts as anchor
+ KeyStore cacerts = KeyStore.getInstance("JKS");
+ try (FileInputStream fis = new FileInputStream(CACERTS_STORE)) {
+ cacerts.load(fis, "changeit".toCharArray());
+ } catch (IOException | NoSuchAlgorithmException | CertificateException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ // Set additional trust certificates
+ if (trustedRootCerts != null) {
+ for (int i = 0; i < trustedRootCerts.length; i++) {
+ X509Certificate rootCACert = getCertificate(trustedRootCerts[i]);
+ cacerts.setCertificateEntry("tempca" + i, rootCACert);
+ }
+ }
+
+ PKIXParameters params;
+ params = new PKIXParameters(cacerts);
+ params.addCertPathChecker(certPathChecker);
+
+ // Set backdated validation if requested, if null, current date is set
+ params.setDate(validationDate);
+
+ // Validate
+ certPathValidator.validate(certPath, params);
+ out.println("Successful CertPath validation");
+ }
+
+ private X509Certificate getCertificate(String encodedCert)
+ throws IOException, CertificateException {
+ ByteArrayInputStream is
+ = new ByteArrayInputStream(encodedCert.getBytes());
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+ return cert;
+ }
+
+ /**
+ * Set list of disabled algorithms
+ *
+ * @param algos algorithms to disable
+ */
+ public static void setDisabledAlgorithms(String algos) {
+ Security.setProperty("jdk.certpath.disabledAlgorithms", algos);
+ }
+
+ /**
+ * Enable OCSP only revocation checks, treat network error as success
+ */
+ public void enableOCSPCheck() {
+ // OCSP is by default, disable fallback to CRL
+ certPathChecker.setOptions(EnumSet.of(
+ PKIXRevocationChecker.Option.NO_FALLBACK));
+ }
+
+ /**
+ * Enable CRL only revocation check, treat network error as success
+ */
+ public void enableCRLCheck() {
+ certPathChecker.setOptions(EnumSet.of(
+ PKIXRevocationChecker.Option.PREFER_CRLS,
+ PKIXRevocationChecker.Option.NO_FALLBACK));
+ }
+
+ /**
+ * Overrides OCSP responder URL in AIA extension of certificate
+ *
+ * @param url OCSP responder
+ * @throws URISyntaxException
+ */
+ public void setOCSPResponderURL(String url) throws URISyntaxException {
+ certPathChecker.setOcspResponder(new URI(url));
+ }
+
+ /**
+ * Set validation date for EE certificate
+ *
+ * @param vDate string formatted date
+ * @throws ParseException if vDate is incorrect
+ */
+ public void setValidationDate(String vDate) throws ParseException {
+ validationDate = DateFormat.getDateInstance(DateFormat.MEDIUM,
+ Locale.US).parse(vDate);
+ }
+
+ /**
+ * Reset validation date for EE certificate to current date
+ */
+ public void resetValidationDate() {
+ validationDate = null;
+ }
+}