1 /*
   2  * Copyright (c) 2019, 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.*;
  25 import java.math.BigInteger;
  26 import java.security.*;
  27 import java.security.cert.*;
  28 import java.time.*;
  29 import java.util.*;
  30 import javax.net.ssl.*;
  31 import sun.security.validator.Validator;
  32 import sun.security.validator.ValidatorException;
  33 
  34 
  35 /**
  36  * @test
  37  * @bug 8207258 8216280
  38  * @summary Check that TLS Server certificates chaining back to distrusted
  39  *          Symantec roots are invalid
  40  * @library /lib/security
  41  * @run main/othervm Distrust after policyOn invalid
  42  * @run main/othervm Distrust after policyOff valid
  43  * @run main/othervm Distrust before policyOn valid
  44  * @run main/othervm Distrust before policyOff valid
  45  */
  46 
  47 public class Distrust {
  48 
  49     private static final String TEST_SRC = System.getProperty("test.src", ".");
  50     private static CertificateFactory cf;
  51 
  52     // Each of the roots have a test certificate chain stored in a file
  53     // named "<root>-chain.pem".
  54     private static String[] rootsToTest = new String[] {
  55         "geotrustglobalca", "geotrustprimarycag2", "geotrustprimarycag3",
  56         "geotrustuniversalca", "thawteprimaryrootca", "thawteprimaryrootcag2",
  57         "thawteprimaryrootcag3", "verisignclass3g3ca", "verisignclass3g4ca",
  58         "verisignclass3g5ca", "verisignuniversalrootca" };
  59 
  60     // Each of the subCAs with a delayed distrust date have a test certificate
  61     // chain stored in a file named "<subCA>-chain.pem".
  62     private static String[] subCAsToTest = new String[] {
  63         "appleistca2g1", "appleistca8g1" };
  64 
  65 
  66     // A date that is after the restrictions take affect
  67     private static final Date APRIL_17_2019 =
  68         Date.from(LocalDate.of(2019, 4, 17)
  69                            .atStartOfDay(ZoneOffset.UTC)
  70                            .toInstant());
  71 
  72     // A date that is a second before the restrictions take affect
  73     private static final Date BEFORE_APRIL_17_2019 =
  74         Date.from(LocalDate.of(2019, 4, 17)
  75                            .atStartOfDay(ZoneOffset.UTC)
  76                            .minusSeconds(1)
  77                            .toInstant());
  78 
  79     // A date that is after the subCA restrictions take affect
  80     private static final Date JANUARY_1_2020 =
  81         Date.from(LocalDate.of(2020, 1, 1)
  82                            .atStartOfDay(ZoneOffset.UTC)
  83                            .toInstant());
  84 
  85     // A date that is a second before the subCA restrictions take affect
  86     private static final Date BEFORE_JANUARY_1_2020 =
  87         Date.from(LocalDate.of(2020, 1, 1)
  88                            .atStartOfDay(ZoneOffset.UTC)
  89                            .minusSeconds(1)
  90                            .toInstant());
  91 
  92     public static void main(String[] args) throws Exception {
  93 
  94         cf = CertificateFactory.getInstance("X.509");
  95         boolean distrust = args[0].equals("true");
  96 
  97         boolean before = args[0].equals("before");
  98         boolean policyOn = args[1].equals("policyOn");
  99         boolean isValid = args[2].equals("valid");
 100 
 101         if (!policyOn) {
 102             // disable policy (default is on)
 103             Security.setProperty("jdk.security.caDistrustPolicies", "");
 104         }
 105 
 106         Date notBefore = before ? BEFORE_APRIL_17_2019 : APRIL_17_2019;
 107 
 108         X509TrustManager pkixTM = getTMF("PKIX", null);
 109         X509TrustManager sunX509TM = getTMF("SunX509", null);
 110         for (String test : rootsToTest) {
 111             System.err.println("Testing " + test);
 112             X509Certificate[] chain = loadCertificateChain(test);
 113 
 114             testTM(sunX509TM, chain, notBefore, isValid);
 115             testTM(pkixTM, chain, notBefore, isValid);
 116 
 117         }
 118 
 119         // test chain if params are passed to TrustManager
 120         System.err.println("Testing verisignuniversalrootca with params");
 121         testTM(getTMF("PKIX", getParams()),
 122                loadCertificateChain("verisignuniversalrootca"),
 123                notBefore, isValid);
 124 
 125         // test code-signing chain (should be valid as restrictions don't apply)
 126         System.err.println("Testing verisignclass3g5ca code-signing chain");
 127         Validator v = Validator.getInstance(Validator.TYPE_PKIX,
 128                                             Validator.VAR_CODE_SIGNING,
 129                                             getParams());
 130         // set validation date so this will still pass when cert expires
 131         v.setValidationDate(new Date(1544197375493l));
 132         v.validate(loadCertificateChain("verisignclass3g5ca-codesigning"));
 133 
 134         // test chains issued through subCAs
 135         notBefore = before ? BEFORE_JANUARY_1_2020 : JANUARY_1_2020;
 136         for (String test : subCAsToTest) {
 137             System.err.println("Testing " + test);
 138             X509Certificate[] chain = loadCertificateChain(test);
 139 
 140             testTM(sunX509TM, chain, notBefore, isValid);
 141             testTM(pkixTM, chain, notBefore, isValid);
 142         }
 143     }
 144 
 145     private static X509TrustManager getTMF(String type,
 146             PKIXBuilderParameters params) throws Exception {
 147         TrustManagerFactory tmf = TrustManagerFactory.getInstance(type);
 148         if (params == null) {
 149             tmf.init((KeyStore)null);
 150         } else {
 151             tmf.init(new CertPathTrustManagerParameters(params));
 152         }
 153         TrustManager[] tms = tmf.getTrustManagers();
 154         for (TrustManager tm : tms) {
 155             X509TrustManager xtm = (X509TrustManager)tm;
 156             return xtm;
 157         }
 158         throw new Exception("No TrustManager for " + type);
 159     }
 160 
 161     private static PKIXBuilderParameters getParams() throws Exception {
 162         PKIXBuilderParameters pbp =
 163             new PKIXBuilderParameters(SecurityUtils.getCacertsKeyStore(),
 164                                       new X509CertSelector());
 165         pbp.setRevocationEnabled(false);
 166         return pbp;
 167     }
 168 
 169     private static void testTM(X509TrustManager xtm, X509Certificate[] chain,
 170                                Date notBefore, boolean valid) throws Exception {
 171         // Check if TLS Server certificate (the first element of the chain)
 172         // is issued after the specified notBefore date (should be rejected
 173         // unless distrust property is false). To do this, we need to
 174         // fake the notBefore date since none of the test certs are issued
 175         // after then.
 176         chain[0] = new DistrustedTLSServerCert(chain[0], notBefore);
 177 
 178         try {
 179             xtm.checkServerTrusted(chain, "ECDHE_RSA");
 180             if (!valid) {
 181                 throw new Exception("chain should be invalid");
 182             }
 183         } catch (CertificateException ce) {
 184             if (valid) {
 185                 throw new Exception("Unexpected exception, chain " +
 186                                     "should be valid", ce);
 187             }
 188             if (ce instanceof ValidatorException) {
 189                 ValidatorException ve = (ValidatorException)ce;
 190                 if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) {
 191                     throw new Exception("Unexpected exception: " + ce);
 192                 }
 193             } else {
 194                 throw new Exception("Unexpected exception: " + ce);
 195             }
 196         }
 197     }
 198 
 199     private static X509Certificate[] loadCertificateChain(String name)
 200             throws Exception {
 201         try (InputStream in = new FileInputStream(TEST_SRC + File.separator +
 202                                                   name + "-chain.pem")) {
 203             Collection<X509Certificate> certs =
 204                 (Collection<X509Certificate>)cf.generateCertificates(in);
 205             return certs.toArray(new X509Certificate[0]);
 206         }
 207     }
 208 
 209     private static class DistrustedTLSServerCert extends X509Certificate {
 210         private final X509Certificate cert;
 211         private final Date notBefore;
 212         DistrustedTLSServerCert(X509Certificate cert, Date notBefore) {
 213             this.cert = cert;
 214             this.notBefore = notBefore;
 215         }
 216         public Set<String> getCriticalExtensionOIDs() {
 217            return cert.getCriticalExtensionOIDs();
 218         }
 219         public byte[] getExtensionValue(String oid) {
 220             return cert.getExtensionValue(oid);
 221         }
 222         public Set<String> getNonCriticalExtensionOIDs() {
 223             return cert.getNonCriticalExtensionOIDs();
 224         }
 225         public boolean hasUnsupportedCriticalExtension() {
 226             return cert.hasUnsupportedCriticalExtension();
 227         }
 228         public void checkValidity() throws CertificateExpiredException,
 229             CertificateNotYetValidException {
 230             // always pass
 231         }
 232         public void checkValidity(Date date) throws CertificateExpiredException,
 233             CertificateNotYetValidException {
 234             // always pass
 235         }
 236         public int getVersion() { return cert.getVersion(); }
 237         public BigInteger getSerialNumber() { return cert.getSerialNumber(); }
 238         public Principal getIssuerDN() { return cert.getIssuerDN(); }
 239         public Principal getSubjectDN() { return cert.getSubjectDN(); }
 240         public Date getNotBefore() { return notBefore; }
 241         public Date getNotAfter() { return cert.getNotAfter(); }
 242         public byte[] getTBSCertificate() throws CertificateEncodingException {
 243             return cert.getTBSCertificate();
 244         }
 245         public byte[] getSignature() { return cert.getSignature(); }
 246         public String getSigAlgName() { return cert.getSigAlgName(); }
 247         public String getSigAlgOID() { return cert.getSigAlgOID(); }
 248         public byte[] getSigAlgParams() { return cert.getSigAlgParams(); }
 249         public boolean[] getIssuerUniqueID() {
 250             return cert.getIssuerUniqueID();
 251         }
 252         public boolean[] getSubjectUniqueID() {
 253             return cert.getSubjectUniqueID();
 254         }
 255         public boolean[] getKeyUsage() { return cert.getKeyUsage(); }
 256         public int getBasicConstraints() { return cert.getBasicConstraints(); }
 257         public byte[] getEncoded() throws CertificateEncodingException {
 258             return cert.getEncoded();
 259         }
 260         public void verify(PublicKey key) throws CertificateException,
 261             InvalidKeyException, NoSuchAlgorithmException,
 262             NoSuchProviderException, SignatureException {
 263             cert.verify(key);
 264         }
 265         public void verify(PublicKey key, String sigProvider) throws
 266             CertificateException, InvalidKeyException, NoSuchAlgorithmException,
 267             NoSuchProviderException, SignatureException {
 268             cert.verify(key, sigProvider);
 269         }
 270         public PublicKey getPublicKey() { return cert.getPublicKey(); }
 271         public String toString() { return cert.toString(); }
 272     }
 273 }