/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.luni.tests.internal.net.www.protocol.https; //import dalvik.annotation.AndroidOnly; //import dalvik.annotation.BrokenTest; //import dalvik.annotation.KnownFailure; //import dalvik.annotation.TestTargetClass; //import dalvik.annotation.TestTargets; //import dalvik.annotation.TestLevel; //import dalvik.annotation.TestTargetNew; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Authenticator; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.URL; import java.security.KeyStore; import java.security.cert.Certificate; import java.util.Arrays; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import junit.framework.TestCase; /** * Implementation independent test for HttpsURLConnection. * The test needs certstore file placed in system classpath * and named as "key_store." + the type of the * default KeyStore installed in the system in lower case. * <br> * For example: if default KeyStore type in the system is BKS * (i.e. java.security file sets up the property keystore.type=BKS), * thus classpath should point to the directory with "key_store.bks" * file. * <br> * This certstore file should contain self-signed certificate * generated by keytool utility in a usual way. * <br> * The password to the certstore should be "password" (without quotes). */ //@TestTargetClass(HttpsURLConnection.class) public class HttpsURLConnectionTest extends TestCase { // the password to the store private static final String KS_PASSWORD = "password"; // turn on/off logging private static final boolean DO_LOG = false; // read/connection timeout value private static final int TIMEOUT = 5000; // OK response code private static final int OK_CODE = 200; // Not Found response code private static final int NOT_FOUND_CODE = 404; // Proxy authentication required response code private static final int AUTHENTICATION_REQUIRED_CODE = 407; // fields keeping the system values of corresponding properties private static String systemKeyStoreType; private static String systemKeyStore; private static String systemKeyStorePassword; private static String systemTrustStoreType; private static String systemTrustStore; private static String systemTrustStorePassword; private static File store; static { try { store = File.createTempFile("key_store", "bks"); } catch (Exception e) { // ignore } } /** * Checks that HttpsURLConnection's default SSLSocketFactory is operable. @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies that HttpsURLConnection's default SSLSocketFactory is operable.", method = "getDefaultSSLSocketFactory", args = {} ) @AndroidOnly("we only have a .bks key store in the test resources") */ public void testGetDefaultSSLSocketFactory() throws Exception { // set up the properties defining the default values needed by SSL stuff setUpStoreProperties(); try { SSLSocketFactory defaultSSLSF = HttpsURLConnection .getDefaultSSLSocketFactory(); ServerSocket ss = new ServerSocket(0); Socket s = defaultSSLSF .createSocket("localhost", ss.getLocalPort()); ss.accept(); s.close(); ss.close(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Checks if HTTPS connection performs initial SSL handshake with the * server working over SSL, sends encrypted HTTP request, * and receives expected HTTP response. After HTTPS session if finished * test checks connection state parameters established by * HttpsURLConnection. @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies if HTTPS connection performs initial SSL handshake with the server working over SSL, sends encrypted HTTP request, and receives expected HTTP response.", method = "setDefaultHostnameVerifier", args = {javax.net.ssl.HostnameVerifier.class} ) @KnownFailure("Handshake fails.") @BrokenTest("Different behavior between cts host and run-core-test") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testHttpsConnection() throws Throwable { // set up the properties defining the default values needed by SSL stuff setUpStoreProperties(); try { // create the SSL server socket acting as a server SSLContext ctx = getContext(); ServerSocket ss = ctx.getServerSocketFactory() .createServerSocket(0); // create the HostnameVerifier to check hostname verification TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create url connection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); // perform the interaction between the peers SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); // check the connection state checkConnectionStateParameters(connection, peerSocket); // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests the behaviour of HTTPS connection in case of unavailability * of requested resource. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setReadTimeout", args = {int.class} ) }) @KnownFailure("Handshake fails.") @BrokenTest("Different behavior between cts host and run-core-test") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testHttpsConnection_Not_Found_Response() throws Throwable { // set up the properties defining the default values needed by SSL stuff setUpStoreProperties(); try { // create the SSL server socket acting as a server SSLContext ctx = getContext(); ServerSocket ss = ctx.getServerSocketFactory() .createServerSocket(0); // create the HostnameVerifier to check hostname verification TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create url connection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); try { doInteraction(connection, ss, NOT_FOUND_CODE); fail("Expected exception was not thrown."); } catch (FileNotFoundException e) { if (DO_LOG) { System.out.println("Expected exception was thrown: " + e.getMessage()); } } // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests possibility to set up the default SSLSocketFactory * to be used by HttpsURLConnection. @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies possibility to set up the default SSLSocketFactory to be used by HttpsURLConnection.", method = "setDefaultSSLSocketFactory", args = {javax.net.ssl.SSLSocketFactory.class} ) @AndroidOnly("we only have a .bks key store in the test resources") @KnownFailure("End to end test fails. No response data is transferred from server to client") */ public void testSetDefaultSSLSocketFactory() throws Throwable { // create the SSLServerSocket which will be used by server side SSLContext ctx = getContext(); SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() .createServerSocket(0); SSLSocketFactory socketFactory = (SSLSocketFactory) ctx .getSocketFactory(); // set up the factory as default HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory); // check the result assertSame("Default SSLSocketFactory differs from expected", socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory()); // create the HostnameVerifier to check hostname verification TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); TestHostnameVerifier hnv_late = new TestHostnameVerifier(); // late initialization: should not be used for created connection HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); // perform the interaction between the peers SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); // check the connection state checkConnectionStateParameters(connection, peerSocket); // check the verification process assertTrue("Hostname verification was not done", hnv.verified); assertFalse( "Hostname verification should not be done by this verifier", hnv_late.verified); // check the used SSLSocketFactory assertSame("Default SSLSocketFactory should be used", HttpsURLConnection.getDefaultSSLSocketFactory(), connection .getSSLSocketFactory()); // should silently exit connection.connect(); } /** * Tests possibility to set up the SSLSocketFactory * to be used by HttpsURLConnection. @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies possibility to set up the SSLSocketFactory to be used by HttpsURLConnection.", method = "setSSLSocketFactory", args = {javax.net.ssl.SSLSocketFactory.class} ) @AndroidOnly("we only have a .bks key store in the test resources") @KnownFailure("End to end test fails. No response data is transferred from server to client") */ public void testSetSSLSocketFactory() throws Throwable { // create the SSLServerSocket which will be used by server side SSLContext ctx = getContext(); SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory() .createServerSocket(0); // create the HostnameVerifier to check hostname verification TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); SSLSocketFactory socketFactory = (SSLSocketFactory) ctx .getSocketFactory(); connection.setSSLSocketFactory(socketFactory); TestHostnameVerifier hnv_late = new TestHostnameVerifier(); // late initialization: should not be used for created connection HttpsURLConnection.setDefaultHostnameVerifier(hnv_late); // perform the interaction between the peers SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); // check the connection state checkConnectionStateParameters(connection, peerSocket); // check the verification process assertTrue("Hostname verification was not done", hnv.verified); assertFalse( "Hostname verification should not be done by this verifier", hnv_late.verified); // check the used SSLSocketFactory assertNotSame("Default SSLSocketFactory should not be used", HttpsURLConnection.getDefaultSSLSocketFactory(), connection .getSSLSocketFactory()); assertSame("Result differs from expected", socketFactory, connection .getSSLSocketFactory()); // should silently exit connection.connect(); } /** * Tests the behaviour of HttpsURLConnection in case of retrieving * of the connection state parameters before connection has been made. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", method = "getCipherSuite", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", method = "getPeerPrincipal", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", method = "getLocalPrincipal", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", method = "getServerCertificates", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.", method = "getLocalCertificates", args = {} ) }) @AndroidOnly("we only have a .bks key store in the test resources") */ public void testUnconnectedStateParameters() throws Throwable { // create HttpsURLConnection to be tested URL url = new URL("https://localhost:55555"); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); try { connection.getCipherSuite(); fail("Expected IllegalStateException was not thrown"); } catch (IllegalStateException e) {} try { connection.getPeerPrincipal(); fail("Expected IllegalStateException was not thrown"); } catch (IllegalStateException e) {} try { connection.getLocalPrincipal(); fail("Expected IllegalStateException was not thrown"); } catch (IllegalStateException e) {} try { connection.getServerCertificates(); fail("Expected IllegalStateException was not thrown"); } catch (IllegalStateException e) {} try { connection.getLocalCertificates(); fail("Expected IllegalStateException was not thrown"); } catch (IllegalStateException e) {} } /** * Tests if setHostnameVerifier() method replaces default verifier. @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies if setHostnameVerifier() method replaces default verifier.", method = "setHostnameVerifier", args = {javax.net.ssl.HostnameVerifier.class} ) @KnownFailure("Handshake fails.") @BrokenTest("Different behavior between cts host and run-core-test") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testSetHostnameVerifier() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side SSLServerSocket ss = (SSLServerSocket) getContext() .getServerSocketFactory().createServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); TestHostnameVerifier hnv_late = new TestHostnameVerifier(); // replace default verifier connection.setHostnameVerifier(hnv_late); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); assertTrue("Hostname verification was not done", hnv_late.verified); assertFalse( "Hostname verification should not be done by this verifier", hnv.verified); checkConnectionStateParameters(connection, peerSocket); // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests the behaviour in case of sending the data to the server. @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour in case of sending the data to the server.", method = "setDoOutput", args = {boolean.class} ) @KnownFailure("Handshake fails.") @BrokenTest("Different behavior between cts host and run-core-test") @AndroidOnly("we only have a .bks key store in the test resources") */ public void test_doOutput() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side SSLServerSocket ss = (SSLServerSocket) getContext() .getServerSocketFactory().createServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(); connection.setDoOutput(true); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); checkConnectionStateParameters(connection, peerSocket); // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests HTTPS connection process made through the proxy server. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server.", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server.", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server.", method = "setReadTimeout", args = {int.class} ) }) @KnownFailure("Handshake fails.") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testProxyConnection() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://requested.host:55556/requested.data"); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); checkConnectionStateParameters(connection, peerSocket); // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests HTTPS connection process made through the proxy server. * Proxy server needs authentication. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.", method = "setReadTimeout", args = {int.class} ) }) @KnownFailure("Handshake fails.") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testProxyAuthConnection() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); Authenticator.setDefault(new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("user", "password" .toCharArray()); } }); // create HttpsURLConnection to be tested URL url = new URL("https://requested.host:55555/requested.data"); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); checkConnectionStateParameters(connection, peerSocket); // should silently exit connection.connect(); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests HTTPS connection process made through the proxy server. * 2 HTTPS connections are opened for one URL. For the first time * the connection is opened through one proxy, * for the second time through another. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies HTTPS connection process made through the proxy server.", method = "getCipherSuite", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies HTTPS connection process made through the proxy server.", method = "getLocalPrincipal", args = {} ), @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies HTTPS connection process made through the proxy server.", method = "getPeerPrincipal", args = {} ) }) @KnownFailure("Handshake fails.") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testConsequentProxyConnection() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://requested.host:55555/requested.data"); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss); checkConnectionStateParameters(connection, peerSocket); // create another SSLServerSocket which will be used by server side ss = new ServerSocket(0); connection = (HttpsURLConnection) url.openConnection(new Proxy( Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); // perform the interaction between the peers and check the results peerSocket = (SSLSocket) doInteraction(connection, ss); checkConnectionStateParameters(connection, peerSocket); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests HTTPS connection process made through the proxy server. * Proxy server needs authentication. * Client sends data to the server. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", method = "setReadTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.", method = "setDoOutput", args = {boolean.class} ) }) @KnownFailure("Handshake fails.") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testProxyAuthConnection_doOutput() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); Authenticator.setDefault(new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("user", "password" .toCharArray()); } }); // create HttpsURLConnection to be tested URL url = new URL("https://requested.host:55554/requested.data"); HttpsURLConnection connection = (HttpsURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); connection.setDoOutput(true); // perform the interaction between the peers and check the results SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss, OK_CODE, true); checkConnectionStateParameters(connection, peerSocket); } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests HTTPS connection process made through the proxy server. * Proxy server needs authentication but client fails to authenticate * (Authenticator was not set up in the system). @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).", method = "setReadTimeout", args = {int.class} ) }) @AndroidOnly("we only have a .bks key store in the test resources") */ public void testProxyAuthConnectionFailed() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://requested.host:55555/requested.data"); HttpURLConnection connection = (HttpURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); // perform the interaction between the peers and check the results try { doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE, true); } catch (IOException e) { // SSL Tunnelling failed if (DO_LOG) { System.out.println("Got expected IOException: " + e.getMessage()); } } } finally { // roll the properties back to system values tearDownStoreProperties(); } } /** * Tests the behaviour of HTTPS connection in case of unavailability * of requested resource. @TestTargets({ @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setDoInput", args = {boolean.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setConnectTimeout", args = {int.class} ), @TestTargetNew( level = TestLevel.PARTIAL, notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.", method = "setReadTimeout", args = {int.class} ) }) @KnownFailure("Handshake fails.") @AndroidOnly("we only have a .bks key store in the test resources") */ public void testProxyConnection_Not_Found_Response() throws Throwable { // setting up the properties pointing to the key/trust stores setUpStoreProperties(); try { // create the SSLServerSocket which will be used by server side ServerSocket ss = new ServerSocket(0); // create the HostnameVerifier to check that Hostname verification // is done TestHostnameVerifier hnv = new TestHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hnv); // create HttpsURLConnection to be tested URL url = new URL("https://localhost:" + ss.getLocalPort()); HttpURLConnection connection = (HttpURLConnection) url .openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", ss .getLocalPort()))); try { doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND fail("Expected exception was not thrown."); } catch (FileNotFoundException e) { if (DO_LOG) { System.out.println("Expected exception was thrown: " + e.getMessage()); } } } finally { // roll the properties back to system values tearDownStoreProperties(); } } // --------------------------------------------------------------------- // ------------------------ Staff Methods ------------------------------ // --------------------------------------------------------------------- /** * Log the name of the test case to be executed. */ public void setUp() throws Exception { if (DO_LOG) { System.out.println(); System.out.println("------------------------"); System.out.println("------ " + getName()); System.out.println("------------------------"); } if (store != null) { String ksFileName = "org/apache/harmony/luni/tests/key_store." + KeyStore.getDefaultType().toLowerCase(); InputStream in = getClass().getClassLoader() .getResourceAsStream(ksFileName); FileOutputStream out = new FileOutputStream(store); BufferedInputStream bufIn = new BufferedInputStream(in, 8192); while (bufIn.available() > 0) { byte[] buf = new byte[128]; int read = bufIn.read(buf); out.write(buf, 0, read); } bufIn.close(); out.close(); } else { fail("couldn't set up key store"); } } public void tearDown() { if (store != null) { store.delete(); } } /** * Checks the HttpsURLConnection getter's values and compares * them with actual corresponding values of remote peer. */ public static void checkConnectionStateParameters( HttpsURLConnection clientConnection, SSLSocket serverPeer) throws Exception { SSLSession session = serverPeer.getSession(); assertEquals(session.getCipherSuite(), clientConnection .getCipherSuite()); assertEquals(session.getLocalPrincipal(), clientConnection .getPeerPrincipal()); assertEquals(session.getPeerPrincipal(), clientConnection .getLocalPrincipal()); Certificate[] serverCertificates = clientConnection .getServerCertificates(); Certificate[] localCertificates = session.getLocalCertificates(); assertTrue("Server certificates differ from expected", Arrays.equals( serverCertificates, localCertificates)); localCertificates = clientConnection.getLocalCertificates(); serverCertificates = session.getPeerCertificates(); assertTrue("Local certificates differ from expected", Arrays.equals( serverCertificates, localCertificates)); } /** * Returns the file name of the key/trust store. The key store file * (named as "key_store." + extension equals to the default KeyStore * type installed in the system in lower case) is searched in classpath. * @throws AssertionFailedError if property was not set * or file does not exist. */ private static String getKeyStoreFileName() { return store.getAbsolutePath(); } /** * Builds and returns the context used for secure socket creation. */ private static SSLContext getContext() throws Exception { String type = KeyStore.getDefaultType(); SSLContext ctx; String keyStore = getKeyStoreFileName(); File keyStoreFile = new File(keyStore); FileInputStream fis = new FileInputStream(keyStoreFile); KeyStore ks = KeyStore.getInstance(type); ks.load(fis, KS_PASSWORD.toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory .getDefaultAlgorithm()); kmf.init(ks, KS_PASSWORD.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); ctx = SSLContext.getInstance("TLSv1"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); return ctx; } /** * Sets up the properties pointing to the key store and trust store * and used as default values by JSSE staff. This is needed to test * HTTPS behaviour in the case of default SSL Socket Factories. */ private static void setUpStoreProperties() throws Exception { String type = KeyStore.getDefaultType(); systemKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType"); systemKeyStore = System.getProperty("javax.net.ssl.keyStore"); systemKeyStorePassword = System .getProperty("javax.net.ssl.keyStorePassword"); systemTrustStoreType = System .getProperty("javax.net.ssl.trustStoreType"); systemTrustStore = System.getProperty("javax.net.ssl.trustStore"); systemTrustStorePassword = System .getProperty("javax.net.ssl.trustStorePassword"); System.setProperty("javax.net.ssl.keyStoreType", type); System.setProperty("javax.net.ssl.keyStore", getKeyStoreFileName()); System.setProperty("javax.net.ssl.keyStorePassword", KS_PASSWORD); System.setProperty("javax.net.ssl.trustStoreType", type); System.setProperty("javax.net.ssl.trustStore", getKeyStoreFileName()); System.setProperty("javax.net.ssl.trustStorePassword", KS_PASSWORD); } /** * Rolls back the values of system properties. */ private static void tearDownStoreProperties() { if (systemKeyStoreType == null) { System.clearProperty("javax.net.ssl.keyStoreType"); } else { System .setProperty("javax.net.ssl.keyStoreType", systemKeyStoreType); } if (systemKeyStore == null) { System.clearProperty("javax.net.ssl.keyStore"); } else { System.setProperty("javax.net.ssl.keyStore", systemKeyStore); } if (systemKeyStorePassword == null) { System.clearProperty("javax.net.ssl.keyStorePassword"); } else { System.setProperty("javax.net.ssl.keyStorePassword", systemKeyStorePassword); } if (systemTrustStoreType == null) { System.clearProperty("javax.net.ssl.trustStoreType"); } else { System.setProperty("javax.net.ssl.trustStoreType", systemTrustStoreType); } if (systemTrustStore == null) { System.clearProperty("javax.net.ssl.trustStore"); } else { System.setProperty("javax.net.ssl.trustStore", systemTrustStore); } if (systemTrustStorePassword == null) { System.clearProperty("javax.net.ssl.trustStorePassword"); } else { System.setProperty("javax.net.ssl.trustStorePassword", systemTrustStorePassword); } } /** * Performs interaction between client's HttpURLConnection and * servers side (ServerSocket). */ public static Socket doInteraction( final HttpURLConnection clientConnection, final ServerSocket serverSocket) throws Throwable { return doInteraction(clientConnection, serverSocket, OK_CODE, false); } /** * Performs interaction between client's HttpURLConnection and * servers side (ServerSocket). Server will response with specified * response code. */ public static Socket doInteraction( final HttpURLConnection clientConnection, final ServerSocket serverSocket, final int responseCode) throws Throwable { return doInteraction(clientConnection, serverSocket, responseCode, false); } /** * Performs interaction between client's HttpURLConnection and * servers side (ServerSocket). Server will response with specified * response code. * @param doAuthentication specifies * if the server needs client authentication. */ public static Socket doInteraction( final HttpURLConnection clientConnection, final ServerSocket serverSocket, final int responseCode, final boolean doAuthentication) throws Throwable { // set up the connection clientConnection.setDoInput(true); clientConnection.setConnectTimeout(TIMEOUT); clientConnection.setReadTimeout(TIMEOUT); ServerWork server = new ServerWork(serverSocket, responseCode, doAuthentication); ClientConnectionWork client = new ClientConnectionWork(clientConnection); server.start(); client.start(); client.join(); server.join(); if (client.thrown != null) { if (responseCode != OK_CODE) { // not OK response expected // it is probably expected exception, keep it as is throw client.thrown; } if ((client.thrown instanceof SocketTimeoutException) && (server.thrown != null)) { // server's exception is more informative in this case throw new Exception(server.thrown); } else { throw new Exception(client.thrown); } } if (server.thrown != null) { throw server.thrown; } return server.peerSocket; } /** * The host name verifier used in test. */ static class TestHostnameVerifier implements HostnameVerifier { boolean verified = false; public boolean verify(String hostname, SSLSession session) { if (DO_LOG) { System.out.println("***> verification " + hostname + " " + session.getPeerHost()); } verified = true; return true; } } /** * The base class for mock Client and Server. */ static class Work extends Thread { /** * The header of OK HTTP response. */ static final String responseHead = "HTTP/1.1 200 OK\n"; /** * The content of the response. */ static final String plainResponseContent = "<HTML>\n" + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n" + "</HTML>"; /** * The tail of the response. */ static final String plainResponseTail = "Content-type: text/html\n" + "Content-length: " + plainResponseContent.length() + "\n\n" + plainResponseContent; /** * The response message to be sent in plain (HTTP) format. */ static final String plainResponse = responseHead + plainResponseTail; /** * The content of the response to be sent during HTTPS session. */ static final String httpsResponseContent = "<HTML>\n" + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n" + "</HTML>"; /** * The tail of the response to be sent during HTTPS session. */ static final String httpsResponseTail = "Content-type: text/html\n" + "Content-length: " + httpsResponseContent.length() + "\n\n" + httpsResponseContent; /** * The response requiring client's proxy authentication. */ static final String respAuthenticationRequired = "HTTP/1.0 407 Proxy authentication required\n" + "Proxy-authenticate: Basic realm=\"localhost\"\n\n"; /** * The data to be posted by client to the server. */ static final String clientsData = "_.-^ Client's Data ^-._"; /** * The exception thrown during peers interaction. */ protected Throwable thrown; /** * The print stream used for debug log. * If it is null debug info will not be printed. */ private PrintStream out = new PrintStream(System.out); /** * Prints log message. */ public synchronized void log(String message) { if (DO_LOG && (out != null)) { System.out.println("[" + getName() + "]: " + message); } } } /** * The class used for server side works. */ static class ServerWork extends Work { // the server socket used for connection private ServerSocket serverSocket; // the socket connected with client peer private Socket peerSocket; // indicates if the server acts as proxy server private boolean actAsProxy; // indicates if the server needs proxy authentication private boolean needProxyAuthentication; // response code to be send to the client peer private int responseCode; /** * Creates the thread acting as a server side. */ public ServerWork(ServerSocket serverSocket) { // the server does not require proxy authentication // and sends OK_CODE (OK) response code this(serverSocket, OK_CODE, false); } /** * Creates the thread acting as a server side. * @param serverSocket the server socket to be used during connection * @param responseCode the response code to be sent to the client * @param needProxyAuthentication * indicates if the server needs proxy authentication */ public ServerWork(ServerSocket serverSocket, int responseCode, boolean needProxyAuthentication) { this.serverSocket = serverSocket; this.responseCode = responseCode; this.needProxyAuthentication = needProxyAuthentication; // will act as a proxy server if the specified server socket // is not a secure server socket if (serverSocket instanceof SSLServerSocket) { // demand client to send its certificate ((SSLServerSocket) serverSocket).setNeedClientAuth(true); // work as a HTTPS server, not as HTTP proxy this.actAsProxy = false; } else { this.actAsProxy = true; } this.actAsProxy = !(serverSocket instanceof SSLServerSocket); setName(this.actAsProxy ? "Proxy Server" : "Server"); } /** * Closes the connection. */ public void closeSocket(Socket socket) { try { socket.getInputStream().close(); } catch (IOException e) {} try { socket.getOutputStream().close(); } catch (IOException e) {} try { socket.close(); } catch (IOException e) {} } /** * Performs the actual server work. * If some exception occurs during the work it will be * stored in the <code>thrown</code> field. */ public void run() { // the buffer used for reading the messages byte[] buff = new byte[2048]; // the number of bytes read into the buffer int num; try { // configure the server socket to avoid blocking serverSocket.setSoTimeout(TIMEOUT); // accept client connection peerSocket = serverSocket.accept(); // configure the client connection to avoid blocking peerSocket.setSoTimeout(TIMEOUT); log("Client connection ACCEPTED"); InputStream is = peerSocket.getInputStream(); OutputStream os = peerSocket.getOutputStream(); num = is.read(buff); String message = new String(buff, 0, num); log("Got request:\n" + message); log("------------------"); if (!actAsProxy) { // Act as Server (not Proxy) side if (message.startsWith("POST")) { // client connection sent some data log("try to read client data"); num = is.read(buff); message = new String(buff, 0, num); log("client's data: '" + message + "'"); // check the received data assertEquals(clientsData, message); } // just send the response os .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) .getBytes()); os.flush(); os.close(); // and return log("Work is DONE !actAsProxy"); return; } // Do proxy work if (needProxyAuthentication) { log("Authentication required ..."); // send Authentication Request os.write(respAuthenticationRequired.getBytes()); // read response num = is.read(buff); if (num == -1) { // this connection was closed, // do clean up and create new one: closeSocket(peerSocket); peerSocket = serverSocket.accept(); peerSocket.setSoTimeout(TIMEOUT); log("New client connection ACCEPTED"); is = peerSocket.getInputStream(); os = peerSocket.getOutputStream(); num = is.read(buff); } message = new String(buff, 0, num); log("Got authenticated request:\n" + message); log("------------------"); // check provided authorization credentials assertTrue("Received message does not contain " + "authorization credentials", message .toLowerCase().indexOf("proxy-authorization:") > 0); } // The content of this response will reach proxied HTTPUC // but will not reach proxied HTTPSUC // In case of HTTP connection it will be the final message, // in case of HTTPS connection this message will just indicate // that connection with remote host has been done // (i.e. SSL tunnel has been established). os.write(plainResponse.getBytes()); log("Sent OK RESPONSE"); if (message.startsWith("CONNECT")) { // request for SSL tunnel log("Perform SSL Handshake..."); // create sslSocket acting as a remote server peer SSLSocket sslSocket = (SSLSocket) getContext() .getSocketFactory().createSocket(peerSocket, "localhost", peerSocket.getPort(), true); // do autoclose sslSocket.setUseClientMode(false); // demand client authentication sslSocket.setNeedClientAuth(true); sslSocket.startHandshake(); peerSocket = sslSocket; is = peerSocket.getInputStream(); os = peerSocket.getOutputStream(); // read the HTTP request sent by secure connection // (HTTPS request) num = is.read(buff); message = new String(buff, 0, num); log("[Remote Server] Request from SSL tunnel:\n" + message); log("------------------"); if (message.startsWith("POST")) { // client connection sent some data log("[Remote Server] try to read client data"); num = is.read(buff); message = new String(buff, 0, num); log("[Remote Server] client's data: '" + message + "'"); // check the received data assertEquals(clientsData, message); } log("[Remote Server] Sending the response by SSL tunnel.."); // send the response with specified response code os .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail) .getBytes()); } log("Work is DONE actAsProxy"); } catch (Throwable e) { if (DO_LOG) { e.printStackTrace(); } thrown = e; } finally { closeSocket(peerSocket); try { serverSocket.close(); } catch (IOException e) {} } } } /** * The class used for client side works. It could be used to test * both HttpURLConnection and HttpsURLConnection. */ static class ClientConnectionWork extends Work { // connection to be used to contact the server side private HttpURLConnection connection; /** * Creates the thread acting as a client side. * @param connection connection to be used to contact the server side */ public ClientConnectionWork(HttpURLConnection connection) { this.connection = connection; setName("Client Connection"); log("Created over connection: " + connection.getClass()); } /** * Performs the actual client work. * If some exception occurs during the work it will be * stored in the <code>thrown<code> field. */ public void run() { try { log("Opening the connection.."); connection.connect(); log("Connection has been ESTABLISHED, using proxy: " + connection.usingProxy()); if (connection.getDoOutput()) { // connection configured to post data, do so connection.getOutputStream().write(clientsData.getBytes()); } // read the content of HTTP(s) response InputStream is = connection.getInputStream(); log("Input Stream obtained"); byte[] buff = new byte[2048]; int num = 0; int byt = 0; while ((num < buff.length) && (is.available() > 0) && ((byt = is.read()) != -1)) { buff[num++] = (byte) byt; } String message = new String(buff, 0, num); log("Got content:\n" + message); log("------------------"); log("Response code: " + connection.getResponseCode()); if (connection instanceof HttpsURLConnection) { assertEquals(httpsResponseContent, message); } else { assertEquals(plainResponseContent, message); } } catch (Throwable e) { if (DO_LOG) { e.printStackTrace(); } thrown = e; } } } }