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