/*
* Adito
*
* Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.adito.server.jetty;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mortbay.http.JsseListener;
import org.mortbay.util.InetAddrPort;
import com.adito.boot.ContextHolder;
import com.adito.boot.ContextKey;
import com.adito.boot.KeyStoreManager;
import com.adito.boot.PropertyList;
import com.adito.server.CustomKeyManager;
/**
* Extension to Jettys {@link org.mortbay.http.JsseListener} that loads
* its SSL keys from Aditos keystore using the password that has been
* configured in the property database.
*/
public class CustomJsseListener extends JsseListener {
private String keyPassword;
private TrustManager trustManager;
private boolean initialised;
private boolean require = false;
private boolean configureContext = true;
private static boolean createAvailableCipherSuitesList = true;
final static Log log = LogFactory.getLog(CustomJsseListener.class);
/**
* Constructor
*
* @param keyPassword key password
*/
public CustomJsseListener(String keyPassword) {
super();
init(keyPassword);
}
/**
* Constructor
*
* @param address address and port on which to listen
* @param keyPassword key password
*/
public CustomJsseListener(InetAddrPort address, String keyPassword) {
super(address);
init(keyPassword);
}
private void init(String keyPassword) {
this.keyPassword = keyPassword;
}
/**
* Set the trust manager to use. This must be done <b>before</b> the
* socket factory is initialised (i.e. when Jetty is started). If the
* method is not called the default trust managers will be used.
*
* @param trustManager trustManager
* @throws IllegalStateException if the Jetty has already been started
*
*/
public void setTrustManager(TrustManager trustManager, boolean require) {
if(initialised) {
throw new IllegalStateException("Socket factory already created. Cannot set trust manager.");
}
this.trustManager = trustManager;
this.require = require;
}
protected ServerSocket newServerSocket( InetAddrPort p_address,
int p_acceptQueueSize )
throws IOException
{
SSLServerSocket serverSocket = (SSLServerSocket)super.newServerSocket(p_address, p_acceptQueueSize);
if(serverSocket.getNeedClientAuth()) {
serverSocket.setNeedClientAuth(require);
setNeedClientAuth(require);
if(!require)
serverSocket.setWantClientAuth(true);
}
String[] ciphers = serverSocket.getSupportedCipherSuites();
String[] protocols = serverSocket.getSupportedProtocols();
if(log.isInfoEnabled()) {
log.info("The following protocols are supported:");
for(int i=0;i<protocols.length;i++) {
log.info(" " + protocols[i]);
}
}
if(createAvailableCipherSuitesList) {
File f = new File(ContextHolder.getContext().getTempDirectory(), "availableCipherSuites.txt");
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
if(log.isInfoEnabled())
log.info("The following cipher suites are supported:");
for(int i=0;i<ciphers.length;i++) {
if(log.isInfoEnabled())
log.info(" " + ciphers[i]);
writer.write(ciphers[i]);
writer.newLine();
}
} catch (Throwable e) {
log.error("Could not create cipher list!", e);
configureContext = false;
} finally {
if(writer!=null)
writer.close();
}
createAvailableCipherSuitesList = false;
}
if(configureContext) {
PropertyList list = ContextHolder.getContext().getConfig().retrievePropertyList(new ContextKey("ssl.supportedProtocols"));
if(!list.isEmpty()) {
serverSocket.setEnabledProtocols(list.asArray());
}
list = ContextHolder.getContext().getConfig().retrievePropertyList(new ContextKey("ssl.supportedCiphers"));
if(!list.isEmpty()) {
serverSocket.setEnabledCipherSuites(list.asArray());
}
}
protocols = serverSocket.getEnabledProtocols();
if(log.isInfoEnabled()) {
log.info("The following protocols are enabled:");
for(int i=0;i<protocols.length;i++) {
log.info(" " + protocols[i]);
}
}
ciphers = serverSocket.getEnabledCipherSuites();
if(log.isInfoEnabled()) {
log.info("The following cipher suites are enabled:");
for(int i=0;i<ciphers.length;i++) {
log.info(" " + ciphers[i]);
}
}
return serverSocket;
}
protected SSLServerSocketFactory createFactory() throws Exception {
if(KeyStoreManager.getInstance(KeyStoreManager.DEFAULT_KEY_STORE).isKeyStoreEmpty()) {
throw new Exception("The keystore does not contain any certificates. Please run the installation wizard (--install).");
}
KeyStore ks = KeyStoreManager.getInstance(KeyStoreManager.DEFAULT_KEY_STORE).getKeyStore();
String pw = ContextHolder.getContext().getConfig().retrieveProperty(new ContextKey("webServer.keystore.sslCertificate.password"));
KeyManager[] kma = new KeyManager[] { new CustomKeyManager(pw) };
TrustManager[] tma = null;
if(trustManager == null) {
TrustManagerFactory tm = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tm.init(ks);
tma = tm.getTrustManagers();
}
else {
// LDP - Add the existing trust managers so that outgoing certificates are still trusted.
TrustManagerFactory tm = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tm.init(ks);
tma = new TrustManager[tm.getTrustManagers().length + 1];
for(int i=0;i<tm.getTrustManagers().length-1;i++) {
tma[i] = tm.getTrustManagers()[i];
}
tma[tma.length-1] = trustManager;
}
SSLContext sslc = SSLContext.getInstance("SSL");
sslc.init(kma, tma, SecureRandom.getInstance("SHA1PRNG"));
SSLServerSocketFactory ssfc = sslc.getServerSocketFactory();
if (log.isInfoEnabled())
log.info("SSLServerSocketFactory=" + ssfc);
initialised = true;
return ssfc;
}
public boolean isConfigureContext() {
return configureContext;
}
public void setConfigureContext(boolean configureContext) {
this.configureContext = configureContext;
}
}