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