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