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.IOException; 26 import java.io.InputStream; 27 import java.net.SocketTimeoutException; 28 import java.nio.file.Files; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.security.KeyFactory; 32 import java.security.KeyStore; 33 import java.security.PrivateKey; 34 import java.security.Security; 35 import java.security.cert.Certificate; 36 import java.security.cert.CertificateFactory; 37 import java.security.spec.PKCS8EncodedKeySpec; 38 import java.util.Base64; 39 import java.util.stream.Collectors; 40 41 import javax.net.ssl.KeyManagerFactory; 42 import javax.net.ssl.SSLContext; 43 import javax.net.ssl.SSLHandshakeException; 44 import javax.net.ssl.TrustManagerFactory; 45 46 import jdk.test.lib.process.OutputAnalyzer; 47 import jdk.test.lib.process.ProcessTools; 48 49 /* 50 * @test 51 * @summary Verify the restrictions for certificate path on JSSE with custom trust store. 52 * @library /test/lib 53 * @build jdk.test.lib.Utils 54 * jdk.test.lib.Asserts 55 * jdk.test.lib.JDKToolFinder 56 * jdk.test.lib.JDKToolLauncher 57 * jdk.test.lib.Platform 58 * jdk.test.lib.process.* 59 * @compile JSSEClient.java 60 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions DEFAULT 61 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C1 62 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S1 63 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C2 64 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S2 65 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C3 66 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S3 67 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C4 68 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S4 69 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C5 70 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S5 71 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C6 72 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S6 73 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C7 74 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S7 75 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C8 76 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S8 77 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions C9 78 * @run main/othervm -Djava.security.debug=certpath TLSRestrictions S9 79 */ 80 public class TLSRestrictions { 81 82 private static final String TEST_CLASSES = System.getProperty("test.classes"); 83 private static final char[] PASSWORD = "".toCharArray(); 84 private static final String CERT_DIR = System.getProperty("cert.dir", 85 System.getProperty("test.src") + "/certs"); 86 87 static final String PROP = "jdk.certpath.disabledAlgorithms"; 88 static final String NOSHA1 = "MD2, MD5"; 89 private static final String TLSSERVER = "SHA1 usage TLSServer"; 90 private static final String TLSCLIENT = "SHA1 usage TLSClient"; 91 static final String JDKCATLSSERVER = "SHA1 jdkCA & usage TLSServer"; 92 static final String JDKCATLSCLIENT = "SHA1 jdkCA & usage TLSClient"; 93 94 // This is a space holder in command arguments, and stands for none certificate. 95 static final String NONE_CERT = "NONE_CERT"; 96 97 static final String DELIMITER = ","; 98 static final int TIMEOUT = 30000; 99 100 // It checks if java.security contains constraint "SHA1 jdkCA & usage TLSServer" 101 // for jdk.certpath.disabledAlgorithms by default. 102 private static void checkDefaultConstraint() { 103 System.out.println( 104 "Case: Checks the default value of jdk.certpath.disabledAlgorithms"); 105 if (!Security.getProperty(PROP).contains(JDKCATLSSERVER)) { 106 throw new RuntimeException(String.format( 107 "%s doesn't contain constraint \"%s\", the real value is \"%s\".", 108 PROP, JDKCATLSSERVER, Security.getProperty(PROP))); 109 } 110 } 111 112 /* 113 * This method creates trust store and key store with specified certificates 114 * respectively. And then it creates SSL context with the stores. 115 * If trustNames contains NONE_CERT only, it does not create a custom trust 116 * store, but the default one in JDK. 117 * 118 * @param trustNames Trust anchors, which are used to create custom trust store. 119 * If null, no custom trust store is created and the default 120 * trust store in JDK is used. 121 * @param certNames Certificate chain, which is used to create key store. 122 * It cannot be null. 123 */ 124 static SSLContext createSSLContext(String[] trustNames, 125 String[] certNames) throws Exception { 126 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 127 128 TrustManagerFactory tmf = null; 129 if (trustNames != null && trustNames.length > 0 130 && !trustNames[0].equals(NONE_CERT)) { 131 KeyStore trustStore = KeyStore.getInstance("JKS"); 132 trustStore.load(null, null); 133 for (int i = 0; i < trustNames.length; i++) { 134 try (InputStream is = new ByteArrayInputStream( 135 loadCert(trustNames[i]).getBytes())) { 136 Certificate trustCert = certFactory.generateCertificate(is); 137 trustStore.setCertificateEntry("trustCert-" + i, trustCert); 138 } 139 } 140 141 tmf = TrustManagerFactory.getInstance("PKIX"); 142 tmf.init(trustStore); 143 } 144 145 Certificate[] certChain = new Certificate[certNames.length]; 146 for (int i = 0; i < certNames.length; i++) { 147 try (InputStream is = new ByteArrayInputStream( 148 loadCert(certNames[i]).getBytes())) { 149 Certificate cert = certFactory.generateCertificate(is); 150 certChain[i] = cert; 151 } 152 } 153 154 PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec( 155 Base64.getMimeDecoder().decode(loadPrivKey(certNames[0]))); 156 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 157 PrivateKey privKey = keyFactory.generatePrivate(privKeySpec); 158 159 KeyStore keyStore = KeyStore.getInstance("JKS"); 160 keyStore.load(null, null); 161 keyStore.setKeyEntry("keyCert", privKey, PASSWORD, certChain); 162 163 KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); 164 kmf.init(keyStore, PASSWORD); 165 166 SSLContext context = SSLContext.getInstance("TLS"); 167 context.init(kmf.getKeyManagers(), 168 tmf == null ? null : tmf.getTrustManagers(), null); 169 return context; 170 } 171 172 /* 173 * This method sets jdk.certpath.disabledAlgorithms, and then retrieves 174 * and prints its value. 175 */ 176 static void setConstraint(String side, String constraint) { 177 System.out.printf("%s: Old %s=%s%n", side, PROP, 178 Security.getProperty(PROP)); 179 Security.setProperty(PROP, constraint); 180 System.out.printf("%s: New %s=%s%n", side, PROP, 181 Security.getProperty(PROP)); 182 } 183 184 /* 185 * This method is used to run a variety of cases. 186 * It launches a server, and then takes a client to connect the server. 187 * Both of server and client use the same certificates. 188 * 189 * @param trustNames Trust anchors, which are used to create custom trust store. 190 * If null, the default trust store in JDK is used. 191 * @param certNames Certificate chain, which is used to create key store. 192 * It cannot be null. The first certificate is regarded as 193 * the end entity. 194 * @param serverConstraint jdk.certpath.disabledAlgorithms value on server side. 195 * @param clientConstraint jdk.certpath.disabledAlgorithms value on client side. 196 * @param needClientAuth If true, server side acquires client authentication; 197 * otherwise, false. 198 * @param pass If true, the connection should be blocked; otherwise, false. 199 */ 200 static void testConstraint(String[] trustNames, String[] certNames, 201 String serverConstraint, String clientConstraint, 202 boolean needClientAuth, boolean pass) throws Exception { 203 String trustNameStr = trustNames == null ? "" 204 : String.join(DELIMITER, trustNames); 205 String certNameStr = certNames == null ? "" 206 : String.join(DELIMITER, certNames); 207 208 System.out.printf("Case:%n" 209 + " trustNames=%s; certNames=%s%n" 210 + " serverConstraint=%s; clientConstraint=%s%n" 211 + " needClientAuth=%s%n" 212 + " pass=%s%n%n", 213 trustNameStr, certNameStr, 214 serverConstraint, clientConstraint, 215 needClientAuth, 216 pass); 217 218 JSSEServer server = new JSSEServer( 219 createSSLContext(trustNames, certNames), 220 serverConstraint, 221 needClientAuth); 222 int port = server.getPort(); 223 server.start(); 224 225 // Run client on another JVM so that its properties cannot be in conflict 226 // with server's. 227 OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJvm( 228 "-Dcert.dir=" + CERT_DIR, 229 "-Djava.security.debug=certpath", 230 "-classpath", 231 TEST_CLASSES, 232 "JSSEClient", 233 port + "", 234 trustNameStr, 235 certNameStr, 236 clientConstraint); 237 int exitValue = outputAnalyzer.getExitValue(); 238 String clientOut = outputAnalyzer.getOutput(); 239 240 Exception serverException = server.getException(); 241 if (serverException != null) { 242 System.out.println("Server: failed"); 243 } 244 245 System.out.println("---------- Client output start ----------"); 246 System.out.println(clientOut); 247 System.out.println("---------- Client output end ----------"); 248 249 if (serverException instanceof SocketTimeoutException 250 || clientOut.contains("SocketTimeoutException")) { 251 System.out.println("The communication gets timeout and skips the test."); 252 return; 253 } 254 255 if (pass) { 256 if (serverException != null || exitValue != 0) { 257 throw new RuntimeException( 258 "Unexpected failure. Operation was blocked."); 259 } 260 } else { 261 if (serverException == null && exitValue == 0) { 262 throw new RuntimeException( 263 "Unexpected pass. Operation was allowed."); 264 } 265 266 // The test may encounter non-SSL issues, like network problem. 267 if (!(serverException instanceof SSLHandshakeException 268 || clientOut.contains("SSLHandshakeException"))) { 269 throw new RuntimeException("Failure with unexpected exception."); 270 } 271 } 272 } 273 274 /* 275 * This method is used to run a variety of cases, which don't require client 276 * authentication by default. 277 */ 278 static void testConstraint(String[] trustNames, String[] certNames, 279 String serverConstraint, String clientConstraint, boolean pass) 280 throws Exception { 281 testConstraint(trustNames, certNames, serverConstraint, clientConstraint, 282 false, pass); 283 } 284 285 public static void main(String[] args) throws Exception { 286 switch (args[0]) { 287 // Case DEFAULT only checks one of default settings for 288 // jdk.certpath.disabledAlgorithms in JDK/conf/security/java.security. 289 case "DEFAULT": 290 checkDefaultConstraint(); 291 break; 292 293 // Cases C1 and S1 use SHA256 root CA in trust store, 294 // and use SHA256 end entity in key store. 295 // C1 only sets constraint "SHA1 usage TLSServer" on client side; 296 // S1 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 297 // The connection of the both cases should not be blocked. 298 case "C1": 299 testConstraint( 300 new String[] { "ROOT_CA_SHA256" }, 301 new String[] { "INTER_CA_SHA256-ROOT_CA_SHA256" }, 302 NOSHA1, 303 TLSSERVER, 304 true); 305 break; 306 case "S1": 307 testConstraint( 308 new String[] { "ROOT_CA_SHA256" }, 309 new String[] { "INTER_CA_SHA256-ROOT_CA_SHA256" }, 310 TLSCLIENT, 311 NOSHA1, 312 true, 313 true); 314 break; 315 316 // Cases C2 and S2 use SHA256 root CA in trust store, 317 // and use SHA1 end entity in key store. 318 // C2 only sets constraint "SHA1 usage TLSServer" on client side; 319 // S2 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 320 // The connection of the both cases should be blocked. 321 case "C2": 322 testConstraint( 323 new String[] { "ROOT_CA_SHA256" }, 324 new String[] { "INTER_CA_SHA1-ROOT_CA_SHA256" }, 325 NOSHA1, 326 TLSSERVER, 327 false); 328 break; 329 case "S2": 330 testConstraint( 331 new String[] { "ROOT_CA_SHA256" }, 332 new String[] { "INTER_CA_SHA1-ROOT_CA_SHA256" }, 333 TLSCLIENT, 334 NOSHA1, 335 true, 336 false); 337 break; 338 339 // Cases C3 and S3 use SHA1 root CA in trust store, 340 // and use SHA1 end entity in key store. 341 // C3 only sets constraint "SHA1 usage TLSServer" on client side; 342 // S3 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 343 // The connection of the both cases should be blocked. 344 case "C3": 345 testConstraint( 346 new String[] { "ROOT_CA_SHA1" }, 347 new String[] { "INTER_CA_SHA1-ROOT_CA_SHA1" }, 348 NOSHA1, 349 TLSSERVER, 350 false); 351 break; 352 case "S3": 353 testConstraint( 354 new String[] { "ROOT_CA_SHA1" }, 355 new String[] { "INTER_CA_SHA1-ROOT_CA_SHA1" }, 356 TLSCLIENT, 357 NOSHA1, 358 true, 359 false); 360 break; 361 362 // Cases C4 and S4 use SHA1 root CA as trust store, 363 // and use SHA256 end entity in key store. 364 // C4 only sets constraint "SHA1 usage TLSServer" on client side; 365 // S4 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 366 // The connection of the both cases should not be blocked. 367 case "C4": 368 testConstraint( 369 new String[] { "ROOT_CA_SHA1" }, 370 new String[] { "INTER_CA_SHA256-ROOT_CA_SHA1" }, 371 NOSHA1, 372 TLSSERVER, 373 true); 374 break; 375 case "S4": 376 testConstraint( 377 new String[] { "ROOT_CA_SHA1" }, 378 new String[] { "INTER_CA_SHA256-ROOT_CA_SHA1" }, 379 TLSCLIENT, 380 NOSHA1, 381 true, 382 true); 383 break; 384 385 // Cases C5 and S5 use SHA1 root CA in trust store, 386 // and use SHA256 intermediate CA and SHA256 end entity in key store. 387 // C5 only sets constraint "SHA1 usage TLSServer" on client side; 388 // S5 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 389 // The connection of the both cases should not be blocked. 390 case "C5": 391 testConstraint( 392 new String[] { "ROOT_CA_SHA1" }, 393 new String[] { 394 "END_ENTITY_SHA256-INTER_CA_SHA256-ROOT_CA_SHA1", 395 "INTER_CA_SHA256-ROOT_CA_SHA1" }, 396 NOSHA1, 397 TLSSERVER, 398 true); 399 break; 400 case "S5": 401 testConstraint( 402 new String[] { "ROOT_CA_SHA1" }, 403 new String[] { 404 "END_ENTITY_SHA256-INTER_CA_SHA256-ROOT_CA_SHA1", 405 "INTER_CA_SHA256-ROOT_CA_SHA1" }, 406 TLSCLIENT, 407 NOSHA1, 408 true, 409 true); 410 break; 411 412 // Cases C6 and S6 use SHA1 root CA as trust store, 413 // and use SHA1 intermediate CA and SHA256 end entity in key store. 414 // C6 only sets constraint "SHA1 usage TLSServer" on client side; 415 // S6 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 416 // The connection of the both cases should be blocked. 417 case "C6": 418 testConstraint( 419 new String[] { "ROOT_CA_SHA1" }, 420 new String[] { 421 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA1", 422 "INTER_CA_SHA1-ROOT_CA_SHA1" }, 423 NOSHA1, 424 TLSSERVER, 425 false); 426 break; 427 case "S6": 428 testConstraint( 429 new String[] { "ROOT_CA_SHA1" }, 430 new String[] { 431 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA1", 432 "INTER_CA_SHA1-ROOT_CA_SHA1" }, 433 TLSCLIENT, 434 NOSHA1, 435 true, 436 false); 437 break; 438 439 // Cases C7 and S7 use SHA256 root CA in trust store, 440 // and use SHA256 intermediate CA and SHA1 end entity in key store. 441 // C7 only sets constraint "SHA1 usage TLSServer" on client side; 442 // S7 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 443 // The connection of the both cases should be blocked. 444 case "C7": 445 testConstraint( 446 new String[] { "ROOT_CA_SHA256" }, 447 new String[] { 448 "END_ENTITY_SHA1-INTER_CA_SHA256-ROOT_CA_SHA256", 449 "INTER_CA_SHA256-ROOT_CA_SHA256" }, 450 NOSHA1, 451 TLSSERVER, 452 false); 453 break; 454 case "S7": 455 testConstraint( 456 new String[] { "ROOT_CA_SHA256" }, 457 new String[] { 458 "END_ENTITY_SHA1-INTER_CA_SHA256-ROOT_CA_SHA256", 459 "INTER_CA_SHA256-ROOT_CA_SHA256" }, 460 TLSCLIENT, 461 NOSHA1, 462 true, 463 false); 464 break; 465 466 // Cases C8 and S8 use SHA256 root CA in trust store, 467 // and use SHA1 intermediate CA and SHA256 end entity in key store. 468 // C8 only sets constraint "SHA1 usage TLSServer" on client side; 469 // S8 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 470 // The connection of the both cases should be blocked. 471 case "C8": 472 testConstraint( 473 new String[] { "ROOT_CA_SHA256" }, 474 new String[] { 475 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256", 476 "INTER_CA_SHA1-ROOT_CA_SHA256" }, 477 NOSHA1, 478 TLSSERVER, 479 false); 480 break; 481 case "S8": 482 testConstraint( 483 new String[] { "ROOT_CA_SHA256" }, 484 new String[] { 485 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256", 486 "INTER_CA_SHA1-ROOT_CA_SHA256" }, 487 TLSCLIENT, 488 NOSHA1, 489 true, 490 false); 491 break; 492 493 // Cases C9 and S9 use SHA256 root CA and SHA1 intermediate CA in trust store, 494 // and use SHA256 end entity in key store. 495 // C9 only sets constraint "SHA1 usage TLSServer" on client side; 496 // S9 only sets constraint "SHA1 usage TLSClient" on server side with client auth. 497 // The connection of the both cases should not be blocked. 498 case "C9": 499 testConstraint( 500 new String[] { 501 "ROOT_CA_SHA256", 502 "INTER_CA_SHA1-ROOT_CA_SHA256" }, 503 new String[] { 504 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256" }, 505 NOSHA1, 506 TLSSERVER, 507 true); 508 break; 509 case "S9": 510 testConstraint( 511 new String[] { 512 "ROOT_CA_SHA256", 513 "INTER_CA_SHA1-ROOT_CA_SHA256" }, 514 new String[] { 515 "END_ENTITY_SHA256-INTER_CA_SHA1-ROOT_CA_SHA256" }, 516 TLSCLIENT, 517 NOSHA1, 518 true, 519 true); 520 break; 521 } 522 523 System.out.println("Case passed"); 524 System.out.println("========================================"); 525 } 526 527 private static String loadCert(String certName) { 528 try { 529 Path certFilePath = Paths.get(CERT_DIR, certName + ".cer"); 530 return String.join("\n", 531 Files.lines(certFilePath).filter((String line) -> { 532 return !line.startsWith("Certificate") 533 && !line.startsWith(" "); 534 }).collect(Collectors.toList())); 535 } catch (IOException e) { 536 throw new RuntimeException("Load certificate failed", e); 537 } 538 } 539 540 private static String loadPrivKey(String certName) { 541 Path priveKeyFilePath = Paths.get(CERT_DIR, certName + "-PRIV.key"); 542 try { 543 return new String(Files.readAllBytes(priveKeyFilePath)); 544 } catch (IOException e) { 545 throw new RuntimeException("Load private key failed", e); 546 } 547 } 548 }