001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */package org.apache.mina.filter.ssl; 020 021import java.io.BufferedReader; 022import java.io.IOException; 023import java.io.InputStreamReader; 024import java.net.InetAddress; 025import java.net.InetSocketAddress; 026import java.net.Socket; 027import java.security.GeneralSecurityException; 028import java.security.KeyStore; 029import java.security.Security; 030 031import javax.net.ssl.KeyManagerFactory; 032import javax.net.ssl.SSLContext; 033import javax.net.ssl.SSLSocketFactory; 034import javax.net.ssl.TrustManagerFactory; 035 036import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; 037import org.apache.mina.core.service.IoHandlerAdapter; 038import org.apache.mina.core.session.IoSession; 039import org.apache.mina.filter.codec.ProtocolCodecFilter; 040import org.apache.mina.filter.codec.textline.TextLineCodecFactory; 041import org.apache.mina.transport.socket.nio.NioSocketAcceptor; 042import org.apache.mina.util.AvailablePortFinder; 043import org.junit.Test; 044 045/** 046 * Test a SSL session where the connection is established and closed twice. It should be 047 * processed correctly (Test for DIRMINA-650) 048 * 049 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 050 */ 051public class SslTest { 052 /** A static port used for his test, chosen to avoid collisions */ 053 private static final int port = AvailablePortFinder.getNextAvailable(5555); 054 055 private static Exception clientError = null; 056 057 private static InetAddress address; 058 059 private static SSLSocketFactory factory; 060 061 /** A JVM independant KEY_MANAGER_FACTORY algorithm */ 062 private static final String KEY_MANAGER_FACTORY_ALGORITHM; 063 064 static { 065 String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); 066 if (algorithm == null) { 067 algorithm = KeyManagerFactory.getDefaultAlgorithm(); 068 } 069 070 KEY_MANAGER_FACTORY_ALGORITHM = algorithm; 071 } 072 073 private static class TestHandler extends IoHandlerAdapter { 074 public void messageReceived(IoSession session, Object message) throws Exception { 075 String line = (String) message; 076 077 if (line.startsWith("hello")) { 078 //System.out.println("Server got: 'hello', waiting for 'send'"); 079 Thread.sleep(1500); 080 } else if (line.startsWith("send")) { 081 //System.out.println("Server got: 'send', sending 'data'"); 082 StringBuilder sb = new StringBuilder(); 083 084 for ( int i = 0; i < 10000; i++) { 085 sb.append('A'); 086 } 087 088 session.write(sb.toString()); 089 session.close(true); 090 } 091 } 092 } 093 094 /** 095 * Starts a Server with the SSL Filter and a simple text line 096 * protocol codec filter 097 */ 098 private static void startServer() throws Exception { 099 NioSocketAcceptor acceptor = new NioSocketAcceptor(); 100 101 acceptor.setReuseAddress(true); 102 DefaultIoFilterChainBuilder filters = acceptor.getFilterChain(); 103 104 // Inject the SSL filter 105 SslFilter sslFilter = new SslFilter(createSSLContext()); 106 filters.addLast("sslFilter", sslFilter); 107 108 // Inject the TestLine codec filter 109 filters.addLast("text", new ProtocolCodecFilter(new TextLineCodecFactory())); 110 111 acceptor.setHandler(new TestHandler()); 112 acceptor.bind(new InetSocketAddress(port)); 113 } 114 115 /** 116 * Starts a client which will connect twice using SSL 117 */ 118 private static void startClient() throws Exception { 119 address = InetAddress.getByName("localhost"); 120 121 SSLContext context = createSSLContext(); 122 factory = context.getSocketFactory(); 123 124 connectAndSend(); 125 126 // This one will throw a SocketTimeoutException if DIRMINA-650 is not fixed 127 connectAndSend(); 128 } 129 130 private static void connectAndSend() throws Exception { 131 Socket parent = new Socket(address, port); 132 Socket socket = factory.createSocket(parent, address.getCanonicalHostName(), port, false); 133 134 //System.out.println("Client sending: hello"); 135 socket.getOutputStream().write("hello \n".getBytes()); 136 socket.getOutputStream().flush(); 137 socket.setSoTimeout(1000000); 138 139 //System.out.println("Client sending: send"); 140 socket.getOutputStream().write("send\n".getBytes()); 141 socket.getOutputStream().flush(); 142 143 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 144 String line = in.readLine(); 145 //System.out.println("Client got: " + line); 146 socket.close(); 147 148 } 149 150 private static SSLContext createSSLContext() throws IOException, GeneralSecurityException { 151 char[] passphrase = "password".toCharArray(); 152 153 SSLContext ctx = SSLContext.getInstance("TLS"); 154 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM); 155 TrustManagerFactory tmf = TrustManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM); 156 157 KeyStore ks = KeyStore.getInstance("JKS"); 158 KeyStore ts = KeyStore.getInstance("JKS"); 159 160 ks.load(SslTest.class.getResourceAsStream("keystore.sslTest"), passphrase); 161 ts.load(SslTest.class.getResourceAsStream("truststore.sslTest"), passphrase); 162 163 kmf.init(ks, passphrase); 164 tmf.init(ts); 165 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 166 167 return ctx; 168 } 169 170 @Test 171 public void testSSL() throws Exception { 172 startServer(); 173 174 Thread t = new Thread() { 175 public void run() { 176 try { 177 startClient(); 178 } catch (Exception e) { 179 clientError = e; 180 } 181 } 182 }; 183 t.start(); 184 t.join(); 185 if (clientError != null) 186 throw clientError; 187 } 188}