HttpsURLConnectionTest.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package org.apache.harmony.luni.tests.internal.net.www.protocol.https; 19 20import java.io.File; 21import java.io.FileInputStream; 22import java.io.FileNotFoundException; 23import java.io.IOException; 24import java.io.InputStream; 25import java.io.OutputStream; 26import java.io.PrintStream; 27import java.net.Authenticator; 28import java.net.HttpURLConnection; 29import java.net.InetSocketAddress; 30import java.net.PasswordAuthentication; 31import java.net.Proxy; 32import java.net.ServerSocket; 33import java.net.Socket; 34import java.net.SocketTimeoutException; 35import java.net.URL; 36import java.security.KeyStore; 37import java.security.cert.Certificate; 38import java.util.Arrays; 39import javax.net.ssl.HostnameVerifier; 40import javax.net.ssl.HttpsURLConnection; 41import javax.net.ssl.KeyManagerFactory; 42import javax.net.ssl.SSLContext; 43import javax.net.ssl.SSLServerSocket; 44import javax.net.ssl.SSLSession; 45import javax.net.ssl.SSLSocket; 46import javax.net.ssl.SSLSocketFactory; 47import javax.net.ssl.TrustManagerFactory; 48 49import junit.framework.TestCase; 50import junit.framework.TestSuite; 51 52/** 53 * Implementation independent test for HttpsURLConnection. 54 * The test needs certstore file placed in system classpath 55 * and named as "key_store." + the type of the 56 * default KeyStore installed in the system in lower case. 57 * <br> 58 * For example: if default KeyStore type in the system is BKS 59 * (i.e. java.security file sets up the property keystore.type=BKS), 60 * thus classpath should point to the directory with "key_store.bks" 61 * file. 62 * <br> 63 * This certstore file should contain self-signed certificate 64 * generated by keytool utility in a usual way. 65 * <br> 66 * The password to the certstore should be "password" (without quotes). 67 */ 68public class HttpsURLConnectionTest extends TestCase { 69 70 // the password to the store 71 private static final String KS_PASSWORD = "password"; 72 73 // turn on/off logging 74 private static final boolean DO_LOG = false; 75 76 // read/connection timeout value 77 private static final int TIMEOUT = 5000; 78 79 // OK response code 80 private static final int OK_CODE = 200; 81 82 // Not Found response code 83 private static final int NOT_FOUND_CODE = 404; 84 85 // Proxy authentication required response code 86 private static final int AUTHENTICATION_REQUIRED_CODE = 407; 87 88 // fields keeping the system values of corresponding properties 89 private static String systemKeyStoreType; 90 91 private static String systemKeyStore; 92 93 private static String systemKeyStorePassword; 94 95 private static String systemTrustStoreType; 96 97 private static String systemTrustStore; 98 99 private static String systemTrustStorePassword; 100 101 /** 102 * Checks that HttpsURLConnection's default SSLSocketFactory is operable. 103 */ 104 public void testGetDefaultSSLSocketFactory() throws Exception { 105 // set up the properties defining the default values needed by SSL stuff 106 setUpStoreProperties(); 107 108 try { 109 SSLSocketFactory defaultSSLSF = HttpsURLConnection 110 .getDefaultSSLSocketFactory(); 111 ServerSocket ss = new ServerSocket(0); 112 Socket s = defaultSSLSF 113 .createSocket("localhost", ss.getLocalPort()); 114 ss.accept(); 115 s.close(); 116 ss.close(); 117 } finally { 118 // roll the properties back to system values 119 tearDownStoreProperties(); 120 } 121 } 122 123 /** 124 * Checks if HTTPS connection performs initial SSL handshake with the 125 * server working over SSL, sends encrypted HTTP request, 126 * and receives expected HTTP response. After HTTPS session if finished 127 * test checks connection state parameters established by 128 * HttpsURLConnection. 129 */ 130 public void testHttpsConnection() throws Throwable { 131 // set up the properties defining the default values needed by SSL stuff 132 setUpStoreProperties(); 133 134 try { 135 // create the SSL server socket acting as a server 136 SSLContext ctx = getContext(); 137 ServerSocket ss = ctx.getServerSocketFactory() 138 .createServerSocket(0); 139 140 // create the HostnameVerifier to check hostname verification 141 TestHostnameVerifier hnv = new TestHostnameVerifier(); 142 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 143 144 // create url connection to be tested 145 URL url = new URL("https://localhost:" + ss.getLocalPort()); 146 HttpsURLConnection connection = (HttpsURLConnection) url 147 .openConnection(); 148 149 // perform the interaction between the peers 150 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 151 152 // check the connection state 153 checkConnectionStateParameters(connection, peerSocket); 154 155 // should silently exit 156 connection.connect(); 157 } finally { 158 // roll the properties back to system values 159 tearDownStoreProperties(); 160 } 161 } 162 163 /** 164 * Tests the behaviour of HTTPS connection in case of unavailability 165 * of requested resource. 166 */ 167 public void testHttpsConnection_Not_Found_Response() throws Throwable { 168 // set up the properties defining the default values needed by SSL stuff 169 setUpStoreProperties(); 170 171 try { 172 // create the SSL server socket acting as a server 173 SSLContext ctx = getContext(); 174 ServerSocket ss = ctx.getServerSocketFactory() 175 .createServerSocket(0); 176 177 // create the HostnameVerifier to check hostname verification 178 TestHostnameVerifier hnv = new TestHostnameVerifier(); 179 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 180 181 // create url connection to be tested 182 URL url = new URL("https://localhost:" + ss.getLocalPort()); 183 HttpsURLConnection connection = (HttpsURLConnection) url 184 .openConnection(); 185 186 try { 187 doInteraction(connection, ss, NOT_FOUND_CODE); 188 fail("Expected exception was not thrown."); 189 } catch (FileNotFoundException e) { 190 if (DO_LOG) { 191 System.out.println("Expected exception was thrown: " 192 + e.getMessage()); 193 } 194 } 195 196 // should silently exit 197 connection.connect(); 198 } finally { 199 // roll the properties back to system values 200 tearDownStoreProperties(); 201 } 202 } 203 204 /** 205 * Tests possibility to set up the default SSLSocketFactory 206 * to be used by HttpsURLConnection. 207 */ 208 public void testSetDefaultSSLSocketFactory() throws Throwable { 209 // create the SSLServerSocket which will be used by server side 210 SSLContext ctx = getContext(); 211 SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() 212 .createServerSocket(0); 213 214 SSLSocketFactory socketFactory = (SSLSocketFactory) ctx 215 .getSocketFactory(); 216 // set up the factory as default 217 HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); 218 // check the result 219 assertSame("Default SSLSocketFactory differs from expected", 220 socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory()); 221 222 // create the HostnameVerifier to check hostname verification 223 TestHostnameVerifier hnv = new TestHostnameVerifier(); 224 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 225 226 // create HttpsURLConnection to be tested 227 URL url = new URL("https://localhost:" + ss.getLocalPort()); 228 HttpsURLConnection connection = (HttpsURLConnection) url 229 .openConnection(); 230 231 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 232 // late initialization: should not be used for created connection 233 HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); 234 235 // perform the interaction between the peers 236 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 237 // check the connection state 238 checkConnectionStateParameters(connection, peerSocket); 239 // check the verification process 240 assertTrue("Hostname verification was not done", hnv.verified); 241 assertFalse( 242 "Hostname verification should not be done by this verifier", 243 hnv_late.verified); 244 // check the used SSLSocketFactory 245 assertSame("Default SSLSocketFactory should be used", 246 HttpsURLConnection.getDefaultSSLSocketFactory(), connection 247 .getSSLSocketFactory()); 248 249 // should silently exit 250 connection.connect(); 251 } 252 253 /** 254 * Tests possibility to set up the SSLSocketFactory 255 * to be used by HttpsURLConnection. 256 */ 257 public void testSetSSLSocketFactory() throws Throwable { 258 // create the SSLServerSocket which will be used by server side 259 SSLContext ctx = getContext(); 260 SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() 261 .createServerSocket(0); 262 263 // create the HostnameVerifier to check hostname verification 264 TestHostnameVerifier hnv = new TestHostnameVerifier(); 265 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 266 267 // create HttpsURLConnection to be tested 268 URL url = new URL("https://localhost:" + ss.getLocalPort()); 269 HttpsURLConnection connection = (HttpsURLConnection) url 270 .openConnection(); 271 272 SSLSocketFactory socketFactory = (SSLSocketFactory) ctx 273 .getSocketFactory(); 274 connection.setSSLSocketFactory(socketFactory); 275 276 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 277 // late initialization: should not be used for created connection 278 HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); 279 280 // perform the interaction between the peers 281 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 282 // check the connection state 283 checkConnectionStateParameters(connection, peerSocket); 284 // check the verification process 285 assertTrue("Hostname verification was not done", hnv.verified); 286 assertFalse( 287 "Hostname verification should not be done by this verifier", 288 hnv_late.verified); 289 // check the used SSLSocketFactory 290 assertNotSame("Default SSLSocketFactory should not be used", 291 HttpsURLConnection.getDefaultSSLSocketFactory(), connection 292 .getSSLSocketFactory()); 293 assertSame("Result differs from expected", socketFactory, connection 294 .getSSLSocketFactory()); 295 296 // should silently exit 297 connection.connect(); 298 } 299 300 /** 301 * Tests the behaviour of HttpsURLConnection in case of retrieving 302 * of the connection state parameters before connection has been made. 303 */ 304 public void testUnconnectedStateParameters() throws Throwable { 305 // create HttpsURLConnection to be tested 306 URL url = new URL("https://localhost:55555"); 307 HttpsURLConnection connection = (HttpsURLConnection) url 308 .openConnection(); 309 310 try { 311 connection.getCipherSuite(); 312 fail("Expected IllegalStateException was not thrown"); 313 } catch (IllegalStateException e) {} 314 try { 315 connection.getPeerPrincipal(); 316 fail("Expected IllegalStateException was not thrown"); 317 } catch (IllegalStateException e) {} 318 try { 319 connection.getLocalPrincipal(); 320 fail("Expected IllegalStateException was not thrown"); 321 } catch (IllegalStateException e) {} 322 323 try { 324 connection.getServerCertificates(); 325 fail("Expected IllegalStateException was not thrown"); 326 } catch (IllegalStateException e) {} 327 try { 328 connection.getLocalCertificates(); 329 fail("Expected IllegalStateException was not thrown"); 330 } catch (IllegalStateException e) {} 331 } 332 333 /** 334 * Tests if setHostnameVerifier() method replaces default verifier. 335 */ 336 public void testSetHostnameVerifier() throws Throwable { 337 // setting up the properties pointing to the key/trust stores 338 setUpStoreProperties(); 339 340 try { 341 // create the SSLServerSocket which will be used by server side 342 SSLServerSocket ss = (SSLServerSocket) getContext() 343 .getServerSocketFactory().createServerSocket(0); 344 345 // create the HostnameVerifier to check that Hostname verification 346 // is done 347 TestHostnameVerifier hnv = new TestHostnameVerifier(); 348 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 349 350 // create HttpsURLConnection to be tested 351 URL url = new URL("https://localhost:" + ss.getLocalPort()); 352 HttpsURLConnection connection = (HttpsURLConnection) url 353 .openConnection(); 354 355 TestHostnameVerifier hnv_late = new TestHostnameVerifier(); 356 // replace default verifier 357 connection.setHostnameVerifier(hnv_late); 358 359 // perform the interaction between the peers and check the results 360 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 361 assertTrue("Hostname verification was not done", hnv_late.verified); 362 assertFalse( 363 "Hostname verification should not be done by this verifier", 364 hnv.verified); 365 checkConnectionStateParameters(connection, peerSocket); 366 367 // should silently exit 368 connection.connect(); 369 } finally { 370 // roll the properties back to system values 371 tearDownStoreProperties(); 372 } 373 } 374 375 /** 376 * Tests the behaviour in case of sending the data to the server. 377 */ 378 public void test_doOutput() throws Throwable { 379 // setting up the properties pointing to the key/trust stores 380 setUpStoreProperties(); 381 382 try { 383 // create the SSLServerSocket which will be used by server side 384 SSLServerSocket ss = (SSLServerSocket) getContext() 385 .getServerSocketFactory().createServerSocket(0); 386 387 // create the HostnameVerifier to check that Hostname verification 388 // is done 389 TestHostnameVerifier hnv = new TestHostnameVerifier(); 390 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 391 392 // create HttpsURLConnection to be tested 393 URL url = new URL("https://localhost:" + ss.getLocalPort()); 394 HttpsURLConnection connection = (HttpsURLConnection) url 395 .openConnection(); 396 connection.setDoOutput(true); 397 398 // perform the interaction between the peers and check the results 399 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 400 checkConnectionStateParameters(connection, peerSocket); 401 402 // should silently exit 403 connection.connect(); 404 } finally { 405 // roll the properties back to system values 406 tearDownStoreProperties(); 407 } 408 } 409 410 /** 411 * Tests HTTPS connection process made through the proxy server. 412 */ 413 public void testProxyConnection() throws Throwable { 414 // setting up the properties pointing to the key/trust stores 415 setUpStoreProperties(); 416 417 try { 418 // create the SSLServerSocket which will be used by server side 419 ServerSocket ss = new ServerSocket(0); 420 421 // create the HostnameVerifier to check that Hostname verification 422 // is done 423 TestHostnameVerifier hnv = new TestHostnameVerifier(); 424 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 425 426 // create HttpsURLConnection to be tested 427 URL url = new URL("https://requested.host:55556/requested.data"); 428 HttpsURLConnection connection = (HttpsURLConnection) url 429 .openConnection(new Proxy(Proxy.Type.HTTP, 430 new InetSocketAddress("localhost", ss 431 .getLocalPort()))); 432 433 // perform the interaction between the peers and check the results 434 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 435 checkConnectionStateParameters(connection, peerSocket); 436 437 // should silently exit 438 connection.connect(); 439 } finally { 440 // roll the properties back to system values 441 tearDownStoreProperties(); 442 } 443 } 444 445 /** 446 * Tests HTTPS connection process made through the proxy server. 447 * Proxy server needs authentication. 448 */ 449 public void testProxyAuthConnection() throws Throwable { 450 // setting up the properties pointing to the key/trust stores 451 setUpStoreProperties(); 452 453 try { 454 // create the SSLServerSocket which will be used by server side 455 ServerSocket ss = new ServerSocket(0); 456 457 // create the HostnameVerifier to check that Hostname verification 458 // is done 459 TestHostnameVerifier hnv = new TestHostnameVerifier(); 460 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 461 462 Authenticator.setDefault(new Authenticator() { 463 464 protected PasswordAuthentication getPasswordAuthentication() { 465 return new PasswordAuthentication("user", "password" 466 .toCharArray()); 467 } 468 }); 469 470 // create HttpsURLConnection to be tested 471 URL url = new URL("https://requested.host:55555/requested.data"); 472 HttpsURLConnection connection = (HttpsURLConnection) url 473 .openConnection(new Proxy(Proxy.Type.HTTP, 474 new InetSocketAddress("localhost", ss 475 .getLocalPort()))); 476 477 // perform the interaction between the peers and check the results 478 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 479 checkConnectionStateParameters(connection, peerSocket); 480 481 // should silently exit 482 connection.connect(); 483 } finally { 484 // roll the properties back to system values 485 tearDownStoreProperties(); 486 } 487 } 488 489 /** 490 * Tests HTTPS connection process made through the proxy server. 491 * 2 HTTPS connections are opened for one URL. For the first time 492 * the connection is opened through one proxy, 493 * for the second time through another. 494 */ 495 public void testConsequentProxyConnection() throws Throwable { 496 // setting up the properties pointing to the key/trust stores 497 setUpStoreProperties(); 498 499 try { 500 // create the SSLServerSocket which will be used by server side 501 ServerSocket ss = new ServerSocket(0); 502 503 // create the HostnameVerifier to check that Hostname verification 504 // is done 505 TestHostnameVerifier hnv = new TestHostnameVerifier(); 506 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 507 508 // create HttpsURLConnection to be tested 509 URL url = new URL("https://requested.host:55555/requested.data"); 510 HttpsURLConnection connection = (HttpsURLConnection) url 511 .openConnection(new Proxy(Proxy.Type.HTTP, 512 new InetSocketAddress("localhost", ss 513 .getLocalPort()))); 514 515 // perform the interaction between the peers and check the results 516 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); 517 checkConnectionStateParameters(connection, peerSocket); 518 519 // create another SSLServerSocket which will be used by server side 520 ss = new ServerSocket(0); 521 522 connection = (HttpsURLConnection) url.openConnection(new Proxy( 523 Proxy.Type.HTTP, new InetSocketAddress("localhost", ss 524 .getLocalPort()))); 525 526 // perform the interaction between the peers and check the results 527 peerSocket = (SSLSocket) doInteraction(connection, ss); 528 checkConnectionStateParameters(connection, peerSocket); 529 } finally { 530 // roll the properties back to system values 531 tearDownStoreProperties(); 532 } 533 } 534 535 /** 536 * Tests HTTPS connection process made through the proxy server. 537 * Proxy server needs authentication. 538 * Client sends data to the server. 539 */ 540 public void testProxyAuthConnection_doOutput() throws Throwable { 541 // setting up the properties pointing to the key/trust stores 542 setUpStoreProperties(); 543 544 try { 545 // create the SSLServerSocket which will be used by server side 546 ServerSocket ss = new ServerSocket(0); 547 548 // create the HostnameVerifier to check that Hostname verification 549 // is done 550 TestHostnameVerifier hnv = new TestHostnameVerifier(); 551 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 552 553 Authenticator.setDefault(new Authenticator() { 554 555 protected PasswordAuthentication getPasswordAuthentication() { 556 return new PasswordAuthentication("user", "password" 557 .toCharArray()); 558 } 559 }); 560 561 // create HttpsURLConnection to be tested 562 URL url = new URL("https://requested.host:55554/requested.data"); 563 HttpsURLConnection connection = (HttpsURLConnection) url 564 .openConnection(new Proxy(Proxy.Type.HTTP, 565 new InetSocketAddress("localhost", ss 566 .getLocalPort()))); 567 connection.setDoOutput(true); 568 569 // perform the interaction between the peers and check the results 570 SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss, 571 OK_CODE, true); 572 checkConnectionStateParameters(connection, peerSocket); 573 } finally { 574 // roll the properties back to system values 575 tearDownStoreProperties(); 576 } 577 } 578 579 /** 580 * Tests HTTPS connection process made through the proxy server. 581 * Proxy server needs authentication but client fails to authenticate 582 * (Authenticator was not set up in the system). 583 */ 584 public void testProxyAuthConnectionFailed() throws Throwable { 585 // setting up the properties pointing to the key/trust stores 586 setUpStoreProperties(); 587 588 try { 589 // create the SSLServerSocket which will be used by server side 590 ServerSocket ss = new ServerSocket(0); 591 592 // create the HostnameVerifier to check that Hostname verification 593 // is done 594 TestHostnameVerifier hnv = new TestHostnameVerifier(); 595 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 596 597 // create HttpsURLConnection to be tested 598 URL url = new URL("https://requested.host:55555/requested.data"); 599 HttpURLConnection connection = (HttpURLConnection) url 600 .openConnection(new Proxy(Proxy.Type.HTTP, 601 new InetSocketAddress("localhost", ss 602 .getLocalPort()))); 603 604 // perform the interaction between the peers and check the results 605 try { 606 doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE, 607 true); 608 } catch (IOException e) { 609 // SSL Tunnelling failed 610 if (DO_LOG) { 611 System.out.println("Got expected IOException: " 612 + e.getMessage()); 613 } 614 } 615 } finally { 616 // roll the properties back to system values 617 tearDownStoreProperties(); 618 } 619 } 620 621 /** 622 * Tests the behaviour of HTTPS connection in case of unavailability 623 * of requested resource. 624 */ 625 public void testProxyConnection_Not_Found_Response() throws Throwable { 626 // setting up the properties pointing to the key/trust stores 627 setUpStoreProperties(); 628 629 try { 630 // create the SSLServerSocket which will be used by server side 631 ServerSocket ss = new ServerSocket(0); 632 633 // create the HostnameVerifier to check that Hostname verification 634 // is done 635 TestHostnameVerifier hnv = new TestHostnameVerifier(); 636 HttpsURLConnection.setDefaultHostnameVerifier(hnv); 637 638 // create HttpsURLConnection to be tested 639 URL url = new URL("https://localhost:" + ss.getLocalPort()); 640 HttpURLConnection connection = (HttpURLConnection) url 641 .openConnection(new Proxy(Proxy.Type.HTTP, 642 new InetSocketAddress("localhost", ss 643 .getLocalPort()))); 644 645 try { 646 doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND 647 fail("Expected exception was not thrown."); 648 } catch (FileNotFoundException e) { 649 if (DO_LOG) { 650 System.out.println("Expected exception was thrown: " 651 + e.getMessage()); 652 } 653 } 654 } finally { 655 // roll the properties back to system values 656 tearDownStoreProperties(); 657 } 658 } 659 660 // --------------------------------------------------------------------- 661 // ------------------------ Staff Methods ------------------------------ 662 // --------------------------------------------------------------------- 663 664 /** 665 * Log the name of the test case to be executed. 666 */ 667 public void setUp() throws Exception { 668 if (DO_LOG) { 669 System.out.println(); 670 System.out.println("------------------------"); 671 System.out.println("------ " + getName()); 672 System.out.println("------------------------"); 673 } 674 } 675 676 /** 677 * Checks the HttpsURLConnection getter's values and compares 678 * them with actual corresponding values of remote peer. 679 */ 680 public static void checkConnectionStateParameters( 681 HttpsURLConnection clientConnection, SSLSocket serverPeer) 682 throws Exception { 683 SSLSession session = serverPeer.getSession(); 684 685 assertEquals(session.getCipherSuite(), clientConnection 686 .getCipherSuite()); 687 688 assertEquals(session.getLocalPrincipal(), clientConnection 689 .getPeerPrincipal()); 690 691 assertEquals(session.getPeerPrincipal(), clientConnection 692 .getLocalPrincipal()); 693 694 Certificate[] serverCertificates = clientConnection 695 .getServerCertificates(); 696 Certificate[] localCertificates = session.getLocalCertificates(); 697 assertTrue("Server certificates differ from expected", Arrays.equals( 698 serverCertificates, localCertificates)); 699 700 localCertificates = clientConnection.getLocalCertificates(); 701 serverCertificates = session.getPeerCertificates(); 702 assertTrue("Local certificates differ from expected", Arrays.equals( 703 serverCertificates, localCertificates)); 704 } 705 706 /** 707 * Returns the file name of the key/trust store. The key store file 708 * (named as "key_store." + extension equals to the default KeyStore 709 * type installed in the system in lower case) is searched in classpath. 710 * @throws AssertionFailedError if property was not set 711 * or file does not exist. 712 */ 713 private static String getKeyStoreFileName() throws Exception { 714 String ksFileName = "org/apache/harmony/luni/tests/key_store." 715 + KeyStore.getDefaultType().toLowerCase(); 716 URL url = ClassLoader.getSystemClassLoader().getResource(ksFileName); 717 assertNotNull("Expected KeyStore file: '" + ksFileName 718 + "' for default KeyStore of type '" 719 + KeyStore.getDefaultType() + "' does not exist.", url); 720 return new File(url.toURI()).getAbsolutePath(); 721 } 722 723 /** 724 * Builds and returns the context used for secure socket creation. 725 */ 726 private static SSLContext getContext() throws Exception { 727 String type = KeyStore.getDefaultType(); 728 SSLContext ctx; 729 730 String keyStore = getKeyStoreFileName(); 731 File keyStoreFile = new File(keyStore); 732 733 FileInputStream fis = new FileInputStream(keyStoreFile); 734 735 KeyStore ks = KeyStore.getInstance(type); 736 ks.load(fis, KS_PASSWORD.toCharArray()); 737 738 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory 739 .getDefaultAlgorithm()); 740 kmf.init(ks, KS_PASSWORD.toCharArray()); 741 742 TrustManagerFactory tmf = TrustManagerFactory 743 .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 744 tmf.init(ks); 745 746 ctx = SSLContext.getInstance("TLSv1"); 747 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 748 749 return ctx; 750 } 751 752 /** 753 * Sets up the properties pointing to the key store and trust store 754 * and used as default values by JSSE staff. This is needed to test 755 * HTTPS behaviour in the case of default SSL Socket Factories. 756 */ 757 private static void setUpStoreProperties() throws Exception { 758 String type = KeyStore.getDefaultType(); 759 760 systemKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType"); 761 systemKeyStore = System.getProperty("javax.net.ssl.keyStore"); 762 systemKeyStorePassword = System 763 .getProperty("javax.net.ssl.keyStorePassword"); 764 765 systemTrustStoreType = System 766 .getProperty("javax.net.ssl.trustStoreType"); 767 systemTrustStore = System.getProperty("javax.net.ssl.trustStore"); 768 systemTrustStorePassword = System 769 .getProperty("javax.net.ssl.trustStorePassword"); 770 771 System.setProperty("javax.net.ssl.keyStoreType", type); 772 System.setProperty("javax.net.ssl.keyStore", getKeyStoreFileName()); 773 System.setProperty("javax.net.ssl.keyStorePassword", KS_PASSWORD); 774 775 System.setProperty("javax.net.ssl.trustStoreType", type); 776 System.setProperty("javax.net.ssl.trustStore", getKeyStoreFileName()); 777 System.setProperty("javax.net.ssl.trustStorePassword", KS_PASSWORD); 778 } 779 780 /** 781 * Rolls back the values of system properties. 782 */ 783 private static void tearDownStoreProperties() { 784 if (systemKeyStoreType == null) { 785 System.clearProperty("javax.net.ssl.keyStoreType"); 786 } else { 787 System 788 .setProperty("javax.net.ssl.keyStoreType", 789 systemKeyStoreType); 790 } 791 if (systemKeyStore == null) { 792 System.clearProperty("javax.net.ssl.keyStore"); 793 } else { 794 System.setProperty("javax.net.ssl.keyStore", systemKeyStore); 795 } 796 if (systemKeyStorePassword == null) { 797 System.clearProperty("javax.net.ssl.keyStorePassword"); 798 } else { 799 System.setProperty("javax.net.ssl.keyStorePassword", 800 systemKeyStorePassword); 801 } 802 803 if (systemTrustStoreType == null) { 804 System.clearProperty("javax.net.ssl.trustStoreType"); 805 } else { 806 System.setProperty("javax.net.ssl.trustStoreType", 807 systemTrustStoreType); 808 } 809 if (systemTrustStore == null) { 810 System.clearProperty("javax.net.ssl.trustStore"); 811 } else { 812 System.setProperty("javax.net.ssl.trustStore", systemTrustStore); 813 } 814 if (systemTrustStorePassword == null) { 815 System.clearProperty("javax.net.ssl.trustStorePassword"); 816 } else { 817 System.setProperty("javax.net.ssl.trustStorePassword", 818 systemTrustStorePassword); 819 } 820 } 821 822 /** 823 * Performs interaction between client's HttpURLConnection and 824 * servers side (ServerSocket). 825 */ 826 public static Socket doInteraction( 827 final HttpURLConnection clientConnection, 828 final ServerSocket serverSocket) throws Throwable { 829 return doInteraction(clientConnection, serverSocket, OK_CODE, false); 830 } 831 832 /** 833 * Performs interaction between client's HttpURLConnection and 834 * servers side (ServerSocket). Server will response with specified 835 * response code. 836 */ 837 public static Socket doInteraction( 838 final HttpURLConnection clientConnection, 839 final ServerSocket serverSocket, final int responseCode) 840 throws Throwable { 841 return doInteraction(clientConnection, serverSocket, responseCode, 842 false); 843 } 844 845 /** 846 * Performs interaction between client's HttpURLConnection and 847 * servers side (ServerSocket). Server will response with specified 848 * response code. 849 * @param doAuthentication specifies 850 * if the server needs client authentication. 851 */ 852 public static Socket doInteraction( 853 final HttpURLConnection clientConnection, 854 final ServerSocket serverSocket, final int responseCode, 855 final boolean doAuthentication) throws Throwable { 856 857 // set up the connection 858 clientConnection.setDoInput(true); 859 clientConnection.setConnectTimeout(TIMEOUT); 860 clientConnection.setReadTimeout(TIMEOUT); 861 862 ServerWork server = new ServerWork(serverSocket, responseCode, 863 doAuthentication); 864 865 ClientConnectionWork client = new ClientConnectionWork(clientConnection); 866 867 server.start(); 868 client.start(); 869 870 client.join(); 871 server.join(); 872 873 if (client.thrown != null) { 874 if (responseCode != OK_CODE) { // not OK response expected 875 // it is probably expected exception, keep it as is 876 throw client.thrown; 877 } 878 if ((client.thrown instanceof SocketTimeoutException) 879 && (server.thrown != null)) { 880 // server's exception is more informative in this case 881 throw new Exception(server.thrown); 882 } else { 883 throw new Exception(client.thrown); 884 } 885 } 886 if (server.thrown != null) { 887 throw server.thrown; 888 } 889 return server.peerSocket; 890 } 891 892 /** 893 * The host name verifier used in test. 894 */ 895 static class TestHostnameVerifier implements HostnameVerifier { 896 897 boolean verified = false; 898 899 public boolean verify(String hostname, SSLSession session) { 900 if (DO_LOG) { 901 System.out.println("***> verification " + hostname + " " 902 + session.getPeerHost()); 903 } 904 verified = true; 905 return true; 906 } 907 } 908 909 /** 910 * The base class for mock Client and Server. 911 */ 912 static class Work extends Thread { 913 914 /** 915 * The header of OK HTTP response. 916 */ 917 static String responseHead = "HTTP/1.1 200 OK\n"; 918 919 /** 920 * The content of the response. 921 */ 922 static String plainResponseContent = "<HTML>\n" 923 + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n" 924 + "</HTML>"; 925 926 /** 927 * The tail of the response. 928 */ 929 static String plainResponseTail = "Content-type: text/html\n" 930 + "Content-length: " + plainResponseContent.length() + "\n\n" 931 + plainResponseContent; 932 933 /** 934 * The response message to be sent in plain (HTTP) format. 935 */ 936 static String plainResponse = responseHead + plainResponseTail; 937 938 /** 939 * The content of the response to be sent during HTTPS session. 940 */ 941 static String httpsResponseContent = "<HTML>\n" 942 + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n" 943 + "</HTML>"; 944 945 /** 946 * The tail of the response to be sent during HTTPS session. 947 */ 948 static String httpsResponseTail = "Content-type: text/html\n" 949 + "Content-length: " + httpsResponseContent.length() + "\n\n" 950 + httpsResponseContent; 951 952 /** 953 * The response requiring client's proxy authentication. 954 */ 955 static String respAuthenticationRequired = "HTTP/1.0 407 Proxy authentication required\n" 956 + "Proxy-authenticate: Basic realm=\"localhost\"\n\n"; 957 958 /** 959 * The data to be posted by client to the server. 960 */ 961 static String clientsData = "_.-^ Client's Data ^-._"; 962 963 /** 964 * The exception thrown during peers interaction. 965 */ 966 protected Throwable thrown; 967 968 /** 969 * The print stream used for debug log. 970 * If it is null debug info will not be printed. 971 */ 972 private PrintStream out = new PrintStream(System.out); 973 974 /** 975 * Prints log message. 976 */ 977 public synchronized void log(String message) { 978 if (DO_LOG && (out != null)) { 979 System.out.println("[" + getName() + "]: " + message); 980 } 981 } 982 } 983 984 /** 985 * The class used for server side works. 986 */ 987 static class ServerWork extends Work { 988 989 // the server socket used for connection 990 private ServerSocket serverSocket; 991 992 // the socket connected with client peer 993 private Socket peerSocket; 994 995 // indicates if the server acts as proxy server 996 private boolean actAsProxy; 997 998 // indicates if the server needs proxy authentication 999 private boolean needProxyAuthentication; 1000 1001 // response code to be send to the client peer 1002 private int responseCode; 1003 1004 /** 1005 * Creates the thread acting as a server side. 1006 */ 1007 public ServerWork(ServerSocket serverSocket) { 1008 // the server does not require proxy authentication 1009 // and sends OK_CODE (OK) response code 1010 this(serverSocket, OK_CODE, false); 1011 } 1012 1013 /** 1014 * Creates the thread acting as a server side. 1015 * @param serverSocket the server socket to be used during connection 1016 * @param responseCode the response code to be sent to the client 1017 * @param needProxyAuthentication 1018 * indicates if the server needs proxy authentication 1019 */ 1020 public ServerWork(ServerSocket serverSocket, int responseCode, 1021 boolean needProxyAuthentication) { 1022 this.serverSocket = serverSocket; 1023 this.responseCode = responseCode; 1024 this.needProxyAuthentication = needProxyAuthentication; 1025 // will act as a proxy server if the specified server socket 1026 // is not a secure server socket 1027 if (serverSocket instanceof SSLServerSocket) { 1028 // demand client to send its certificate 1029 ((SSLServerSocket) serverSocket).setNeedClientAuth(true); 1030 // work as a HTTPS server, not as HTTP proxy 1031 this.actAsProxy = false; 1032 } else { 1033 this.actAsProxy = true; 1034 } 1035 this.actAsProxy = !(serverSocket instanceof SSLServerSocket); 1036 setName(this.actAsProxy ? "Proxy Server" : "Server"); 1037 } 1038 1039 /** 1040 * Closes the connection. 1041 */ 1042 public void closeSocket(Socket socket) { 1043 try { 1044 socket.getInputStream().close(); 1045 } catch (IOException e) {} 1046 try { 1047 socket.getOutputStream().close(); 1048 } catch (IOException e) {} 1049 try { 1050 socket.close(); 1051 } catch (IOException e) {} 1052 } 1053 1054 /** 1055 * Performs the actual server work. 1056 * If some exception occurs during the work it will be 1057 * stored in the <code>thrown</code> field. 1058 */ 1059 public void run() { 1060 // the buffer used for reading the messages 1061 byte[] buff = new byte[2048]; 1062 // the number of bytes read into the buffer 1063 int num; 1064 try { 1065 // configure the server socket to avoid blocking 1066 serverSocket.setSoTimeout(TIMEOUT); 1067 // accept client connection 1068 peerSocket = serverSocket.accept(); 1069 // configure the client connection to avoid blocking 1070 peerSocket.setSoTimeout(TIMEOUT); 1071 log("Client connection ACCEPTED"); 1072 1073 InputStream is = peerSocket.getInputStream(); 1074 OutputStream os = peerSocket.getOutputStream(); 1075 1076 num = is.read(buff); 1077 String message = new String(buff, 0, num); 1078 log("Got request:\n" + message); 1079 log("------------------"); 1080 1081 if (!actAsProxy) { 1082 // Act as Server (not Proxy) side 1083 if (message.startsWith("POST")) { 1084 // client connection sent some data 1085 log("try to read client data"); 1086 num = is.read(buff); 1087 message = new String(buff, 0, num); 1088 log("client's data: '" + message + "'"); 1089 // check the received data 1090 assertEquals(clientsData, message); 1091 } 1092 // just send the response 1093 os 1094 .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) 1095 .getBytes()); 1096 // and return 1097 log("Work is DONE"); 1098 return; 1099 } 1100 1101 // Do proxy work 1102 if (needProxyAuthentication) { 1103 log("Authentication required ..."); 1104 // send Authentication Request 1105 os.write(respAuthenticationRequired.getBytes()); 1106 // read response 1107 num = is.read(buff); 1108 if (num == -1) { 1109 // this connection was closed, 1110 // do clean up and create new one: 1111 closeSocket(peerSocket); 1112 peerSocket = serverSocket.accept(); 1113 peerSocket.setSoTimeout(TIMEOUT); 1114 log("New client connection ACCEPTED"); 1115 is = peerSocket.getInputStream(); 1116 os = peerSocket.getOutputStream(); 1117 num = is.read(buff); 1118 } 1119 message = new String(buff, 0, num); 1120 log("Got authenticated request:\n" + message); 1121 log("------------------"); 1122 // check provided authorization credentials 1123 assertTrue("Received message does not contain " 1124 + "authorization credentials", message 1125 .toLowerCase().indexOf("proxy-authorization:") > 0); 1126 } 1127 1128 // The content of this response will reach proxied HTTPUC 1129 // but will not reach proxied HTTPSUC 1130 // In case of HTTP connection it will be the final message, 1131 // in case of HTTPS connection this message will just indicate 1132 // that connection with remote host has been done 1133 // (i.e. SSL tunnel has been established). 1134 os.write(plainResponse.getBytes()); 1135 log("Sent OK RESPONSE"); 1136 1137 if (message.startsWith("CONNECT")) { // request for SSL tunnel 1138 log("Perform SSL Handshake..."); 1139 // create sslSocket acting as a remote server peer 1140 SSLSocket sslSocket = (SSLSocket) getContext() 1141 .getSocketFactory().createSocket(peerSocket, 1142 "localhost", peerSocket.getPort(), true); // do autoclose 1143 sslSocket.setUseClientMode(false); 1144 // demand client authentication 1145 sslSocket.setNeedClientAuth(true); 1146 sslSocket.startHandshake(); 1147 peerSocket = sslSocket; 1148 is = peerSocket.getInputStream(); 1149 os = peerSocket.getOutputStream(); 1150 1151 // read the HTTP request sent by secure connection 1152 // (HTTPS request) 1153 num = is.read(buff); 1154 message = new String(buff, 0, num); 1155 log("[Remote Server] Request from SSL tunnel:\n" + message); 1156 log("------------------"); 1157 1158 if (message.startsWith("POST")) { 1159 // client connection sent some data 1160 log("[Remote Server] try to read client data"); 1161 num = is.read(buff); 1162 message = new String(buff, 0, num); 1163 log("[Remote Server] client's data: '" + message + "'"); 1164 // check the received data 1165 assertEquals(clientsData, message); 1166 } 1167 1168 log("[Remote Server] Sending the response by SSL tunnel.."); 1169 // send the response with specified response code 1170 os 1171 .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) 1172 .getBytes()); 1173 } 1174 log("Work is DONE"); 1175 } catch (Throwable e) { 1176 if (DO_LOG) { 1177 e.printStackTrace(); 1178 } 1179 thrown = e; 1180 } finally { 1181 closeSocket(peerSocket); 1182 try { 1183 serverSocket.close(); 1184 } catch (IOException e) {} 1185 } 1186 } 1187 } 1188 1189 /** 1190 * The class used for client side works. It could be used to test 1191 * both HttpURLConnection and HttpsURLConnection. 1192 */ 1193 static class ClientConnectionWork extends Work { 1194 1195 // connection to be used to contact the server side 1196 private HttpURLConnection connection; 1197 1198 /** 1199 * Creates the thread acting as a client side. 1200 * @param connection connection to be used to contact the server side 1201 */ 1202 public ClientConnectionWork(HttpURLConnection connection) { 1203 this.connection = connection; 1204 setName("Client Connection"); 1205 log("Created over connection: " + connection.getClass()); 1206 } 1207 1208 /** 1209 * Performs the actual client work. 1210 * If some exception occurs during the work it will be 1211 * stored in the <code>thrown<code> field. 1212 */ 1213 public void run() { 1214 try { 1215 log("Opening the connection.."); 1216 connection.connect(); 1217 log("Connection has been ESTABLISHED, using proxy: " 1218 + connection.usingProxy()); 1219 if (connection.getDoOutput()) { 1220 // connection configured to post data, do so 1221 connection.getOutputStream().write(clientsData.getBytes()); 1222 } 1223 // read the content of HTTP(s) response 1224 InputStream is = connection.getInputStream(); 1225 log("Input Stream obtained"); 1226 byte[] buff = new byte[2048]; 1227 int num = 0; 1228 int byt = 0; 1229 while ((num < buff.length) && (is.available() > 0) 1230 && ((byt = is.read()) != -1)) { 1231 buff[num++] = (byte) byt; 1232 } 1233 String message = new String(buff, 0, num); 1234 log("Got content:\n" + message); 1235 log("------------------"); 1236 log("Response code: " + connection.getResponseCode()); 1237 1238 if (connection instanceof HttpsURLConnection) { 1239 assertEquals(httpsResponseContent, message); 1240 } else { 1241 assertEquals(plainResponseContent, message); 1242 } 1243 } catch (Throwable e) { 1244 if (DO_LOG) { 1245 e.printStackTrace(); 1246 } 1247 thrown = e; 1248 } 1249 } 1250 } 1251 1252 public static junit.framework.Test suite() { 1253 return new TestSuite(HttpsURLConnectionTest.class); 1254 } 1255 1256 public static void main(String[] args) { 1257 junit.textui.TestRunner.run(suite()); 1258 } 1259} 1260