View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.transport.nio;
    21
    22  import static org.junit.Assert.assertTrue;
    23
    24  import java.io.BufferedReader;
    25  import java.io.IOException;
    26  import java.io.InputStreamReader;
    27  import java.net.InetAddress;
    28  import java.net.InetSocketAddress;
    29  import java.net.Socket;
    30  import java.nio.ByteBuffer;
    31  import java.nio.charset.Charset;
    32  import java.security.GeneralSecurityException;
    33  import java.security.KeyStore;
    34  import java.security.Security;
    35  import java.util.concurrent.CountDownLatch;
    36  import java.util.concurrent.TimeUnit;
    37
    38  import javax.net.ssl.KeyManagerFactory;
    39  import javax.net.ssl.SSLContext;
    40  import javax.net.ssl.SSLSocketFactory;
    41  import javax.net.ssl.TrustManagerFactory;
    42
    43  import org.apache.mina.api.AbstractIoHandler;
    44  import org.apache.mina.api.IoSession;
    45  import org.apache.mina.transport.nio.NioTcpServer;
    46  import org.junit.Ignore;
    47  import org.junit.Test;
    48
    49  /**
    50   * Test a SSL session where the connection is established and closed twice. It should be
    51   * processed correctly (Test for DIRMINA-650)
    52   *
    53   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    54   */
    55  public class SslTest {
    56      private static Exception clientError = null;
    57
    58      private static InetAddress address;
    59
    60      private static SSLSocketFactory factory;
    61
    62      /** A JVM independant KEY_MANAGER_FACTORY algorithm */
    63      private static final String KEY_MANAGER_FACTORY_ALGORITHM;
    64
    65      static {
    66          String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
    67          if (algorithm == null) {
    68              algorithm = KeyManagerFactory.getDefaultAlgorithm();
    69          }
    70
    71          KEY_MANAGER_FACTORY_ALGORITHM = algorithm;
    72      }
    73
    74      private static class TestHandler extends AbstractIoHandler {
    75          public void messageReceived(IoSession session, Object message) {
    76              String line = Charset.defaultCharset().decode((ByteBuffer) message).toString();
    77
    78              if (line.startsWith("hello")) {
    79                  System.out.println("Server got: 'hello', waiting for 'send'");
    80              } else if (line.startsWith("send")) {
    81                  System.out.println("Server got: 'send', sending 'data'");
    82                  session.write(Charset.defaultCharset().encode("data\n"));
    83              }
    84          }
    85      }
    86
    87      /**
    88       * Starts a Server with the SSL Filter and a simple text line
    89       * protocol codec filter
    90       */
    91      private static int startServer() throws Exception {
    92          NioTcpServer server = new NioTcpServer();
    93
    94          server.setReuseAddress(true);
    95          server.getSessionConfig().setSslContext(createSSLContext());
    96          server.setIoHandler(new TestHandler());
    97          server.bind(new InetSocketAddress(0));
    98          return server.getServerSocketChannel().socket().getLocalPort();
    99      }
    100
    101     /**
    102      * Starts a client which will connect twice using SSL
    103      */
    104     private static void startClient(int port) throws Exception {
    105         address = InetAddress.getByName("localhost");
    106
    107         SSLContext context = createSSLContext();
    108         factory = context.getSocketFactory();
    109
    110         connectAndSend(port);
    111
    112         // This one will throw a SocketTimeoutException if DIRMINA-650 is not fixed
    113         connectAndSend(port);
    114     }
    115
    116     private static void connectAndSend(int port) throws Exception {
    117         Socket parent = new Socket(address, port);
    118         Socket socket = factory.createSocket(parent, address.getCanonicalHostName(), port, false);
    119
    120         System.out.println("Client sending: hello");
    121         socket.getOutputStream().write("hello                      \n".getBytes());
    122         socket.getOutputStream().flush();
    123         socket.setSoTimeout(10000);
    124
    125         System.out.println("Client sending: send");
    126         socket.getOutputStream().write("send\n".getBytes());
    127         socket.getOutputStream().flush();
    128
    129         BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    130         String line = in.readLine();
    131         System.out.println("Client got: " + line);
    132         socket.close();
    133
    134     }
    135
    136     private static SSLContext createSSLContext() throws IOException, GeneralSecurityException {
    137         char[] passphrase = "password".toCharArray();
    138
    139         SSLContext ctx = SSLContext.getInstance("TLS");
    140         KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
    141         TrustManagerFactory tmf = TrustManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
    142
    143         KeyStore ks = KeyStore.getInstance("JKS");
    144         KeyStore ts = KeyStore.getInstance("JKS");
    145
    146         ks.load(SslTest.class.getResourceAsStream("keystore.sslTest"), passphrase);
    147         ts.load(SslTest.class.getResourceAsStream("truststore.sslTest"), passphrase);
    148
    149         kmf.init(ks, passphrase);
    150         tmf.init(ts);
    151         ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    152
    153         return ctx;
    154     }
    155
    156     @Test
    157     @Ignore("check for fragmentation")
    158     public void testSSL() throws Exception {
    159         final int port = startServer();
    160
    161         Thread t = new Thread() {
    162             public void run() {
    163                 try {
    164                     startClient(port);
    165                 } catch (Exception e) {
    166                     clientError = e;
    167                 }
    168             }
    169         };
    170         t.start();
    171         t.join();
    172         if (clientError != null)
    173             throw clientError;
    174     }
    175
    176     @Test
    177     public void testBigMessage() throws IOException, GeneralSecurityException, InterruptedException {
    178         final CountDownLatch counter = new CountDownLatch(1);
    179         NioTcpServer server = new NioTcpServer();
    180         final int messageSize = 1 * 1024 * 1024;
    181
    182         /*
    183          * Server
    184          */
    185         server.setReuseAddress(true);
    186         server.getSessionConfig().setSslContext(createSSLContext());
    187         server.setIoHandler(new AbstractIoHandler() {
    188             private int receivedSize = 0;
    189
    190             /**
    191              * {@inheritedDoc}
    192              */
    193             @Override
    194             public void messageReceived(IoSession session, Object message) {
    195                 receivedSize += ((ByteBuffer) message).remaining();
    196                 if (receivedSize == messageSize) {
    197                     counter.countDown();
    198                 }
    199             }
    200         });
    201         server.bind(new InetSocketAddress(0));
    202         int port = server.getServerSocketChannel().socket().getLocalPort();
    203
    204         /*
    205          * Client
    206          */
    207         Socket socket = server.getSessionConfig().getSslContext().getSocketFactory().createSocket("localhost", port);
    208         socket.getOutputStream().write(new byte[messageSize]);
    209         socket.getOutputStream().flush();
    210         socket.close();
    211         assertTrue(counter.await(10, TimeUnit.SECONDS));
    212
    213     }
    214 }