Path: blob/master/test/jdk/sun/security/ssl/CertPathRestrictions/TLSRestrictions.java
41152 views
/*1* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.ByteArrayInputStream;24import java.io.IOException;25import java.io.InputStream;26import java.net.SocketTimeoutException;27import java.nio.file.Files;28import java.nio.file.Path;29import java.nio.file.Paths;30import java.security.KeyFactory;31import java.security.KeyStore;32import java.security.PrivateKey;33import java.security.Security;34import java.security.cert.Certificate;35import java.security.cert.CertificateFactory;36import java.security.spec.PKCS8EncodedKeySpec;37import java.util.Base64;38import java.util.concurrent.ExecutorService;39import java.util.concurrent.Executors;40import java.util.concurrent.Future;41import java.util.concurrent.TimeUnit;42import java.util.stream.Collectors;4344import javax.net.ssl.KeyManagerFactory;45import javax.net.ssl.SSLContext;46import javax.net.ssl.SSLHandshakeException;47import javax.net.ssl.TrustManagerFactory;4849import jdk.test.lib.process.OutputAnalyzer;50import jdk.test.lib.process.ProcessTools;5152/*53* @test54* @bug 816536755* @summary Verify the restrictions for certificate path on JSSE with custom trust store.56* @library /test/lib57* @build jdk.test.lib.Utils58* jdk.test.lib.Asserts59* jdk.test.lib.JDKToolFinder60* jdk.test.lib.JDKToolLauncher61* jdk.test.lib.Platform62* jdk.test.lib.process.*63* @compile JSSEClient.java64* @run main/othervm -Djava.security.debug=certpath TLSRestrictions DEFAULT65* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C166* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S167* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C268* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S269* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C370* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S371* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C472* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S473* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C574* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S575* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C676* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S677* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C778* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S779* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C880* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S881* @run main/othervm -Djava.security.debug=certpath TLSRestrictions C982* @run main/othervm -Djava.security.debug=certpath TLSRestrictions S983*/84public class TLSRestrictions {8586private static final String TEST_CLASSES = System.getProperty("test.classes");87private static final char[] PASSWORD = "".toCharArray();88private static final String CERT_DIR = System.getProperty("cert.dir",89System.getProperty("test.src") + "/certs");9091static final String PROP = "jdk.certpath.disabledAlgorithms";92static final String NOSHA1 = "MD2, MD5";93private static final String TLSSERVER = "SHA1 usage TLSServer";94private static final String TLSCLIENT = "SHA1 usage TLSClient";95static final String JDKCATLSSERVER = "SHA1 jdkCA & usage TLSServer";96static final String JDKCATLSCLIENT = "SHA1 jdkCA & usage TLSClient";9798// This is a space holder in command arguments, and stands for none certificate.99static final String NONE_CERT = "NONE_CERT";100101static final String DELIMITER = ",";102static final int TIMEOUT = 30000;103104// It checks if java.security contains constraint "SHA1 jdkCA & usage TLSServer"105// for jdk.certpath.disabledAlgorithms by default.106private static void checkDefaultConstraint() {107System.out.println(108"Case: Checks the default value of jdk.certpath.disabledAlgorithms");109if (!Security.getProperty(PROP).contains(JDKCATLSSERVER)) {110throw new RuntimeException(String.format(111"%s doesn't contain constraint \"%s\", the real value is \"%s\".",112PROP, JDKCATLSSERVER, Security.getProperty(PROP)));113}114}115116/*117* This method creates trust store and key store with specified certificates118* respectively. And then it creates SSL context with the stores.119* If trustNames contains NONE_CERT only, it does not create a custom trust120* store, but the default one in JDK.121*122* @param trustNames Trust anchors, which are used to create custom trust store.123* If null, no custom trust store is created and the default124* trust store in JDK is used.125* @param certNames Certificate chain, which is used to create key store.126* It cannot be null.127*/128static SSLContext createSSLContext(String[] trustNames,129String[] certNames) throws Exception {130CertificateFactory certFactory = CertificateFactory.getInstance("X.509");131132TrustManagerFactory tmf = null;133if (trustNames != null && trustNames.length > 0134&& !trustNames[0].equals(NONE_CERT)) {135KeyStore trustStore = KeyStore.getInstance("JKS");136trustStore.load(null, null);137for (int i = 0; i < trustNames.length; i++) {138try (InputStream is = new ByteArrayInputStream(139loadCert(trustNames[i]).getBytes())) {140Certificate trustCert = certFactory.generateCertificate(is);141trustStore.setCertificateEntry("trustCert-" + i, trustCert);142}143}144145tmf = TrustManagerFactory.getInstance("PKIX");146tmf.init(trustStore);147}148149Certificate[] certChain = new Certificate[certNames.length];150for (int i = 0; i < certNames.length; i++) {151try (InputStream is = new ByteArrayInputStream(152loadCert(certNames[i]).getBytes())) {153Certificate cert = certFactory.generateCertificate(is);154certChain[i] = cert;155}156}157158PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(159Base64.getMimeDecoder().decode(loadPrivKey(certNames[0])));160KeyFactory keyFactory = KeyFactory.getInstance("RSA");161PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);162163KeyStore keyStore = KeyStore.getInstance("JKS");164keyStore.load(null, null);165keyStore.setKeyEntry("keyCert", privKey, PASSWORD, certChain);166167KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");168kmf.init(keyStore, PASSWORD);169170SSLContext context = SSLContext.getInstance("TLS");171context.init(kmf.getKeyManagers(),172tmf == null ? null : tmf.getTrustManagers(), null);173return context;174}175176/*177* This method sets jdk.certpath.disabledAlgorithms, and then retrieves178* and prints its value.179*/180static void setConstraint(String side, String constraint) {181System.out.printf("%s: Old %s=%s%n", side, PROP,182Security.getProperty(PROP));183Security.setProperty(PROP, constraint);184System.out.printf("%s: New %s=%s%n", side, PROP,185Security.getProperty(PROP));186}187188/*189* This method is used to run a variety of cases.190* It launches a server, and then takes a client to connect the server.191* Both of server and client use the same certificates.192*193* @param trustNames Trust anchors, which are used to create custom trust store.194* If null, the default trust store in JDK is used.195* @param certNames Certificate chain, which is used to create key store.196* It cannot be null. The first certificate is regarded as197* the end entity.198* @param serverConstraint jdk.certpath.disabledAlgorithms value on server side.199* @param clientConstraint jdk.certpath.disabledAlgorithms value on client side.200* @param needClientAuth If true, server side acquires client authentication;201* otherwise, false.202* @param pass If true, the connection should be blocked; otherwise, false.203*/204static void testConstraint(String[] trustNames, String[] certNames,205String serverConstraint, String clientConstraint,206boolean needClientAuth, boolean pass) throws Exception {207String trustNameStr = trustNames == null ? ""208: String.join(DELIMITER, trustNames);209String certNameStr = certNames == null ? ""210: String.join(DELIMITER, certNames);211212System.out.printf("Case:%n"213+ " trustNames=%s; certNames=%s%n"214+ " serverConstraint=%s; clientConstraint=%s%n"215+ " needClientAuth=%s%n"216+ " pass=%s%n%n",217trustNameStr, certNameStr,218serverConstraint, clientConstraint,219needClientAuth,220pass);221222ExecutorService executor = Executors.newFixedThreadPool(1);223try {224JSSEServer server = new JSSEServer(225createSSLContext(trustNames, certNames),226serverConstraint,227needClientAuth);228int port = server.getPort();229Future<Exception> serverFuture = executor.submit(() -> server.start());230231// Run client on another JVM so that its properties cannot be in conflict232// with server's.233OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJvm(234"-Dcert.dir=" + CERT_DIR,235"-Djava.security.debug=certpath",236"-classpath",237TEST_CLASSES,238"JSSEClient",239port + "",240trustNameStr,241certNameStr,242clientConstraint);243int clientExitValue = outputAnalyzer.getExitValue();244String clientOut = outputAnalyzer.getOutput();245System.out.println("---------- Client output start ----------");246System.out.println(clientOut);247System.out.println("---------- Client output end ----------");248249Exception serverException = serverFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);250if (serverException instanceof SocketTimeoutException251|| clientOut.contains("SocketTimeoutException")) {252System.out.println("The communication gets timeout and skips the test.");253return;254}255256if (pass) {257if (serverException != null || clientExitValue != 0) {258throw new RuntimeException(259"Unexpected failure. Operation was blocked.");260}261} else {262if (serverException == null && clientExitValue == 0) {263throw new RuntimeException(264"Unexpected pass. Operation was allowed.");265}266267// The test may encounter non-SSL issues, like network problem.268if (!(serverException instanceof SSLHandshakeException269|| clientOut.contains("SSLHandshakeException"))) {270throw new RuntimeException("Failure with unexpected exception.");271}272}273} finally {274executor.shutdown();275}276}277278/*279* This method is used to run a variety of cases, which don't require client280* authentication by default.281*/282static void testConstraint(String[] trustNames, String[] certNames,283String serverConstraint, String clientConstraint, boolean pass)284throws Exception {285testConstraint(trustNames, certNames, serverConstraint, clientConstraint,286false, pass);287}288289public static void main(String[] args) throws Exception {290switch (args[0]) {291// Case DEFAULT only checks one of default settings for292// jdk.certpath.disabledAlgorithms in JDK/conf/security/java.security.293case "DEFAULT":294checkDefaultConstraint();295break;296297// Cases C1 and S1 use SHA256 root CA in trust store,298// and use SHA256 end entity in key store.299// C1 only sets constraint "SHA1 usage TLSServer" on client side;300// S1 only sets constraint "SHA1 usage TLSClient" on server side with client auth.301// The connection of the both cases should not be blocked.302case "C1":303testConstraint(304new String[] { "ROOT_CA_SHA256" },305new String[] { "INTER_CA_SHA256-ROOT_CA_SHA256" },306NOSHA1,307TLSSERVER,308true);309break;310case "S1":311testConstraint(312new String[] { "ROOT_CA_SHA256" },313new String[] { "INTER_CA_SHA256-ROOT_CA_SHA256" },314TLSCLIENT,315NOSHA1,316true,317true);318break;319320// Cases C2 and S2 use SHA256 root CA in trust store,321// and use SHA1 end entity in key store.322// C2 only sets constraint "SHA1 usage TLSServer" on client side;323// S2 only sets constraint "SHA1 usage TLSClient" on server side with client auth.324// The connection of the both cases should be blocked.325case "C2":326testConstraint(327new String[] { "ROOT_CA_SHA256" },328new String[] { "INTER_CA_SHA1-ROOT_CA_SHA256" },329NOSHA1,330TLSSERVER,331false);332break;333case "S2":334testConstraint(335new String[] { "ROOT_CA_SHA256" },336new String[] { "INTER_CA_SHA1-ROOT_CA_SHA256" },337TLSCLIENT,338NOSHA1,339true,340false);341break;342343// Cases C3 and S3 use SHA1 root CA in trust store,344// and use SHA1 end entity in key store.345// C3 only sets constraint "SHA1 usage TLSServer" on client side;346// S3 only sets constraint "SHA1 usage TLSClient" on server side with client auth.347// The connection of the both cases should be blocked.348case "C3":349testConstraint(350new String[] { "ROOT_CA_SHA1" },351new String[] { "INTER_CA_SHA1-ROOT_CA_SHA1" },352NOSHA1,353TLSSERVER,354false);355break;356case "S3":357testConstraint(358new String[] { "ROOT_CA_SHA1" },359new String[] { "INTER_CA_SHA1-ROOT_CA_SHA1" },360TLSCLIENT,361NOSHA1,362true,363false);364break;365366// Cases C4 and S4 use SHA1 root CA as trust store,367// and use SHA256 end entity in key store.368// C4 only sets constraint "SHA1 usage TLSServer" on client side;369// S4 only sets constraint "SHA1 usage TLSClient" on server side with client auth.370// The connection of the both cases should not be blocked.371case "C4":372testConstraint(373new String[] { "ROOT_CA_SHA1" },374new String[] { "INTER_CA_SHA256-ROOT_CA_SHA1" },375NOSHA1,376TLSSERVER,377true);378break;379case "S4":380testConstraint(381new String[] { "ROOT_CA_SHA1" },382new String[] { "INTER_CA_SHA256-ROOT_CA_SHA1" },383TLSCLIENT,384NOSHA1,385true,386true);387break;388389// Cases C5 and S5 use SHA1 root CA in trust store,390// and use SHA256 intermediate CA and SHA256 end entity in key store.391// C5 only sets constraint "SHA1 usage TLSServer" on client side;392// S5 only sets constraint "SHA1 usage TLSClient" on server side with client auth.393// The connection of the both cases should not be blocked.394case "C5":395testConstraint(396new String[] { "ROOT_CA_SHA1" },397new String[] {398"END_ENTITY_SHA256-INTER_CA_SHA256-ROOT_CA_SHA1",399"INTER_CA_SHA256-ROOT_CA_SHA1" },400NOSHA1,401TLSSERVER,402true);403break;404case "S5":405testConstraint(406new String[] { "ROOT_CA_SHA1" },407new String[] {408"END_ENTITY_SHA256-INTER_CA_SHA256-ROOT_CA_SHA1",409"INTER_CA_SHA256-ROOT_CA_SHA1" },410TLSCLIENT,411NOSHA1,412true,413true);414break;415416// Cases C6 and S6 use SHA1 root CA as trust store,417// and use SHA1 intermediate CA and SHA256 end entity in key store.418// C6 only sets constraint "SHA1 usage TLSServer" on client side;419// S6 only sets constraint "SHA1 usage TLSClient" on server side with client auth.420// The connection of the both cases should be blocked.421case "C6":422testConstraint(423new String[] { "ROOT_CA_SHA1" },424new String[] {425"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA1",426"INTER_CA_SHA1-ROOT_CA_SHA1" },427NOSHA1,428TLSSERVER,429false);430break;431case "S6":432testConstraint(433new String[] { "ROOT_CA_SHA1" },434new String[] {435"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA1",436"INTER_CA_SHA1-ROOT_CA_SHA1" },437TLSCLIENT,438NOSHA1,439true,440false);441break;442443// Cases C7 and S7 use SHA256 root CA in trust store,444// and use SHA256 intermediate CA and SHA1 end entity in key store.445// C7 only sets constraint "SHA1 usage TLSServer" on client side;446// S7 only sets constraint "SHA1 usage TLSClient" on server side with client auth.447// The connection of the both cases should be blocked.448case "C7":449testConstraint(450new String[] { "ROOT_CA_SHA256" },451new String[] {452"END_ENTITY_SHA1-INTER_CA_SHA256-ROOT_CA_SHA256",453"INTER_CA_SHA256-ROOT_CA_SHA256" },454NOSHA1,455TLSSERVER,456false);457break;458case "S7":459testConstraint(460new String[] { "ROOT_CA_SHA256" },461new String[] {462"END_ENTITY_SHA1-INTER_CA_SHA256-ROOT_CA_SHA256",463"INTER_CA_SHA256-ROOT_CA_SHA256" },464TLSCLIENT,465NOSHA1,466true,467false);468break;469470// Cases C8 and S8 use SHA256 root CA in trust store,471// and use SHA1 intermediate CA and SHA256 end entity in key store.472// C8 only sets constraint "SHA1 usage TLSServer" on client side;473// S8 only sets constraint "SHA1 usage TLSClient" on server side with client auth.474// The connection of the both cases should be blocked.475case "C8":476testConstraint(477new String[] { "ROOT_CA_SHA256" },478new String[] {479"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256",480"INTER_CA_SHA1-ROOT_CA_SHA256" },481NOSHA1,482TLSSERVER,483false);484break;485case "S8":486testConstraint(487new String[] { "ROOT_CA_SHA256" },488new String[] {489"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256",490"INTER_CA_SHA1-ROOT_CA_SHA256" },491TLSCLIENT,492NOSHA1,493true,494false);495break;496497// Cases C9 and S9 use SHA256 root CA and SHA1 intermediate CA in trust store,498// and use SHA256 end entity in key store.499// C9 only sets constraint "SHA1 usage TLSServer" on client side;500// S9 only sets constraint "SHA1 usage TLSClient" on server side with client auth.501// The connection of the both cases should not be blocked.502case "C9":503testConstraint(504new String[] {505"ROOT_CA_SHA256",506"INTER_CA_SHA1-ROOT_CA_SHA256" },507new String[] {508"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256" },509NOSHA1,510TLSSERVER,511true);512break;513case "S9":514testConstraint(515new String[] {516"ROOT_CA_SHA256",517"INTER_CA_SHA1-ROOT_CA_SHA256" },518new String[] {519"END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256" },520TLSCLIENT,521NOSHA1,522true,523true);524break;525}526System.out.println("Case passed");527System.out.println("========================================");528}529530private static String loadCert(String certName) {531try {532Path certFilePath = Paths.get(CERT_DIR, certName + ".cer");533return String.join("\n",534Files.lines(certFilePath).filter((String line) -> {535return !line.startsWith("Certificate")536&& !line.startsWith(" ");537}).collect(Collectors.toList()));538} catch (IOException e) {539throw new RuntimeException("Load certificate failed", e);540}541}542543private static String loadPrivKey(String certName) {544Path priveKeyFilePath = Paths.get(CERT_DIR, certName + "-PRIV.key");545try {546return new String(Files.readAllBytes(priveKeyFilePath));547} catch (IOException e) {548throw new RuntimeException("Load private key failed", e);549}550}551}552553554