1 /* 2 * Copyright (c) 2002, 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.*; 25 import java.net.*; 26 import java.util.*; 27 import java.util.concurrent.*; 28 29 import java.security.*; 30 import java.security.cert.*; 31 32 import javax.net.ssl.*; 33 34 /** 35 * Test that all ciphersuites work in all versions and all client 36 * authentication types. The way this is setup the server is stateless and 37 * all checking is done on the client side. 38 * 39 * The test is multithreaded to speed it up, especially on multiprocessor 40 * machines. To simplify debugging, run with -DnumThreads=1. 41 * 42 * @author Andreas Sterbenz 43 */ 44 public class CipherTest { 45 46 // use any available port for the server socket 47 static volatile int serverPort = 0; 48 49 static final int THREADS = Integer.getInteger("numThreads", 4); 50 static final String TEST_SRC = System.getProperty("test.src", "."); 51 52 // assume that if we do not read anything for 20 seconds, something 53 // has gone wrong 54 final static int TIMEOUT = 20 * 1000; 55 56 static KeyStore trustStore, keyStore; 57 static X509ExtendedKeyManager keyManager; 58 static X509TrustManager trustManager; 59 static SecureRandom secureRandom; 60 61 private static PeerFactory peerFactory; 62 63 static final CountDownLatch clientCondition = new CountDownLatch(1); 64 65 static abstract class Server implements Runnable { 66 67 final CipherTest cipherTest; 68 69 Server(CipherTest cipherTest) throws Exception { 70 this.cipherTest = cipherTest; 71 } 72 73 @Override 74 public abstract void run(); 75 76 void handleRequest(InputStream in, OutputStream out) throws IOException { 77 boolean newline = false; 78 StringBuilder sb = new StringBuilder(); 79 while (true) { 80 int ch = in.read(); 81 if (ch < 0) { 82 throw new EOFException(); 83 } 84 sb.append((char)ch); 85 if (ch == '\r') { 86 // empty 87 } else if (ch == '\n') { 88 if (newline) { 89 // 2nd newline in a row, end of request 90 break; 91 } 92 newline = true; 93 } else { 94 newline = false; 95 } 96 } 97 String request = sb.toString(); 98 if (request.startsWith("GET / HTTP/1.") == false) { 99 throw new IOException("Invalid request: " + request); 100 } 101 out.write("HTTP/1.0 200 OK\r\n\r\n".getBytes()); 102 } 103 104 } 105 106 public static class TestParameters { 107 108 String cipherSuite; 109 String protocol; 110 String clientAuth; 111 112 TestParameters(String cipherSuite, String protocol, 113 String clientAuth) { 114 this.cipherSuite = cipherSuite; 115 this.protocol = protocol; 116 this.clientAuth = clientAuth; 117 } 118 119 boolean isEnabled() { 120 return TLSCipherStatus.isEnabled(cipherSuite, protocol); 121 } 122 123 @Override 124 public String toString() { 125 String s = cipherSuite + " in " + protocol + " mode"; 126 if (clientAuth != null) { 127 s += " with " + clientAuth + " client authentication"; 128 } 129 return s; 130 } 131 132 static enum TLSCipherStatus { 133 // cipher suites supported since TLS 1.2 134 CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 135 CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 136 CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 137 CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 138 CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 139 CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 140 CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 141 142 CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 143 CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 144 CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 145 CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 146 CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 147 CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 148 CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 149 150 CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 151 CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 152 CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), 153 154 CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 155 CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 156 CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 157 CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 158 CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 159 CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 160 CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 161 CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 162 163 CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 164 CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 165 CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 166 CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 167 CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 168 CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 169 170 CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 171 CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 172 173 // cipher suites obsoleted since TLS 1.2 174 CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), 175 CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), 176 CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0000, 0x0303), 177 CS_53("SSL_DH_anon_WITH_DES_CBC_SHA", 0x0000, 0x0303), 178 CS_54("TLS_KRB5_WITH_DES_CBC_SHA", 0x0000, 0x0303), 179 CS_55("TLS_KRB5_WITH_DES_CBC_MD5", 0x0000, 0x0303), 180 181 // cipher suites obsoleted since TLS 1.1 182 CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 183 CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 184 CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 185 CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 186 CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 187 CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 188 CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0000, 0x0302), 189 CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 190 CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0000, 0x0302), 191 CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0000, 0x0302), 192 193 // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always 194 CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0xFFFF, 0x0000); 195 196 // the cipher suite name 197 final String cipherSuite; 198 199 // supported since protocol version 200 final int supportedSince; 201 202 // obsoleted since protocol version 203 final int obsoletedSince; 204 205 TLSCipherStatus(String cipherSuite, 206 int supportedSince, int obsoletedSince) { 207 this.cipherSuite = cipherSuite; 208 this.supportedSince = supportedSince; 209 this.obsoletedSince = obsoletedSince; 210 } 211 212 static boolean isEnabled(String cipherSuite, String protocol) { 213 int versionNumber = toVersionNumber(protocol); 214 215 if (versionNumber < 0) { 216 return true; // unlikely to happen 217 } 218 219 for (TLSCipherStatus status : TLSCipherStatus.values()) { 220 if (cipherSuite.equals(status.cipherSuite)) { 221 if ((versionNumber < status.supportedSince) || 222 (versionNumber >= status.obsoletedSince)) { 223 return false; 224 } 225 226 return true; 227 } 228 } 229 230 return true; 231 } 232 233 private static int toVersionNumber(String protocol) { 234 int versionNumber = -1; 235 236 switch (protocol) { 237 case "SSLv2Hello": 238 versionNumber = 0x0002; 239 break; 240 case "SSLv3": 241 versionNumber = 0x0300; 242 break; 243 case "TLSv1": 244 versionNumber = 0x0301; 245 break; 246 case "TLSv1.1": 247 versionNumber = 0x0302; 248 break; 249 case "TLSv1.2": 250 versionNumber = 0x0303; 251 break; 252 default: 253 // unlikely to happen 254 } 255 256 return versionNumber; 257 } 258 } 259 } 260 261 private List<TestParameters> tests; 262 private Iterator<TestParameters> testIterator; 263 private SSLSocketFactory factory; 264 private boolean failed; 265 266 private CipherTest(PeerFactory peerFactory) throws IOException { 267 factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); 268 SSLSocket socket = (SSLSocket)factory.createSocket(); 269 String[] cipherSuites = socket.getSupportedCipherSuites(); 270 String[] protocols = socket.getSupportedProtocols(); 271 String[] clientAuths = {null, "RSA", "DSA", "ECDSA"}; 272 tests = new ArrayList<TestParameters>( 273 cipherSuites.length * protocols.length * clientAuths.length); 274 for (int i = 0; i < cipherSuites.length; i++) { 275 String cipherSuite = cipherSuites[i]; 276 277 for (int j = 0; j < protocols.length; j++) { 278 String protocol = protocols[j]; 279 280 if (!peerFactory.isSupported(cipherSuite, protocol)) { 281 continue; 282 } 283 284 for (int k = 0; k < clientAuths.length; k++) { 285 String clientAuth = clientAuths[k]; 286 if ((clientAuth != null) && 287 (cipherSuite.indexOf("DH_anon") != -1)) { 288 // no client with anonymous ciphersuites 289 continue; 290 } 291 292 tests.add(new TestParameters(cipherSuite, protocol, 293 clientAuth)); 294 } 295 } 296 } 297 298 testIterator = tests.iterator(); 299 } 300 301 synchronized void setFailed() { 302 failed = true; 303 } 304 305 public void run() throws Exception { 306 Thread[] threads = new Thread[THREADS]; 307 for (int i = 0; i < THREADS; i++) { 308 try { 309 threads[i] = new Thread(peerFactory.newClient(this), 310 "Client " + i); 311 } catch (Exception e) { 312 e.printStackTrace(); 313 return; 314 } 315 threads[i].start(); 316 } 317 318 // The client threads are ready. 319 clientCondition.countDown(); 320 321 try { 322 for (int i = 0; i < THREADS; i++) { 323 threads[i].join(); 324 } 325 } catch (InterruptedException e) { 326 setFailed(); 327 e.printStackTrace(); 328 } 329 if (failed) { 330 throw new Exception("*** Test '" + peerFactory.getName() + 331 "' failed ***"); 332 } else { 333 System.out.println("Test '" + peerFactory.getName() + 334 "' completed successfully"); 335 } 336 } 337 338 synchronized TestParameters getTest() { 339 if (failed) { 340 return null; 341 } 342 if (testIterator.hasNext()) { 343 return (TestParameters)testIterator.next(); 344 } 345 return null; 346 } 347 348 SSLSocketFactory getFactory() { 349 return factory; 350 } 351 352 static abstract class Client implements Runnable { 353 354 final CipherTest cipherTest; 355 356 Client(CipherTest cipherTest) throws Exception { 357 this.cipherTest = cipherTest; 358 } 359 360 @Override 361 public final void run() { 362 while (true) { 363 TestParameters params = cipherTest.getTest(); 364 if (params == null) { 365 // no more tests 366 break; 367 } 368 if (params.isEnabled() == false) { 369 System.out.println("Skipping disabled test " + params); 370 continue; 371 } 372 try { 373 runTest(params); 374 System.out.println("Passed " + params); 375 } catch (SocketTimeoutException ste) { 376 System.out.println("The client connects to the server timeout, " 377 + "so ignore the test."); 378 break; 379 } catch (Exception e) { 380 cipherTest.setFailed(); 381 System.out.println("** Failed " + params + "**"); 382 e.printStackTrace(); 383 } 384 } 385 } 386 387 abstract void runTest(TestParameters params) throws Exception; 388 389 void sendRequest(InputStream in, OutputStream out) throws IOException { 390 out.write("GET / HTTP/1.0\r\n\r\n".getBytes()); 391 out.flush(); 392 StringBuilder sb = new StringBuilder(); 393 while (true) { 394 int ch = in.read(); 395 if (ch < 0) { 396 break; 397 } 398 sb.append((char)ch); 399 } 400 String response = sb.toString(); 401 if (response.startsWith("HTTP/1.0 200 ") == false) { 402 throw new IOException("Invalid response: " + response); 403 } 404 } 405 406 } 407 408 // for some reason, ${test.src} has a different value when the 409 // test is called from the script and when it is called directly... 410 static String pathToStores = "."; 411 static String pathToStoresSH = "."; 412 static String keyStoreFile = "keystore"; 413 static String trustStoreFile = "truststore"; 414 static char[] passwd = "passphrase".toCharArray(); 415 416 static File PATH; 417 418 private static KeyStore readKeyStore(String name) throws Exception { 419 File file = new File(PATH, name); 420 KeyStore ks; 421 try (InputStream in = new FileInputStream(file)) { 422 ks = KeyStore.getInstance("JKS"); 423 ks.load(in, passwd); 424 } 425 return ks; 426 } 427 428 public static void main(PeerFactory peerFactory, String[] args) 429 throws Exception { 430 long time = System.currentTimeMillis(); 431 String relPath; 432 if ((args != null) && (args.length > 0) && args[0].equals("sh")) { 433 relPath = pathToStoresSH; 434 } else { 435 relPath = pathToStores; 436 } 437 PATH = new File(TEST_SRC, relPath); 438 CipherTest.peerFactory = peerFactory; 439 System.out.print( 440 "Initializing test '" + peerFactory.getName() + "'..."); 441 secureRandom = new SecureRandom(); 442 secureRandom.nextInt(); 443 trustStore = readKeyStore(trustStoreFile); 444 keyStore = readKeyStore(keyStoreFile); 445 KeyManagerFactory keyFactory = 446 KeyManagerFactory.getInstance( 447 KeyManagerFactory.getDefaultAlgorithm()); 448 keyFactory.init(keyStore, passwd); 449 keyManager = (X509ExtendedKeyManager)keyFactory.getKeyManagers()[0]; 450 trustManager = new AlwaysTrustManager(); 451 452 CipherTest cipherTest = new CipherTest(peerFactory); 453 Thread serverThread = new Thread(peerFactory.newServer(cipherTest), 454 "Server"); 455 serverThread.setDaemon(true); 456 serverThread.start(); 457 System.out.println("Done"); 458 cipherTest.run(); 459 time = System.currentTimeMillis() - time; 460 System.out.println("Done. (" + time + " ms)"); 461 } 462 463 static abstract class PeerFactory { 464 465 abstract String getName(); 466 467 abstract Client newClient(CipherTest cipherTest) throws Exception; 468 469 abstract Server newServer(CipherTest cipherTest) throws Exception; 470 471 boolean isSupported(String cipherSuite, String protocol) { 472 // skip kerberos cipher suites 473 if (cipherSuite.startsWith("TLS_KRB5")) { 474 System.out.println("Skipping unsupported test for " + 475 cipherSuite + " of " + protocol); 476 return false; 477 } 478 479 // skip SSLv2Hello protocol 480 if (protocol.equals("SSLv2Hello")) { 481 System.out.println("Skipping unsupported test for " + 482 cipherSuite + " of " + protocol); 483 return false; 484 } 485 486 // ignore exportable cipher suite for TLSv1.1 487 if (protocol.equals("TLSv1.1")) { 488 if (cipherSuite.indexOf("_EXPORT_WITH") != -1) { 489 System.out.println("Skipping obsoleted test for " + 490 cipherSuite + " of " + protocol); 491 return false; 492 } 493 } 494 495 return true; 496 } 497 } 498 499 } 500 501 // we currently don't do any chain verification. we assume that works ok 502 // and we can speed up the test. we could also just add a plain certificate 503 // chain comparision with our trusted certificates. 504 class AlwaysTrustManager implements X509TrustManager { 505 506 public AlwaysTrustManager() { 507 508 } 509 510 @Override 511 public void checkClientTrusted(X509Certificate[] chain, String authType) 512 throws CertificateException { 513 // empty 514 } 515 516 @Override 517 public void checkServerTrusted(X509Certificate[] chain, String authType) 518 throws CertificateException { 519 // empty 520 } 521 522 @Override 523 public X509Certificate[] getAcceptedIssuers() { 524 return new X509Certificate[0]; 525 } 526 } 527 528 class MyX509KeyManager extends X509ExtendedKeyManager { 529 530 private final X509ExtendedKeyManager keyManager; 531 private String authType; 532 533 MyX509KeyManager(X509ExtendedKeyManager keyManager) { 534 this.keyManager = keyManager; 535 } 536 537 void setAuthType(String authType) { 538 this.authType = "ECDSA".equals(authType) ? "EC" : authType; 539 } 540 541 @Override 542 public String[] getClientAliases(String keyType, Principal[] issuers) { 543 if (authType == null) { 544 return null; 545 } 546 return keyManager.getClientAliases(authType, issuers); 547 } 548 549 @Override 550 public String chooseClientAlias(String[] keyType, Principal[] issuers, 551 Socket socket) { 552 if (authType == null) { 553 return null; 554 } 555 return keyManager.chooseClientAlias(new String[] {authType}, 556 issuers, socket); 557 } 558 559 @Override 560 public String chooseEngineClientAlias(String[] keyType, 561 Principal[] issuers, SSLEngine engine) { 562 if (authType == null) { 563 return null; 564 } 565 return keyManager.chooseEngineClientAlias(new String[] {authType}, 566 issuers, engine); 567 } 568 569 @Override 570 public String[] getServerAliases(String keyType, Principal[] issuers) { 571 throw new UnsupportedOperationException("Servers not supported"); 572 } 573 574 @Override 575 public String chooseServerAlias(String keyType, Principal[] issuers, 576 Socket socket) { 577 throw new UnsupportedOperationException("Servers not supported"); 578 } 579 580 @Override 581 public String chooseEngineServerAlias(String keyType, Principal[] issuers, 582 SSLEngine engine) { 583 throw new UnsupportedOperationException("Servers not supported"); 584 } 585 586 @Override 587 public X509Certificate[] getCertificateChain(String alias) { 588 return keyManager.getCertificateChain(alias); 589 } 590 591 @Override 592 public PrivateKey getPrivateKey(String alias) { 593 return keyManager.getPrivateKey(alias); 594 } 595 596 } 597 598 class DaemonThreadFactory implements ThreadFactory { 599 600 final static ThreadFactory INSTANCE = new DaemonThreadFactory(); 601 602 private final static ThreadFactory DEFAULT = Executors.defaultThreadFactory(); 603 604 @Override 605 public Thread newThread(Runnable r) { 606 Thread t = DEFAULT.newThread(r); 607 t.setDaemon(true); 608 return t; 609 } 610 611 }