1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package libcore.javax.net.ssl; 18 19import static java.nio.charset.StandardCharsets.UTF_8; 20 21import java.io.IOException; 22import java.nio.ByteBuffer; 23import java.util.Arrays; 24import java.util.concurrent.Callable; 25import java.util.concurrent.CountDownLatch; 26import java.util.concurrent.ExecutorService; 27import java.util.concurrent.Executors; 28import java.util.concurrent.Future; 29import javax.crypto.SecretKey; 30import javax.crypto.spec.SecretKeySpec; 31import javax.net.ssl.KeyManager; 32import javax.net.ssl.SSLContext; 33import javax.net.ssl.SSLEngine; 34import javax.net.ssl.SSLEngineResult; 35import javax.net.ssl.SSLEngineResult.HandshakeStatus; 36import javax.net.ssl.SSLException; 37import javax.net.ssl.SSLHandshakeException; 38import javax.net.ssl.SSLParameters; 39import javax.net.ssl.SSLSession; 40import javax.net.ssl.X509ExtendedKeyManager; 41import junit.framework.TestCase; 42import libcore.java.security.StandardNames; 43import libcore.java.security.TestKeyStore; 44 45public class SSLEngineTest extends TestCase { 46 47 public void assertConnected(TestSSLEnginePair e) { 48 assertConnected(e.client, e.server); 49 } 50 51 public void assertNotConnected(TestSSLEnginePair e) { 52 assertNotConnected(e.client, e.server); 53 } 54 55 public void assertConnected(SSLEngine a, SSLEngine b) { 56 assertTrue(connected(a, b)); 57 } 58 59 public void assertNotConnected(SSLEngine a, SSLEngine b) { 60 assertFalse(connected(a, b)); 61 } 62 63 public boolean connected(SSLEngine a, SSLEngine b) { 64 return (a.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 65 && b.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 66 && a.getSession() != null 67 && b.getSession() != null 68 && !a.isInboundDone() 69 && !b.isInboundDone() 70 && !a.isOutboundDone() 71 && !b.isOutboundDone()); 72 } 73 74 public void test_SSLEngine_defaultConfiguration() throws Exception { 75 SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration( 76 TestSSLContext.create().clientContext.createSSLEngine()); 77 } 78 79 public void test_SSLEngine_getSupportedCipherSuites_returnsCopies() throws Exception { 80 TestSSLContext c = TestSSLContext.create(); 81 SSLEngine e = c.clientContext.createSSLEngine(); 82 assertNotSame(e.getSupportedCipherSuites(), e.getSupportedCipherSuites()); 83 c.close(); 84 } 85 86 public void test_SSLEngine_getSupportedCipherSuites_connect() throws Exception { 87 // note the rare usage of non-RSA keys 88 TestKeyStore testKeyStore = new TestKeyStore.Builder() 89 .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA") 90 .aliasPrefix("rsa-dsa-ec") 91 .ca(true) 92 .build(); 93 test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, false); 94 test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true); 95 } 96 97 // http://b/18554122 98 public void test_SSLEngine_underflowsOnEmptyBuffersDuringHandshake() throws Exception { 99 final SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine(); 100 sslEngine.setUseClientMode(false); 101 ByteBuffer input = ByteBuffer.allocate(1024); 102 input.flip(); 103 ByteBuffer output = ByteBuffer.allocate(1024); 104 sslEngine.beginHandshake(); 105 assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, sslEngine.getHandshakeStatus()); 106 SSLEngineResult result = sslEngine.unwrap(input, output); 107 assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus()); 108 assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); 109 } 110 111 // http://b/18554122 112 public void test_SSLEngine_underflowsOnEmptyBuffersAfterHandshake() throws Exception { 113 // Note that create performs the handshake. 114 final TestSSLEnginePair engines = TestSSLEnginePair.create(null /* hooks */); 115 ByteBuffer input = ByteBuffer.allocate(1024); 116 input.flip(); 117 ByteBuffer output = ByteBuffer.allocate(1024); 118 assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, 119 engines.client.unwrap(input, output).getStatus()); 120 } 121 122 private void test_SSLEngine_getSupportedCipherSuites_connect(TestKeyStore testKeyStore, 123 boolean secureRenegotiation) 124 throws Exception { 125 KeyManager pskKeyManager = PSKKeyManagerProxy.getConscryptPSKKeyManager( 126 new PSKKeyManagerProxy() { 127 @Override 128 protected SecretKey getKey(String identityHint, String identity, SSLEngine engine) { 129 return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW"); 130 } 131 }); 132 TestSSLContext c = TestSSLContext.createWithAdditionalKeyManagers( 133 testKeyStore, testKeyStore, 134 new KeyManager[] {pskKeyManager}, new KeyManager[] {pskKeyManager}); 135 136 // Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private 137 // keys, matching the algorithm and parameters of the correct keys. 138 // I couldn't find a more elegant way to achieve this other than temporarily replacing the 139 // first X509ExtendedKeyManager element of TestKeyStore.keyManagers while invoking 140 // TestSSLContext.create. 141 TestSSLContext cWithWrongPrivateKeys; 142 { 143 // Create a RandomPrivateKeyX509ExtendedKeyManager based on the first 144 // X509ExtendedKeyManager in c.serverKeyManagers. 145 KeyManager randomPrivateKeyX509ExtendedKeyManager = null; 146 for (KeyManager keyManager : c.serverKeyManagers) { 147 if (keyManager instanceof X509ExtendedKeyManager) { 148 randomPrivateKeyX509ExtendedKeyManager = 149 new RandomPrivateKeyX509ExtendedKeyManager((X509ExtendedKeyManager) keyManager); 150 break; 151 } 152 } 153 if (randomPrivateKeyX509ExtendedKeyManager == null) { 154 fail("No X509ExtendedKeyManager in c.serverKeyManagers"); 155 } 156 157 // Find the first X509ExtendedKeyManager in testKeyStore.keyManagers 158 int replaceIndex = -1; 159 for (int i = 0; i < testKeyStore.keyManagers.length; i++) { 160 KeyManager keyManager = testKeyStore.keyManagers[i]; 161 if (keyManager instanceof X509ExtendedKeyManager) { 162 replaceIndex = i; 163 break; 164 } 165 } 166 if (replaceIndex == -1) { 167 fail("No X509ExtendedKeyManager in testKeyStore.keyManagers"); 168 } 169 170 // Temporarily substitute the RandomPrivateKeyX509ExtendedKeyManager in place of the 171 // original X509ExtendedKeyManager. 172 KeyManager originalKeyManager = testKeyStore.keyManagers[replaceIndex]; 173 testKeyStore.keyManagers[replaceIndex] = randomPrivateKeyX509ExtendedKeyManager; 174 cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore); 175 testKeyStore.keyManagers[replaceIndex] = originalKeyManager; 176 } 177 178 // To catch all the errors. 179 StringBuilder error = new StringBuilder(); 180 181 String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites(); 182 for (String cipherSuite : cipherSuites) { 183 try { 184 // Skip cipher suites that are obsoleted. 185 if (StandardNames.IS_RI && "TLSv1.2".equals(c.clientContext.getProtocol()) 186 && StandardNames.CIPHER_SUITES_OBSOLETE_TLS12.contains(cipherSuite)) { 187 continue; 188 } 189 /* 190 * Signaling Cipher Suite Values (SCSV) cannot be used on their own, but instead in 191 * conjunction with other cipher suites. 192 */ 193 if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION) 194 || cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)) { 195 continue; 196 } 197 /* 198 * Kerberos cipher suites require external setup. See "Kerberos Requirements" in 199 * https://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html 200 * #KRBRequire 201 */ 202 if (cipherSuite.startsWith("TLS_KRB5_")) { 203 continue; 204 } 205 206 final String[] cipherSuiteArray 207 = (secureRenegotiation 208 ? new String[] { cipherSuite, 209 StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION } 210 : new String[] { cipherSuite }); 211 212 // Check that handshake succeeds. 213 TestSSLEnginePair pair = null; 214 try { 215 pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 216 @Override 217 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 218 client.setEnabledCipherSuites(cipherSuiteArray); 219 server.setEnabledCipherSuites(cipherSuiteArray); 220 } 221 }); 222 assertConnected(pair); 223 224 boolean needsRecordSplit = 225 "TLS".equalsIgnoreCase(c.clientContext.getProtocol()) 226 && cipherSuite.contains("_CBC_"); 227 228 assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), 229 pair.client, pair.server, needsRecordSplit); 230 assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), 231 pair.server, pair.client, needsRecordSplit); 232 } finally { 233 if (pair != null) { 234 pair.close(); 235 } 236 } 237 238 // Check that handshake fails when the server does not possess the private key 239 // corresponding to the server's certificate. This is achieved by using SSLContext 240 // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match 241 // the algorithm (and parameters) of the correct keys. 242 boolean serverAuthenticatedUsingPublicKey = true; 243 if (cipherSuite.contains("_anon_")) { 244 serverAuthenticatedUsingPublicKey = false; 245 } else if ((cipherSuite.startsWith("TLS_PSK_")) 246 || (cipherSuite.startsWith("TLS_ECDHE_PSK_"))) { 247 serverAuthenticatedUsingPublicKey = false; 248 } 249 if (serverAuthenticatedUsingPublicKey) { 250 TestSSLEnginePair p = null; 251 try { 252 p = TestSSLEnginePair.create( 253 cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() { 254 @Override 255 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 256 client.setEnabledCipherSuites(cipherSuiteArray); 257 server.setEnabledCipherSuites(cipherSuiteArray); 258 } 259 }); 260 assertNotConnected(p); 261 } catch (IOException expected) { 262 } finally { 263 if (p != null) { 264 p.close(); 265 } 266 } 267 } 268 } catch (Exception e) { 269 String message = ("Problem trying to connect cipher suite " + cipherSuite); 270 System.out.println(message); 271 e.printStackTrace(); 272 error.append(message); 273 error.append('\n'); 274 } 275 } 276 c.close(); 277 278 if (error.length() > 0) { 279 throw new Exception("One or more problems in " 280 + "test_SSLEngine_getSupportedCipherSuites_connect:\n" + error); 281 } 282 } 283 284 private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source, 285 SSLEngine dest, boolean needsRecordSplit) throws SSLException { 286 ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes); 287 SSLSession sourceSession = source.getSession(); 288 ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize()); 289 SSLEngineResult sourceOutRes = source.wrap(sourceOut, sourceToDest); 290 sourceToDest.flip(); 291 292 String sourceCipherSuite = source.getSession().getCipherSuite(); 293 assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed()); 294 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 295 sourceOutRes.getHandshakeStatus()); 296 297 SSLSession destSession = dest.getSession(); 298 ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize()); 299 300 int numUnwrapCalls = 0; 301 while (destIn.position() != sourceOut.limit()) { 302 SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn); 303 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 304 destRes.getHandshakeStatus()); 305 if (needsRecordSplit && numUnwrapCalls == 0) { 306 assertEquals(sourceCipherSuite, 1, destRes.bytesProduced()); 307 } 308 numUnwrapCalls++; 309 } 310 311 destIn.flip(); 312 byte[] actual = new byte[destIn.remaining()]; 313 destIn.get(actual); 314 assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual)); 315 316 if (needsRecordSplit) { 317 assertEquals(sourceCipherSuite, 2, numUnwrapCalls); 318 } else { 319 assertEquals(sourceCipherSuite, 1, numUnwrapCalls); 320 } 321 } 322 323 public void test_SSLEngine_getEnabledCipherSuites_returnsCopies() throws Exception { 324 TestSSLContext c = TestSSLContext.create(); 325 SSLEngine e = c.clientContext.createSSLEngine(); 326 assertNotSame(e.getEnabledCipherSuites(), e.getEnabledCipherSuites()); 327 c.close(); 328 } 329 330 public void test_SSLEngine_setEnabledCipherSuites_storesCopy() throws Exception { 331 TestSSLContext c = TestSSLContext.create(); 332 SSLEngine e = c.clientContext.createSSLEngine(); 333 String[] array = new String[] {e.getEnabledCipherSuites()[0]}; 334 String originalFirstElement = array[0]; 335 e.setEnabledCipherSuites(array); 336 array[0] = "Modified after having been set"; 337 assertEquals(originalFirstElement, e.getEnabledCipherSuites()[0]); 338 } 339 340 public void test_SSLEngine_setEnabledCipherSuites() throws Exception { 341 TestSSLContext c = TestSSLContext.create(); 342 SSLEngine e = c.clientContext.createSSLEngine(); 343 344 try { 345 e.setEnabledCipherSuites(null); 346 fail(); 347 } catch (IllegalArgumentException expected) { 348 } 349 try { 350 e.setEnabledCipherSuites(new String[1]); 351 fail(); 352 } catch (IllegalArgumentException expected) { 353 } 354 try { 355 e.setEnabledCipherSuites(new String[] { "Bogus" } ); 356 fail(); 357 } catch (IllegalArgumentException expected) { 358 } 359 360 e.setEnabledCipherSuites(new String[0]); 361 e.setEnabledCipherSuites(e.getEnabledCipherSuites()); 362 e.setEnabledCipherSuites(e.getSupportedCipherSuites()); 363 364 // Check that setEnabledCipherSuites affects getEnabledCipherSuites 365 String[] cipherSuites = new String[] { e.getSupportedCipherSuites()[0] }; 366 e.setEnabledCipherSuites(cipherSuites); 367 assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites())); 368 369 c.close(); 370 } 371 372 public void test_SSLEngine_getSupportedProtocols_returnsCopies() throws Exception { 373 TestSSLContext c = TestSSLContext.create(); 374 SSLEngine e = c.clientContext.createSSLEngine(); 375 assertNotSame(e.getSupportedProtocols(), e.getSupportedProtocols()); 376 c.close(); 377 } 378 379 public void test_SSLEngine_getEnabledProtocols_returnsCopies() throws Exception { 380 TestSSLContext c = TestSSLContext.create(); 381 SSLEngine e = c.clientContext.createSSLEngine(); 382 assertNotSame(e.getEnabledProtocols(), e.getEnabledProtocols()); 383 c.close(); 384 } 385 386 public void test_SSLEngine_setEnabledProtocols_storesCopy() throws Exception { 387 TestSSLContext c = TestSSLContext.create(); 388 SSLEngine e = c.clientContext.createSSLEngine(); 389 String[] array = new String[] {e.getEnabledProtocols()[0]}; 390 String originalFirstElement = array[0]; 391 e.setEnabledProtocols(array); 392 array[0] = "Modified after having been set"; 393 assertEquals(originalFirstElement, e.getEnabledProtocols()[0]); 394 } 395 396 public void test_SSLEngine_setEnabledProtocols() throws Exception { 397 TestSSLContext c = TestSSLContext.create(); 398 SSLEngine e = c.clientContext.createSSLEngine(); 399 400 try { 401 e.setEnabledProtocols(null); 402 fail(); 403 } catch (IllegalArgumentException expected) { 404 } 405 try { 406 e.setEnabledProtocols(new String[1]); 407 fail(); 408 } catch (IllegalArgumentException expected) { 409 } 410 try { 411 e.setEnabledProtocols(new String[] { "Bogus" } ); 412 fail(); 413 } catch (IllegalArgumentException expected) { 414 } 415 e.setEnabledProtocols(new String[0]); 416 e.setEnabledProtocols(e.getEnabledProtocols()); 417 e.setEnabledProtocols(e.getSupportedProtocols()); 418 419 // Check that setEnabledProtocols affects getEnabledProtocols 420 for (String protocol : e.getSupportedProtocols()) { 421 if ("SSLv2Hello".equals(protocol)) { 422 try { 423 e.setEnabledProtocols(new String[] { protocol }); 424 fail("Should fail when SSLv2Hello is set by itself"); 425 } catch (IllegalArgumentException expected) {} 426 } else { 427 String[] protocols = new String[] { protocol }; 428 e.setEnabledProtocols(protocols); 429 assertEquals(Arrays.deepToString(protocols), 430 Arrays.deepToString(e.getEnabledProtocols())); 431 } 432 } 433 434 c.close(); 435 } 436 437 public void test_SSLEngine_getSession() throws Exception { 438 TestSSLContext c = TestSSLContext.create(); 439 SSLEngine e = c.clientContext.createSSLEngine(); 440 SSLSession session = e.getSession(); 441 assertNotNull(session); 442 assertFalse(session.isValid()); 443 c.close(); 444 } 445 446 public void test_SSLEngine_beginHandshake() throws Exception { 447 TestSSLContext c = TestSSLContext.create(); 448 449 try { 450 c.clientContext.createSSLEngine().beginHandshake(); 451 fail(); 452 } catch (IllegalStateException expected) { 453 } 454 c.close(); 455 456 TestSSLEnginePair p = TestSSLEnginePair.create(null); 457 assertConnected(p); 458 p.close(); 459 460 } 461 462 public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception { 463 TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null, 464 SSLContext.getDefault(), SSLContext.getDefault()); 465 SSLEngine[] p = null; 466 try { 467 // TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND" 468 // ServerHandshakeImpl.selectSuite should not select a suite without a required cert 469 p = TestSSLEnginePair.connect(c, null); 470 fail(); 471 } catch (SSLHandshakeException expected) { 472 } finally { 473 if (p != null) { 474 TestSSLEnginePair.close(p); 475 } 476 } 477 c.close(); 478 } 479 480 public void test_SSLEngine_beginHandshake_noClientCertificate() throws Exception { 481 TestSSLContext c = TestSSLContext.create(); 482 SSLEngine[] engines = TestSSLEnginePair.connect(c, null); 483 assertConnected(engines[0], engines[1]); 484 c.close(); 485 TestSSLEnginePair.close(engines); 486 } 487 488 public void test_SSLEngine_getUseClientMode() throws Exception { 489 TestSSLContext c = TestSSLContext.create(); 490 assertFalse(c.clientContext.createSSLEngine().getUseClientMode()); 491 assertFalse(c.clientContext.createSSLEngine(null, -1).getUseClientMode()); 492 c.close(); 493 } 494 495 public void test_SSLEngine_setUseClientMode() throws Exception { 496 boolean[] finished; 497 TestSSLEnginePair p = null; 498 499 // client is client, server is server 500 finished = new boolean[2]; 501 p = test_SSLEngine_setUseClientMode(true, false, finished); 502 assertConnected(p); 503 assertTrue(finished[0]); 504 assertTrue(finished[1]); 505 p.close(); 506 507 // client is server, server is client 508 finished = new boolean[2]; 509 p = test_SSLEngine_setUseClientMode(false, true, finished); 510 assertConnected(p); 511 assertTrue(finished[0]); 512 assertTrue(finished[1]); 513 p.close(); 514 515 // both are client 516 /* 517 * Our implementation throws an SSLHandshakeException, but RI just 518 * stalls forever 519 */ 520 p = null; 521 try { 522 p = test_SSLEngine_setUseClientMode(true, true, null); 523 assertNotConnected(p); 524 assertTrue(StandardNames.IS_RI); 525 } catch (SSLHandshakeException maybeExpected) { 526 assertFalse(StandardNames.IS_RI); 527 } finally { 528 if (p != null) { 529 p.close(); 530 } 531 532 } 533 534 p = test_SSLEngine_setUseClientMode(false, false, null); 535 // both are server 536 assertNotConnected(p); 537 p.close(); 538 } 539 540 public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception { 541 542 // can't set after handshake 543 TestSSLEnginePair pair = TestSSLEnginePair.create(null); 544 try { 545 pair.server.setUseClientMode(false); 546 fail(); 547 } catch (IllegalArgumentException expected) { 548 } 549 try { 550 pair.client.setUseClientMode(false); 551 fail(); 552 } catch (IllegalArgumentException expected) { 553 } 554 pair.close(); 555 } 556 557 private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode, 558 final boolean serverClientMode, 559 final boolean[] finished) 560 throws Exception { 561 TestSSLContext c; 562 if (!clientClientMode && serverClientMode) { 563 c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient()); 564 } else { 565 c = TestSSLContext.create(); 566 } 567 568 return TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 569 @Override 570 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 571 client.setUseClientMode(clientClientMode); 572 server.setUseClientMode(serverClientMode); 573 } 574 }, finished); 575 } 576 577 public void test_SSLEngine_clientAuth() throws Exception { 578 TestSSLContext c = TestSSLContext.create(); 579 SSLEngine e = c.clientContext.createSSLEngine(); 580 581 assertFalse(e.getWantClientAuth()); 582 assertFalse(e.getNeedClientAuth()); 583 584 // confirm turning one on by itself 585 e.setWantClientAuth(true); 586 assertTrue(e.getWantClientAuth()); 587 assertFalse(e.getNeedClientAuth()); 588 589 // confirm turning setting on toggles the other 590 e.setNeedClientAuth(true); 591 assertFalse(e.getWantClientAuth()); 592 assertTrue(e.getNeedClientAuth()); 593 594 // confirm toggling back 595 e.setWantClientAuth(true); 596 assertTrue(e.getWantClientAuth()); 597 assertFalse(e.getNeedClientAuth()); 598 599 // TODO Fix KnownFailure "init - invalid private key" 600 TestSSLContext clientAuthContext 601 = TestSSLContext.create(TestKeyStore.getClientCertificate(), 602 TestKeyStore.getServer()); 603 TestSSLEnginePair p = TestSSLEnginePair.create(clientAuthContext, 604 new TestSSLEnginePair.Hooks() { 605 @Override 606 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 607 server.setWantClientAuth(true); 608 } 609 }); 610 assertConnected(p); 611 assertNotNull(p.client.getSession().getLocalCertificates()); 612 TestKeyStore.assertChainLength(p.client.getSession().getLocalCertificates()); 613 TestSSLContext.assertClientCertificateChain(clientAuthContext.clientTrustManager, 614 p.client.getSession().getLocalCertificates()); 615 clientAuthContext.close(); 616 c.close(); 617 p.close(); 618 } 619 620 /** 621 * http://code.google.com/p/android/issues/detail?id=31903 622 * This test case directly tests the fix for the issue. 623 */ 624 public void test_SSLEngine_clientAuthWantedNoClientCert() throws Exception { 625 TestSSLContext clientAuthContext 626 = TestSSLContext.create(TestKeyStore.getClient(), 627 TestKeyStore.getServer()); 628 TestSSLEnginePair p = TestSSLEnginePair.create(clientAuthContext, 629 new TestSSLEnginePair.Hooks() { 630 @Override 631 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 632 server.setWantClientAuth(true); 633 } 634 }); 635 assertConnected(p); 636 clientAuthContext.close(); 637 p.close(); 638 } 639 640 /** 641 * http://code.google.com/p/android/issues/detail?id=31903 642 * This test case verifies that if the server requires a client cert 643 * (setNeedClientAuth) but the client does not provide one SSL connection 644 * establishment will fail 645 */ 646 public void test_SSLEngine_clientAuthNeededNoClientCert() throws Exception { 647 boolean handshakeExceptionCaught = false; 648 TestSSLContext clientAuthContext 649 = TestSSLContext.create(TestKeyStore.getClient(), 650 TestKeyStore.getServer()); 651 TestSSLEnginePair p = null; 652 try { 653 p = TestSSLEnginePair.create(clientAuthContext, 654 new TestSSLEnginePair.Hooks() { 655 @Override 656 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 657 server.setNeedClientAuth(true); 658 } 659 }); 660 fail(); 661 } catch (SSLHandshakeException expected) { 662 } finally { 663 clientAuthContext.close(); 664 if (p != null) { 665 p.close(); 666 } 667 } 668 } 669 670 public void test_SSLEngine_endpointVerification_Success() throws Exception { 671 TestSSLContext c = TestSSLContext.create(); 672 TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 673 @Override 674 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 675 SSLParameters p = client.getSSLParameters(); 676 p.setEndpointIdentificationAlgorithm("HTTPS"); 677 client.setSSLParameters(p); 678 } 679 }); 680 assertConnected(p); 681 c.close(); 682 } 683 684 public void test_SSLEngine_getEnableSessionCreation() throws Exception { 685 TestSSLContext c = TestSSLContext.create(); 686 SSLEngine e = c.clientContext.createSSLEngine(); 687 assertTrue(e.getEnableSessionCreation()); 688 c.close(); 689 TestSSLEnginePair.close(new SSLEngine[] { e }); 690 } 691 692 public void test_SSLEngine_setEnableSessionCreation_server() throws Exception { 693 TestSSLEnginePair p = null; 694 try { 695 p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() { 696 @Override 697 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 698 server.setEnableSessionCreation(false); 699 } 700 }); 701 // For some reason, the RI doesn't throw an SSLException. 702 assertTrue(StandardNames.IS_RI); 703 assertNotConnected(p); 704 } catch (SSLException maybeExpected) { 705 assertFalse(StandardNames.IS_RI); 706 } finally { 707 if (p != null) { 708 p.close(); 709 } 710 } 711 } 712 713 public void test_SSLEngine_setEnableSessionCreation_client() throws Exception { 714 TestSSLEnginePair p = null; 715 try { 716 p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() { 717 @Override 718 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 719 client.setEnableSessionCreation(false); 720 } 721 }); 722 fail(); 723 } catch (SSLException expected) { 724 } finally { 725 if (p != null) { 726 p.close(); 727 } 728 } 729 } 730 731 public void test_SSLEngine_getSSLParameters() throws Exception { 732 TestSSLContext c = TestSSLContext.create(); 733 SSLEngine e = c.clientContext.createSSLEngine(); 734 735 SSLParameters p = e.getSSLParameters(); 736 assertNotNull(p); 737 738 String[] cipherSuites = p.getCipherSuites(); 739 assertNotSame(cipherSuites, e.getEnabledCipherSuites()); 740 assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites())); 741 742 String[] protocols = p.getProtocols(); 743 assertNotSame(protocols, e.getEnabledProtocols()); 744 assertEquals(Arrays.asList(protocols), Arrays.asList(e.getEnabledProtocols())); 745 746 assertEquals(p.getWantClientAuth(), e.getWantClientAuth()); 747 assertEquals(p.getNeedClientAuth(), e.getNeedClientAuth()); 748 749 c.close(); 750 } 751 752 public void test_SSLEngine_setSSLParameters() throws Exception { 753 TestSSLContext c = TestSSLContext.create(); 754 SSLEngine e = c.clientContext.createSSLEngine(); 755 String[] defaultCipherSuites = e.getEnabledCipherSuites(); 756 String[] defaultProtocols = e.getEnabledProtocols(); 757 String[] supportedCipherSuites = e.getSupportedCipherSuites(); 758 String[] supportedProtocols = e.getSupportedProtocols(); 759 760 { 761 SSLParameters p = new SSLParameters(); 762 e.setSSLParameters(p); 763 assertEquals(Arrays.asList(defaultCipherSuites), 764 Arrays.asList(e.getEnabledCipherSuites())); 765 assertEquals(Arrays.asList(defaultProtocols), 766 Arrays.asList(e.getEnabledProtocols())); 767 } 768 769 { 770 SSLParameters p = new SSLParameters(supportedCipherSuites, 771 supportedProtocols); 772 e.setSSLParameters(p); 773 assertEquals(Arrays.asList(supportedCipherSuites), 774 Arrays.asList(e.getEnabledCipherSuites())); 775 assertEquals(Arrays.asList(supportedProtocols), 776 Arrays.asList(e.getEnabledProtocols())); 777 } 778 { 779 SSLParameters p = new SSLParameters(); 780 781 p.setNeedClientAuth(true); 782 assertFalse(e.getNeedClientAuth()); 783 assertFalse(e.getWantClientAuth()); 784 e.setSSLParameters(p); 785 assertTrue(e.getNeedClientAuth()); 786 assertFalse(e.getWantClientAuth()); 787 788 p.setWantClientAuth(true); 789 assertTrue(e.getNeedClientAuth()); 790 assertFalse(e.getWantClientAuth()); 791 e.setSSLParameters(p); 792 assertFalse(e.getNeedClientAuth()); 793 assertTrue(e.getWantClientAuth()); 794 795 p.setWantClientAuth(false); 796 assertFalse(e.getNeedClientAuth()); 797 assertTrue(e.getWantClientAuth()); 798 e.setSSLParameters(p); 799 assertFalse(e.getNeedClientAuth()); 800 assertFalse(e.getWantClientAuth()); 801 } 802 c.close(); 803 } 804 805 public void test_TestSSLEnginePair_create() throws Exception { 806 TestSSLEnginePair test = TestSSLEnginePair.create(null); 807 assertNotNull(test.c); 808 assertNotNull(test.server); 809 assertNotNull(test.client); 810 assertConnected(test); 811 test.close(); 812 } 813 814 private final int NUM_STRESS_ITERATIONS = 1000; 815 816 public void test_SSLEngine_Multiple_Thread_Success() throws Exception { 817 try (final TestSSLEnginePair pair = TestSSLEnginePair.create()) { 818 assertConnected(pair); 819 820 final CountDownLatch startUpSync = new CountDownLatch(2); 821 ExecutorService executor = Executors.newFixedThreadPool(2); 822 Future<Void> client = executor.submit(new Callable<Void>() { 823 @Override 824 public Void call() throws Exception { 825 startUpSync.countDown(); 826 827 for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { 828 assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), 829 pair.client, pair.server, false); 830 } 831 832 return null; 833 } 834 }); 835 Future<Void> server = executor.submit(new Callable<Void>() { 836 @Override 837 public Void call() throws Exception { 838 startUpSync.countDown(); 839 840 for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { 841 assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), 842 pair.server, pair.client, false); 843 } 844 845 return null; 846 } 847 }); 848 executor.shutdown(); 849 client.get(); 850 server.get(); 851 } 852 } 853} 854