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 24import java.io.*; 25import java.net.*; 26import java.util.*; 27import java.util.concurrent.*; 28 29import java.security.*; 30import java.security.cert.*; 31import java.security.cert.Certificate; 32 33import javax.net.ssl.*; 34 35/** 36 * Test that all ciphersuites work in all versions and all client 37 * authentication types. The way this is setup the server is stateless and 38 * all checking is done on the client side. 39 * 40 * The test is multithreaded to speed it up, especially on multiprocessor 41 * machines. To simplify debugging, run with -DnumThreads=1. 42 * 43 * @author Andreas Sterbenz 44 */ 45public class CipherTest { 46 47 // use any available port for the server socket 48 static int serverPort = 0; 49 50 final int THREADS; 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 abstract class Server implements Runnable { 64 65 final CipherTest cipherTest; 66 67 Server(CipherTest cipherTest) throws Exception { 68 this.cipherTest = cipherTest; 69 } 70 71 public abstract void run(); 72 73 void handleRequest(InputStream in, OutputStream out) throws IOException { 74 boolean newline = false; 75 StringBuilder sb = new StringBuilder(); 76 while (true) { 77 int ch = in.read(); 78 if (ch < 0) { 79 throw new EOFException(); 80 } 81 sb.append((char)ch); 82 if (ch == '\r') { 83 // empty 84 } else if (ch == '\n') { 85 if (newline) { 86 // 2nd newline in a row, end of request 87 break; 88 } 89 newline = true; 90 } else { 91 newline = false; 92 } 93 } 94 String request = sb.toString(); 95 if (request.startsWith("GET / HTTP/1.") == false) { 96 throw new IOException("Invalid request: " + request); 97 } 98 out.write("HTTP/1.0 200 OK\r\n\r\n".getBytes()); 99 } 100 101 } 102 103 public static class TestParameters { 104 105 String cipherSuite; 106 String protocol; 107 String clientAuth; 108 109 TestParameters(String cipherSuite, String protocol, 110 String clientAuth) { 111 this.cipherSuite = cipherSuite; 112 this.protocol = protocol; 113 this.clientAuth = clientAuth; 114 } 115 116 boolean isEnabled() { 117 return TLSCipherStatus.isEnabled(cipherSuite, protocol); 118 } 119 120 public String toString() { 121 String s = cipherSuite + " in " + protocol + " mode"; 122 if (clientAuth != null) { 123 s += " with " + clientAuth + " client authentication"; 124 } 125 return s; 126 } 127 128 static enum TLSCipherStatus { 129 // cipher suites supported since TLS 1.2 130 CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 131 CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 132 CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 133 CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 134 CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), 135 CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 136 CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 137 138 CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 139 CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 140 CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 141 CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 142 CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 143 CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 144 CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 145 146 CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), 147 CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), 148 CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), 149 150 CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 151 CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 152 CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 153 CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 154 CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 155 CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 156 CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 157 CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 158 159 CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 160 CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 161 CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 162 CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 163 CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 164 CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 165 166 CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), 167 CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), 168 169 // cipher suites obsoleted since TLS 1.2 170 CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), 171 CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), 172 CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0000, 0x0303), 173 CS_53("SSL_DH_anon_WITH_DES_CBC_SHA", 0x0000, 0x0303), 174 CS_54("TLS_KRB5_WITH_DES_CBC_SHA", 0x0000, 0x0303), 175 CS_55("TLS_KRB5_WITH_DES_CBC_MD5", 0x0000, 0x0303), 176 177 // cipher suites obsoleted since TLS 1.1 178 CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 179 CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 180 CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 181 CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 182 CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 183 CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), 184 CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0000, 0x0302), 185 CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), 186 CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0000, 0x0302), 187 CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0000, 0x0302), 188 189 // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always 190 CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0xFFFF, 0x0000); 191 192 // the cipher suite name 193 final String cipherSuite; 194 195 // supported since protocol version 196 final int supportedSince; 197 198 // obsoleted since protocol version 199 final int obsoletedSince; 200 201 TLSCipherStatus(String cipherSuite, 202 int supportedSince, int obsoletedSince) { 203 this.cipherSuite = cipherSuite; 204 this.supportedSince = supportedSince; 205 this.obsoletedSince = obsoletedSince; 206 } 207 208 static boolean isEnabled(String cipherSuite, String protocol) { 209 int versionNumber = toVersionNumber(protocol); 210 211 if (versionNumber < 0) { 212 return true; // unlikely to happen 213 } 214 215 for (TLSCipherStatus status : TLSCipherStatus.values()) { 216 if (cipherSuite.equals(status.cipherSuite)) { 217 if ((versionNumber < status.supportedSince) || 218 (versionNumber >= status.obsoletedSince)) { 219 return false; 220 } 221 222 return true; 223 } 224 } 225 226 return true; 227 } 228 229 private static int toVersionNumber(String protocol) { 230 int versionNumber = -1; 231 232 switch (protocol) { 233 case "SSLv2Hello": 234 versionNumber = 0x0002; 235 break; 236 case "SSLv3": 237 versionNumber = 0x0300; 238 break; 239 case "TLSv1": 240 versionNumber = 0x0301; 241 break; 242 case "TLSv1.1": 243 versionNumber = 0x0302; 244 break; 245 case "TLSv1.2": 246 versionNumber = 0x0303; 247 break; 248 default: 249 // unlikely to happen 250 } 251 252 return versionNumber; 253 } 254 } 255 } 256 257 private List<TestParameters> tests; 258 private Iterator<TestParameters> testIterator; 259 private SSLSocketFactory factory; 260 private boolean failed; 261 262 private CipherTest(PeerFactory peerFactory) throws IOException { 263 THREADS = Integer.parseInt(System.getProperty("numThreads", "4")); 264 factory = (SSLSocketFactory)SSLSocketFactory.getDefault(); 265 SSLSocket socket = (SSLSocket)factory.createSocket(); 266 String[] cipherSuites = socket.getSupportedCipherSuites(); 267 String[] protocols = socket.getSupportedProtocols(); 268// String[] clientAuths = {null, "RSA", "DSA"}; 269 String[] clientAuths = {null}; 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 = "."; 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, KeyStore keyStore, 415 String[] args) 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 CipherTest.keyStore = keyStore; 431// keyStore = readKeyStore(keyStoreFile); 432 KeyManagerFactory keyFactory = 433 KeyManagerFactory.getInstance( 434 KeyManagerFactory.getDefaultAlgorithm()); 435 keyFactory.init(keyStore, "test12".toCharArray()); 436 keyManager = (X509ExtendedKeyManager)keyFactory.getKeyManagers()[0]; 437 438 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 439 tmf.init(keyStore); 440 trustManager = (X509TrustManager)tmf.getTrustManagers()[0]; 441 442// trustManager = new AlwaysTrustManager(); 443 SSLContext context = SSLContext.getInstance("TLS"); 444 context.init(new KeyManager[] {keyManager}, 445 new TrustManager[] {trustManager}, null); 446 SSLContext.setDefault(context); 447 448 CipherTest cipherTest = new CipherTest(peerFactory); 449 Thread serverThread = new Thread(peerFactory.newServer(cipherTest), 450 "Server"); 451 serverThread.setDaemon(true); 452 serverThread.start(); 453 System.out.println("Done"); 454 cipherTest.run(); 455 time = System.currentTimeMillis() - time; 456 System.out.println("Done. (" + time + " ms)"); 457 } 458 459 static abstract class PeerFactory { 460 461 abstract String getName(); 462 463 abstract Client newClient(CipherTest cipherTest) throws Exception; 464 465 abstract Server newServer(CipherTest cipherTest) throws Exception; 466 467 boolean isSupported(String cipherSuite, String protocol) { 468 // skip kerberos cipher suites 469 if (cipherSuite.startsWith("TLS_KRB5")) { 470 System.out.println("Skipping unsupported test for " + 471 cipherSuite + " of " + protocol); 472 return false; 473 } 474 475 // No ECDH-capable certificate in key store. May restructure 476 // this in the future. 477 if (cipherSuite.contains("ECDHE_ECDSA") || 478 cipherSuite.contains("ECDH_ECDSA") || 479 cipherSuite.contains("ECDH_RSA")) { 480 System.out.println("Skipping unsupported test for " + 481 cipherSuite + " of " + protocol); 482 return false; 483 } 484 485 // skip SSLv2Hello protocol 486 // 487 // skip TLSv1.2 protocol, we have not implement "SunTls12Prf" and 488 // SunTls12RsaPremasterSecret in SunPKCS11 provider 489 if (protocol.equals("SSLv2Hello") || protocol.equals("TLSv1.2")) { 490 System.out.println("Skipping unsupported test for " + 491 cipherSuite + " of " + protocol); 492 return false; 493 } 494 495 // ignore exportable cipher suite for TLSv1.1 496 if (protocol.equals("TLSv1.1")) { 497 if (cipherSuite.indexOf("_EXPORT_WITH") != -1) { 498 System.out.println("Skipping obsoleted test for " + 499 cipherSuite + " of " + protocol); 500 return false; 501 } 502 } 503 504 return true; 505 } 506 } 507 508} 509 510// we currently don't do any chain verification. we assume that works ok 511// and we can speed up the test. we could also just add a plain certificate 512// chain comparision with our trusted certificates. 513class AlwaysTrustManager implements X509TrustManager { 514 515 public AlwaysTrustManager() { 516 517 } 518 519 public void checkClientTrusted(X509Certificate[] chain, String authType) 520 throws CertificateException { 521 // empty 522 } 523 524 public void checkServerTrusted(X509Certificate[] chain, String authType) 525 throws CertificateException { 526 // empty 527 } 528 529 public X509Certificate[] getAcceptedIssuers() { 530 return new X509Certificate[0]; 531 } 532} 533 534class MyX509KeyManager extends X509ExtendedKeyManager { 535 536 private final X509ExtendedKeyManager keyManager; 537 private String authType; 538 539 MyX509KeyManager(X509ExtendedKeyManager keyManager) { 540 this.keyManager = keyManager; 541 } 542 543 void setAuthType(String authType) { 544 this.authType = authType; 545 } 546 547 public String[] getClientAliases(String keyType, Principal[] issuers) { 548 if (authType == null) { 549 return null; 550 } 551 return keyManager.getClientAliases(authType, issuers); 552 } 553 554 public String chooseClientAlias(String[] keyType, Principal[] issuers, 555 Socket socket) { 556 if (authType == null) { 557 return null; 558 } 559 return keyManager.chooseClientAlias(new String[] {authType}, 560 issuers, socket); 561 } 562 563 public String chooseEngineClientAlias(String[] keyType, 564 Principal[] issuers, SSLEngine engine) { 565 if (authType == null) { 566 return null; 567 } 568 return keyManager.chooseEngineClientAlias(new String[] {authType}, 569 issuers, engine); 570 } 571 572 public String[] getServerAliases(String keyType, Principal[] issuers) { 573 throw new UnsupportedOperationException("Servers not supported"); 574 } 575 576 public String chooseServerAlias(String keyType, Principal[] issuers, 577 Socket socket) { 578 throw new UnsupportedOperationException("Servers not supported"); 579 } 580 581 public String chooseEngineServerAlias(String keyType, Principal[] issuers, 582 SSLEngine engine) { 583 throw new UnsupportedOperationException("Servers not supported"); 584 } 585 586 public X509Certificate[] getCertificateChain(String alias) { 587 return keyManager.getCertificateChain(alias); 588 } 589 590 public PrivateKey getPrivateKey(String alias) { 591 return keyManager.getPrivateKey(alias); 592 } 593 594} 595 596class DaemonThreadFactory implements ThreadFactory { 597 598 final static ThreadFactory INSTANCE = new DaemonThreadFactory(); 599 600 private final static ThreadFactory DEFAULT = Executors.defaultThreadFactory(); 601 602 public Thread newThread(Runnable r) { 603 Thread t = DEFAULT.newThread(r); 604 t.setDaemon(true); 605 return t; 606 } 607 608} 609