package org.bubblecloud.ilves.server.jetty; import org.bubblecloud.ilves.security.CertificateUtil; import org.eclipse.jetty.server.*; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.security.cert.X509Certificate; /** * Class for testing Jetty TSL functionality. */ public class JettyTslTest { public static final String KEY_STORE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "test.jks"; @After public void after() { new File(KEY_STORE_PATH).deleteOnExit(); } @Ignore @Test public void testTsl() throws Exception { final String keyStorePassword = "changeme"; final String certificatePrivateKeyPassword = "changeme"; final String trustStorePath = KEY_STORE_PATH; final String trustStorePassword = "changeme"; final String clientCertificateAlias = "client"; final String clientCertificateCommonName = "client-common-name"; final String serverCertificateAlias = "server"; final String serverCertificateSubjectAlternativeNameIpAddress = "127.0.0.1"; final String serverCertificateCommonName = "server-common-name"; CertificateUtil.ensureServerCertificateExists( clientCertificateCommonName, null, clientCertificateAlias, certificatePrivateKeyPassword, KEY_STORE_PATH, keyStorePassword); CertificateUtil.ensureServerCertificateExists( serverCertificateCommonName, serverCertificateSubjectAlternativeNameIpAddress, serverCertificateAlias, certificatePrivateKeyPassword, KEY_STORE_PATH, keyStorePassword); final Server server = newServer( serverCertificateAlias, KEY_STORE_PATH, keyStorePassword, certificatePrivateKeyPassword, trustStorePath, trustStorePassword); server.setHandler(new AbstractHandler() { @Override public void handle(final String target, final Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws IOException, ServletException { final X509Certificate[] clientCertificates = (X509Certificate[]) httpServletRequest.getAttribute("javax.servlet.request.X509Certificate"); Assert.assertEquals(1, clientCertificates.length); System.out.println(clientCertificates[0].getSubjectDN().getName()); Assert.assertEquals("CN=" + clientCertificateCommonName, clientCertificates[0].getSubjectDN().getName()); httpServletResponse.setContentType("text/plain;charset=utf-8"); httpServletResponse.setStatus(HttpServletResponse.SC_OK); request.setHandled(true); httpServletResponse.getWriter().println("z"); } }); server.start(); final String postUrl = "https://127.0.0.1:8443/test"; final String postContent = "x=y"; final HttpsURLConnection httpsUrlConnection = newHttpsUrlConnection(new URL(postUrl), clientCertificateAlias, KEY_STORE_PATH, keyStorePassword, certificatePrivateKeyPassword, trustStorePath, trustStorePassword); final int responseCode = writePost(httpsUrlConnection, postContent); final X509Certificate[] serverCertificates = (X509Certificate[]) httpsUrlConnection.getServerCertificates(); Assert.assertEquals(1, serverCertificates.length); System.out.println(serverCertificates[0].getSubjectDN().getName()); Assert.assertEquals("CN=" + serverCertificateCommonName, serverCertificates[0].getSubjectDN().getName()); final InputStream inputStream; if (responseCode == HttpURLConnection.HTTP_OK) { inputStream = httpsUrlConnection.getInputStream(); } else { inputStream = httpsUrlConnection.getErrorStream(); } final String response = readResponse(inputStream); Assert.assertEquals("z", response); inputStream.close(); } private int writePost(HttpsURLConnection httpsUrlConnection, String postContent) throws IOException { httpsUrlConnection.setRequestMethod("POST"); httpsUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); httpsUrlConnection.setRequestProperty("Content-Length", Integer.toString(postContent.length())); final OutputStream outputStream = httpsUrlConnection.getOutputStream(); outputStream.write(postContent.getBytes("UTF-8")); outputStream.close(); return httpsUrlConnection.getResponseCode(); } private String readResponse(InputStream inputStream) throws IOException { final StringBuilder builder = new StringBuilder(); final BufferedReader reader = new BufferedReader( new InputStreamReader( inputStream ) ); String line; while(( line = reader.readLine() ) != null) { builder.append(line); } return builder.toString(); } private HttpsURLConnection newHttpsUrlConnection(final URL url, final String certificateAlias, final String keyStorePath, final String keyStorePassword, final String keyManagerPassword, final String trustStorePath, final String trustStorePassword) throws Exception { final HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) url.openConnection(); httpsUrlConnection.setRequestProperty("Connection", "close"); httpsUrlConnection.setDoInput(true); httpsUrlConnection.setDoOutput(true); httpsUrlConnection.setUseCaches(false); httpsUrlConnection.setConnectTimeout(30000); httpsUrlConnection.setReadTimeout(30000); final SslContextFactory sslContextFactory = newSslSocketFactory(certificateAlias, keyStorePath, keyStorePassword, keyManagerPassword, trustStorePath, trustStorePassword, false); sslContextFactory.start(); final SSLSocketFactory sslSocketFactory = sslContextFactory.getSslContext().getSocketFactory(); httpsUrlConnection.setSSLSocketFactory(sslSocketFactory); return httpsUrlConnection; } private Server newServer( final String certificateAlias, final String keyStorePath, final String keyStorePassword, final String keyManagerPassword, final String trustStorePath, final String trustStorePassword) throws Exception { final Server server = new Server(); final HttpConfiguration httpConfiguration = new HttpConfiguration(); httpConfiguration.setSecureScheme("https"); httpConfiguration.setSecurePort(8443); httpConfiguration.setOutputBufferSize(32768); httpConfiguration.setRequestHeaderSize(8192); httpConfiguration.setResponseHeaderSize(8192); httpConfiguration.setSendServerVersion(false); httpConfiguration.setSendDateHeader(false); final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration); httpsConfiguration.addCustomizer(new SecureRequestCustomizer()); // <-- HERE final SslContextFactory sslContextFactory = newSslSocketFactory(certificateAlias, keyStorePath, keyStorePassword, keyManagerPassword, trustStorePath, trustStorePassword, true); final ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration)); httpConnector.setPort(8080); httpConnector.setIdleTimeout(30000); final ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,"http/1.1"), new HttpConnectionFactory(httpsConfiguration)); httpsConnector.setPort(8443); httpsConnector.setIdleTimeout(30000); server.addConnector(httpConnector); server.addConnector(httpsConnector); return server; } private SslContextFactory newSslSocketFactory(final String certificateAlias, String keyStorePath, String keyStorePassword, String keyManagerPassword, String trustStorePath, String trustStorePassword, final boolean clientAuthentication) throws Exception { final SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setCertAlias(certificateAlias); sslContextFactory.setNeedClientAuth(clientAuthentication); sslContextFactory.setKeyStoreType("BKS"); sslContextFactory.setKeyStorePath(keyStorePath); sslContextFactory.setKeyStorePassword(keyStorePassword); sslContextFactory.setKeyManagerPassword(keyManagerPassword); sslContextFactory.setTrustStoreType("BKS"); sslContextFactory.setTrustStorePath(trustStorePath); sslContextFactory.setTrustStorePassword(trustStorePassword); sslContextFactory.setExcludeCipherSuites( "SSL_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); sslContextFactory.setRenegotiationAllowed(false); return sslContextFactory; } }