/** * Copyright 2013 Tommi S.E. Laukkanen * * Licensed 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.bubblecloud.ilves.server.jetty; import org.bubblecloud.ilves.cache.UserClientCertificateCache; import org.bubblecloud.ilves.security.CertificateUtil; import org.bubblecloud.ilves.site.DefaultSiteUI; import org.bubblecloud.ilves.util.PropertiesUtil; import org.eclipse.jetty.server.*; import org.eclipse.jetty.util.ssl.SslContextFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.security.KeyStore; import java.security.cert.CRL; import java.security.cert.CertificateException; import java.util.Collection; /** * Utility class for creating embedded Jetty sites. * * @author Tommi S.E. Laukkanen */ public class JettyUtil { /** * Constructs Jetty server. * @param httpPort the HTTP port * @param httpsPort the HTTPS port * @param requestClientAuthentication true if client authentication is to be requested * @param requireClientAuthentication true if client authentication is required * @return the Jetty server * @throws IOException if SSL context factory creation fails. */ public static Server newServer( final int httpPort, final int httpsPort, final boolean requestClientAuthentication, final boolean requireClientAuthentication) throws IOException { UserClientCertificateCache.init(DefaultSiteUI.getEntityManagerFactory()); final String keyStorePath = PropertiesUtil.getProperty("site", "key-store-path"); final String keyStorePassword = PropertiesUtil.getProperty("site", "key-store-password"); final String certificateAlias = PropertiesUtil.getProperty("site", "server-certificate-entry-alias"); final String certificatePassword = PropertiesUtil.getProperty("site", "server-certificate-entry-password"); final String selfSignedCertificateHostName = PropertiesUtil.getProperty("site", "server-certificate-self-sign-host-name"); final String selfSignedCertificateIpAddress = PropertiesUtil.getProperty("site", "server-certificate-self-sign-ip-address"); final Server server = new Server(); final HttpConfiguration httpConfiguration = new HttpConfiguration(); httpConfiguration.setSecureScheme("https"); httpConfiguration.setSecurePort(httpsPort); httpConfiguration.setOutputBufferSize(32768); httpConfiguration.setRequestHeaderSize(8192); httpConfiguration.setResponseHeaderSize(8192); httpConfiguration.setSendServerVersion(false); httpConfiguration.setSendDateHeader(false); if (httpPort > 0) { final ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration)); httpConnector.setPort(httpPort); httpConnector.setIdleTimeout(30000); server.addConnector(httpConnector); } if (httpsPort > 0) { CertificateUtil.ensureServerCertificateExists( selfSignedCertificateHostName, selfSignedCertificateIpAddress, certificateAlias, certificatePassword, keyStorePath, keyStorePassword); final JettySiteSslContextFactory sslContextFactory = newSslSocketFactory(certificateAlias, keyStorePath, keyStorePassword, certificatePassword, requestClientAuthentication, requireClientAuthentication); final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration); httpsConfiguration.addCustomizer(new SecureRequestCustomizer()); final ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfiguration)); httpsConnector.setPort(httpsPort); httpsConnector.setIdleTimeout(30000); server.addConnector(httpsConnector); } return server; } /** * Constructs SSL context factory. * @param certificateAlias the certificate alias * @param keyStorePath the key store path * @param keyStorePassword the key store password * @param certificatePassword the certificate password * @param requestClientAuthentication true if client authentication is to be requested * @param requireClientAuthentication true if client authentication is required * @return the constructed SSL context factory * @throws Exception if exception occurs in construction */ private static JettySiteSslContextFactory newSslSocketFactory(final String certificateAlias, final String keyStorePath, final String keyStorePassword, final String certificatePassword, final boolean requestClientAuthentication, final boolean requireClientAuthentication) throws IOException { final JettySiteSslContextFactory sslContextFactory = new JettySiteSslContextFactory(); sslContextFactory.setCertAlias(certificateAlias); sslContextFactory.setNeedClientAuth(requireClientAuthentication); sslContextFactory.setWantClientAuth(requestClientAuthentication); sslContextFactory.setKeyStoreType("BKS"); sslContextFactory.setKeyStorePath(keyStorePath); sslContextFactory.setKeyStorePassword(keyStorePassword); sslContextFactory.setKeyManagerPassword(certificatePassword); 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; } /** * Jetty Site SSL context factory. * * @author Tommi S.E. Laukkanen */ public static class JettySiteSslContextFactory extends SslContextFactory { @Override protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); trustManagerFactory.init(trustStore); final TrustManager trustManager = new X509TrustManager() { @Override public void checkClientTrusted( java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException { if (x509Certificates.length != 1) { throw new CertificateException("Certificate paths not supported."); } if (UserClientCertificateCache.getUserByCertificate(x509Certificates[0], true) == null) { throw new CertificateException("Unknown certificate."); } } @Override public void checkServerTrusted( java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException { throw new CertificateException("Unsupported operation."); } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } }; return new TrustManager[] {trustManager}; } } }