| /* |
| * 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.tomcat.util.net; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| |
| import javax.net.ssl.HandshakeCompletedEvent; |
| import javax.net.ssl.HandshakeCompletedListener; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLSocket; |
| import javax.net.ssl.SSLSocketFactory; |
| |
| import org.junit.Assert; |
| import org.junit.Assume; |
| import org.junit.Test; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Wrapper; |
| import org.apache.catalina.startup.TesterServlet; |
| import org.apache.catalina.startup.Tomcat; |
| import org.apache.catalina.startup.TomcatBaseTest; |
| import org.apache.tomcat.util.buf.ByteChunk; |
| import org.apache.tomcat.websocket.server.WsContextListener; |
| |
| /** |
| * The keys and certificates used in this file are all available in svn and were |
| * generated using a test CA the files for which are in the Tomcat PMC private |
| * repository since not all of them are AL2 licensed. |
| */ |
| public class TestSsl extends TomcatBaseTest { |
| |
| @Test |
| public void testSimpleSsl() throws Exception { |
| TesterSupport.configureClientSsl(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File(getBuildDirectory(), "webapps/examples"); |
| org.apache.catalina.Context ctxt = tomcat.addWebapp( |
| null, "/examples", appDir.getAbsolutePath()); |
| ctxt.addApplicationListener(WsContextListener.class.getName()); |
| |
| TesterSupport.initSsl(tomcat); |
| |
| tomcat.start(); |
| ByteChunk res = getUrl("https://localhost:" + getPort() + |
| "/examples/servlets/servlet/HelloWorldExample"); |
| Assert.assertTrue(res.toString().indexOf("<a href=\"../helloworld.html\">") > 0); |
| } |
| |
| @Test |
| public void testKeyPass() throws Exception { |
| TesterSupport.configureClientSsl(); |
| |
| Tomcat tomcat = getTomcatInstance(); |
| |
| File appDir = new File(getBuildDirectory(), "webapps/examples"); |
| org.apache.catalina.Context ctxt = tomcat.addWebapp( |
| null, "/examples", appDir.getAbsolutePath()); |
| ctxt.addApplicationListener(WsContextListener.class.getName()); |
| |
| TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, |
| TesterSupport.JKS_PASS, TesterSupport.JKS_KEY_PASS); |
| |
| tomcat.start(); |
| ByteChunk res = getUrl("https://localhost:" + getPort() + |
| "/examples/servlets/servlet/HelloWorldExample"); |
| Assert.assertTrue(res.toString().indexOf("<a href=\"../helloworld.html\">") > 0); |
| } |
| |
| |
| @Test |
| public void testRenegotiateWorks() throws Exception { |
| Tomcat tomcat = getTomcatInstance(); |
| |
| Assume.assumeTrue("SSL renegotiation has to be supported for this test", |
| TesterSupport.isRenegotiationSupported(getTomcatInstance())); |
| |
| Context root = tomcat.addContext("", TEMP_DIR); |
| Wrapper w = |
| Tomcat.addServlet(root, "tester", new TesterServlet()); |
| w.setAsyncSupported(true); |
| root.addServletMappingDecoded("/", "tester"); |
| |
| TesterSupport.initSsl(tomcat); |
| |
| tomcat.start(); |
| |
| SSLContext sslCtx = SSLContext.getInstance("TLS"); |
| sslCtx.init(null, TesterSupport.getTrustManagers(), null); |
| SSLSocketFactory socketFactory = sslCtx.getSocketFactory(); |
| SSLSocket socket = (SSLSocket) socketFactory.createSocket("localhost", |
| getPort()); |
| |
| OutputStream os = socket.getOutputStream(); |
| InputStream is = socket.getInputStream(); |
| Reader r = new InputStreamReader(is); |
| |
| doRequest(os, r); |
| |
| TesterHandshakeListener listener = new TesterHandshakeListener(); |
| socket.addHandshakeCompletedListener(listener); |
| |
| socket.startHandshake(); |
| |
| // One request should be sufficient |
| int requestCount = 0; |
| int listenerComplete = 0; |
| try { |
| while (requestCount < 10) { |
| requestCount++; |
| doRequest(os, r); |
| if (listener.isComplete() && listenerComplete == 0) { |
| listenerComplete = requestCount; |
| } |
| } |
| } catch (AssertionError | IOException e) { |
| String message = "Failed on request number " + requestCount |
| + " after startHandshake(). " + e.getMessage(); |
| log.error(message, e); |
| Assert.fail(message); |
| } |
| |
| Assert.assertTrue(listener.isComplete()); |
| System.out.println("Renegotiation completed after " + listenerComplete + " requests"); |
| } |
| |
| private void doRequest(OutputStream os, Reader r) throws IOException { |
| char[] expectedResponseLine = "HTTP/1.1 200 OK\r\n".toCharArray(); |
| |
| os.write("GET /tester HTTP/1.1\r\n".getBytes()); |
| os.write("Host: localhost\r\n".getBytes()); |
| os.write("Connection: Keep-Alive\r\n\r\n".getBytes()); |
| os.flush(); |
| |
| // First check we get the expected response line |
| for (char c : expectedResponseLine) { |
| int read = r.read(); |
| Assert.assertEquals(c, read); |
| } |
| |
| // Skip to the end of the headers |
| char[] endOfHeaders ="\r\n\r\n".toCharArray(); |
| int found = 0; |
| while (found != endOfHeaders.length) { |
| if (r.read() == endOfHeaders[found]) { |
| found++; |
| } else { |
| found = 0; |
| } |
| } |
| |
| // Read the body |
| char[] expectedBody = "OK".toCharArray(); |
| for (char c : expectedBody) { |
| int read = r.read(); |
| Assert.assertEquals(c, read); |
| } |
| } |
| |
| private static class TesterHandshakeListener implements HandshakeCompletedListener { |
| |
| private volatile boolean complete = false; |
| |
| @Override |
| public void handshakeCompleted(HandshakeCompletedEvent event) { |
| complete = true; |
| } |
| |
| public boolean isComplete() { |
| return complete; |
| } |
| } |
| } |