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
  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 true
  42  * @run main/othervm Distrust false
  43  */
  44 
  45 public class Distrust {
  46 
  47     private static final String TEST_SRC = System.getProperty("test.src", ".");
  48     private static CertificateFactory cf;
  49 
  50     // Each of the roots have a test certificate chain stored in a file
  51     // named "<root>-chain.pem".
  52     private static String[] rootsToTest = new String[] {
  53         "geotrustglobalca", "geotrustprimarycag2", "geotrustprimarycag3",
  54         "geotrustuniversalca", "thawteprimaryrootca", "thawteprimaryrootcag2",
  55         "thawteprimaryrootcag3", "verisignclass3g3ca", "verisignclass3g4ca",
  56         "verisignclass3g5ca", "verisignuniversalrootca" };
  57 
  58     // A date that is after the restrictions take affect
  59     private static final Date APRIL_17_2019 =
  60         Date.from(LocalDate.of(2019, 4, 17)
  61                            .atStartOfDay(ZoneOffset.UTC)
  62                            .toInstant());
  63 
  64     public static void main(String[] args) throws Exception {
  65 
  66         cf = CertificateFactory.getInstance("X.509");
  67         boolean distrust = args[0].equals("true");
  68         if (!distrust) {
  69             // disable policy
  70             Security.setProperty("jdk.security.caDistrustPolicies", "");
  71         }
  72 
  73         X509TrustManager pkixTM = getTMF("PKIX", null);
  74         X509TrustManager sunX509TM = getTMF("SunX509", null);
  75         for (String test : rootsToTest) {
  76             System.err.println("Testing " + test);
  77             X509Certificate[] chain = loadCertificateChain(test);
  78 
  79             testTM(sunX509TM, chain, !distrust);
  80             testTM(pkixTM, chain, !distrust);
  81         }
  82 
  83         // test chain if params are passed to TrustManager
  84         System.err.println("Testing verisignuniversalrootca with params");
  85         testTM(getTMF("PKIX", getParams()),
  86                loadCertificateChain("verisignuniversalrootca"), !distrust);
  87 
  88         // test code-signing chain (should be valid as restrictions don't apply)
  89         System.err.println("Testing verisignclass3g5ca code-signing chain");
  90         Validator v = Validator.getInstance(Validator.TYPE_PKIX,
  91                                             Validator.VAR_CODE_SIGNING,
  92                                             getParams());
  93         // set validation date so this will still pass when cert expires
  94         v.setValidationDate(new Date(1544197375493l));
  95         v.validate(loadCertificateChain("verisignclass3g5ca-codesigning"));
  96     }
  97 
  98     private static X509TrustManager getTMF(String type,
  99             PKIXBuilderParameters params) throws Exception {
 100         TrustManagerFactory tmf = TrustManagerFactory.getInstance(type);
 101         if (params == null) {
 102             tmf.init((KeyStore)null);
 103         } else {
 104             tmf.init(new CertPathTrustManagerParameters(params));
 105         }
 106         TrustManager[] tms = tmf.getTrustManagers();
 107         for (TrustManager tm : tms) {
 108             X509TrustManager xtm = (X509TrustManager)tm;
 109             return xtm;
 110         }
 111         throw new Exception("No TrustManager for " + type);
 112     }
 113 
 114     private static PKIXBuilderParameters getParams() throws Exception {
 115         PKIXBuilderParameters pbp =
 116             new PKIXBuilderParameters(SecurityUtils.getCacertsKeyStore(),
 117                                       new X509CertSelector());
 118         pbp.setRevocationEnabled(false);
 119         return pbp;
 120     }
 121 
 122     private static void testTM(X509TrustManager xtm, X509Certificate[] chain,
 123                                boolean valid) throws Exception {
 124         // Check if TLS Server certificate (the first element of the chain)
 125         // is issued after April 16, 2019 (should be rejected unless distrust
 126         // property is false). To do this, we need to fake the notBefore date
 127         // since none of the test certs are issued after then.
 128         chain[0] = new DistrustedTLSServerCert(chain[0], APRIL_17_2019);
 129 
 130         try {
 131             xtm.checkServerTrusted(chain, "ECDHE_RSA");
 132             if (!valid) {
 133                 throw new Exception("chain should be invalid");
 134             }
 135         } catch (CertificateException ce) {
 136             if (valid) {
 137                 throw new Exception("Unexpected exception, chain " +
 138                                     "should be valid", ce);
 139             }
 140             if (ce instanceof ValidatorException) {
 141                 ValidatorException ve = (ValidatorException)ce;
 142                 if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) {
 143                     throw new Exception("Unexpected exception: " + ce);
 144                 }
 145             } else {
 146                 throw new Exception("Unexpected exception: " + ce);
 147             }
 148         }
 149     }
 150 
 151     private static X509Certificate[] loadCertificateChain(String name)
 152             throws Exception {
 153         try (InputStream in = new FileInputStream(TEST_SRC + File.separator +
 154                                                   name + "-chain.pem")) {
 155             Collection<X509Certificate> certs =
 156                 (Collection<X509Certificate>)cf.generateCertificates(in);
 157             return certs.toArray(new X509Certificate[0]);
 158         }
 159     }
 160 
 161     private static class DistrustedTLSServerCert extends X509Certificate {
 162         private final X509Certificate cert;
 163         private final Date notBefore;
 164         DistrustedTLSServerCert(X509Certificate cert, Date notBefore) {
 165             this.cert = cert;
 166             this.notBefore = notBefore;
 167         }
 168         public Set<String> getCriticalExtensionOIDs() {
 169            return cert.getCriticalExtensionOIDs();
 170         }
 171         public byte[] getExtensionValue(String oid) {
 172             return cert.getExtensionValue(oid);
 173         }
 174         public Set<String> getNonCriticalExtensionOIDs() {
 175             return cert.getNonCriticalExtensionOIDs();
 176         }
 177         public boolean hasUnsupportedCriticalExtension() {
 178             return cert.hasUnsupportedCriticalExtension();
 179         }
 180         public void checkValidity() throws CertificateExpiredException,
 181             CertificateNotYetValidException {
 182             // always pass
 183         }
 184         public void checkValidity(Date date) throws CertificateExpiredException,
 185             CertificateNotYetValidException {
 186             // always pass
 187         }
 188         public int getVersion() { return cert.getVersion(); }
 189         public BigInteger getSerialNumber() { return cert.getSerialNumber(); }
 190         public Principal getIssuerDN() { return cert.getIssuerDN(); }
 191         public Principal getSubjectDN() { return cert.getSubjectDN(); }
 192         public Date getNotBefore() { return notBefore; }
 193         public Date getNotAfter() { return cert.getNotAfter(); }
 194         public byte[] getTBSCertificate() throws CertificateEncodingException {
 195             return cert.getTBSCertificate();
 196         }
 197         public byte[] getSignature() { return cert.getSignature(); }
 198         public String getSigAlgName() { return cert.getSigAlgName(); }
 199         public String getSigAlgOID() { return cert.getSigAlgOID(); }
 200         public byte[] getSigAlgParams() { return cert.getSigAlgParams(); }
 201         public boolean[] getIssuerUniqueID() {
 202             return cert.getIssuerUniqueID();
 203         }
 204         public boolean[] getSubjectUniqueID() {
 205             return cert.getSubjectUniqueID();
 206         }
 207         public boolean[] getKeyUsage() { return cert.getKeyUsage(); }
 208         public int getBasicConstraints() { return cert.getBasicConstraints(); }
 209         public byte[] getEncoded() throws CertificateEncodingException {
 210             return cert.getEncoded();
 211         }
 212         public void verify(PublicKey key) throws CertificateException,
 213             InvalidKeyException, NoSuchAlgorithmException,
 214             NoSuchProviderException, SignatureException {
 215             cert.verify(key);
 216         }
 217         public void verify(PublicKey key, String sigProvider) throws
 218             CertificateException, InvalidKeyException, NoSuchAlgorithmException,
 219             NoSuchProviderException, SignatureException {
 220             cert.verify(key, sigProvider);
 221         }
 222         public PublicKey getPublicKey() { return cert.getPublicKey(); }
 223         public String toString() { return cert.toString(); }
 224     }
 225 }