/**
* Copyright (c) 2000/2001 Thomas Kopp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
// $Id: SSLSocketClientFactory.java,v 1.8 2004/11/04 16:20:05 ylafon Exp $
package org.w3c.jigsaw.https.socket;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
/* import java.lang.reflect.InvocationHandler; */
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/* import java.lang.reflect.Proxy; */
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidParameterSpecException;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLKeyException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import org.w3c.jigsaw.http.httpd;
import org.w3c.jigsaw.http.socket.SocketClient;
import org.w3c.jigsaw.http.socket.SocketClientFactory;
import org.w3c.jigsaw.http.socket.SocketClientState;
import org.w3c.jigsaw.https.SSLAdapter;
import org.w3c.util.ObservableProperties;
/**
* @author Thomas Kopp, Dialogika GmbH
* @version 1.1, 27 December 2000, 6 February 2004
*
* This class extends a Jigsaw SocketClientFactory designed for the
* http protocol
* in order to supply a SocketClientFactory for the https protocol
* in accordance with the JSSE API.
*
* Three legal tricks are applied for working around if required:
* Proxy classes are used for addressing multiple inheritance and
* non-official api.
* Non-static access via introspection provides for addressing static api.
* The java.lang.Object type is used for mapping non-official types.
*/
public class SSLSocketClientFactory extends SocketClientFactory {
/**
* The used api-part of javax.net.ssl.KeyManagerFactory.
* A static interface KeyManagerFactory can be used under a
* real proxy approach.
*/
private static final class KeyManagerFactory extends Delegator {
/**
* Creates the specified pseudo-proxy front end.
*
* @param target the target object in use
*/
private KeyManagerFactory(Object target) {
super(target);
}
/**
* Supplies the default factory algorithm.
*
* @return the default algorithm
*/
public /* static */ String getDefaultAlgorithm() {
try {
return (String)invoke("getDefaultAlgorithm", null, null);
}
catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Generates a key manager factory.
*
* @param algorithm the name of the factory algorithm
* @return a key manager factory instance
* @throws NoSuchAlgorithmException if the factory algorithm is
* unavailable
*/
public /* static */ Object getInstance(String algorithm)
throws NoSuchAlgorithmException {
try {
return invoke("getInstance",
new Class[] {String.class},
new Object[] {algorithm});
}
catch (NoSuchAlgorithmException ex) {
throw ex;
}
catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Initializes this factory.
*
* @param ks the underlying keystore
* @param password the key access password in use
* @throws KeyStoreException if initialization fails
* @throws NoSuchAlgorithmException if the specified algorithm
* is unavailable
* @throws UnrecoverableKeyException if the key in question
* cannot be recovered
*/
public void init(KeyStore ks, char[] password)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException
{
try {
invoke("init",
new Class[] {KeyStore.class, char[].class},
new Object[] {ks, password});
} catch (KeyStoreException ex) {
throw ex;
} catch (NoSuchAlgorithmException ex) {
throw ex;
} catch (UnrecoverableKeyException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Alternatively initializes this factory.
*
* @param parameters the manager factory parameters
* (unavailable prior to JDK 1.4)
* @throws InvalidAlgorithmParameterException if initialization fails
*/
public void init(Object parameters)
throws InvalidAlgorithmParameterException
{
try {
invoke("init",
new Class[] {Object.class},
new Object[] {parameters});
} catch (InvalidAlgorithmParameterException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Supplies the available key managers of this factory.
*
* @return the available key managers
*/
public Object getKeyManagers() {
try {
return invoke("getKeyManagers", null, null);
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
}
/**
* The used api-part of javax.net.ssl.TrustManagerFactory.
* A static interface TrustManagerFactory can be used under a
* real proxy approach.
*/
private static final class TrustManagerFactory extends Delegator {
/**
* Creates the specified pseudo-proxy front end.
*
* @param target the target object in use
*/
private TrustManagerFactory(Object target) {
super(target);
}
/**
* Supplies the default factory algorithm.
*
* @return the default algorithm
*/
public /* static */ String getDefaultAlgorithm() {
try {
return (String)invoke("getDefaultAlgorithm", null, null);
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Generates a trust manager factory.
*
* @param algorithm the name of the factory algorithm
* @return a trust manager factory instance
* @throws NoSuchAlgorithmException if the factory algorithm
* is unavailable
*/
public /* static */ Object getInstance(String algorithm)
throws NoSuchAlgorithmException
{
try {
return invoke("getInstance",
new Class[] {String.class},
new Object[] {algorithm});
} catch (NoSuchAlgorithmException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Initializes this factory.
*
* @param ks the underlying keystore
* @throws KeyStoreException if initialization fails
*/
public void init(KeyStore ks)
throws KeyStoreException
{
try {
invoke("init",
new Class[] {KeyStore.class},
new Object[] {ks});
} catch (KeyStoreException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Alternatively initializes this factory.
*
* @param parameters the manager factory parameters
* (unavailable prior to JDK 1.4)
* @throws InvalidAlgorithmParameterException if initialization fails
*/
public void init(Object parameters)
throws InvalidAlgorithmParameterException
{
try {
invoke("init",
new Class[] {Object.class},
new Object[] {parameters});
} catch (InvalidAlgorithmParameterException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Supplies the available trust managers of this factory.
*
* @return the available truat managers
*/
public Object getTrustManagers() {
try {
return invoke("getTrustManagers", null, null);
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
}
/**
* The used api-part of javxx.net.ssl.SSLContext.
* A static interface SSLContext can be used under a real proxy approach.
*/
private static final class SSLContext extends Delegator {
/**
* Creates the specified pseudo-proxy front end.
*
* @param target the target object in use
*/
private SSLContext(Object target) {
super(target);
}
/**
* Generates an ssl context, which implements the specified
* secure socket protocol.
*
* @param protcol the name of protocol implementation
* @return an ssl context instance
* @throws NoSuchAlgorithmException if the specified implementation
* is not available
*/
public /* static */ Object getInstance(String protocol)
throws NoSuchAlgorithmException
{
try {
return invoke("getInstance",
new Class[] {String.class},
new Object[] {protocol});
} catch (NoSuchAlgorithmException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Initializes this context.
*
* @param km the key manager array used
* @param tm the trust manager array used
* @param random the secure random used for initializing seed
* @throws KeyManagementException if initialization fails
*/
public void init(Object km, Object tm, SecureRandom random)
throws KeyManagementException
{
try {
invoke("init",
new Class[] {Object.class, Object.class,
SecureRandom.class},
new Object[] {km, tm, random});
} catch (KeyManagementException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Supplies an ssl server socket factory.
*
* @return a server socket factory instance.
*/
public SSLServerSocketFactory getServerSocketFactory() {
try {
return (SSLServerSocketFactory)invoke("getServerSocketFactory",
null, null);
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
}
/**
* The generic manager factory paremeters bridge.
* A static interface ManagerFactoryParametersFactory can be used under
* a real proxy approach.
*/
private static final class ManagerFactoryParametersFactory
extends Delegator
{
/**
* Creates the specified pseudo-proxy front end.
*
* @param target the target object in use
*/
private ManagerFactoryParametersFactory(Object target) {
super(target);
}
/**
* Generates a manager factory parameters instance.
*
* @param path the generic path argument for a parameters instance
* @param password the password for a parameters instance
* @return a manager factory parameters instance
* @throws InvalidAlgorithmParameterException if the specified
* arguments are not suitable
* @throws NoSuchMethodException if the specified method
* is not available
*/
public /* static */ Object getInstance(String path, String password)
throws InvalidAlgorithmParameterException, NoSuchMethodException
{
try {
return invoke("getInstance",
new Class[] {String.class, String.class},
new Object[] {path, password});
} catch (InvalidAlgorithmParameterException ex) {
throw ex;
} catch (NoSuchMethodException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Generates a manager factory parameters instance.
*
* @param path the generic path argument for a parameters instance
* @return a manager factory parameters instance
* @throws InvalidAlgorithmParameterException if the specified
* arguments are not suitable
* @throws NoSuchMethodException if the specified method is not
* available
*/
public /* static */ Object getInstance(String path)
throws InvalidAlgorithmParameterException, NoSuchMethodException
{
try {
return invoke("getInstance",
new Class[] {String.class},
new Object[] {path});
} catch (InvalidAlgorithmParameterException ex) {
throw ex;
} catch (NoSuchMethodException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* Generates a manager factory parameters instance.
*
* @return a manager factory parameters instance
* @throws InvalidAlgorithmParameterException if the specified
* arguments are not suitable
* @throws NoSuchMethodException if the specified method is not
* available
*/
public /* static */ Object getInstance()
throws InvalidAlgorithmParameterException, NoSuchMethodException
{
try {
return invoke("getInstance", null, null);
} catch (InvalidAlgorithmParameterException ex) {
throw ex;
} catch (NoSuchMethodException ex) {
throw ex;
} catch (Exception ex) {
RuntimeException rex = new RuntimeException(ex.toString());
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
}
/**
* The standard delegation pattern used as a quasi-proxy implementation.
* Unfortunately, a real proxy requires at least JDK 1.3.
*/
private static class Delegator /* implements InvocationHandler */ {
/**
* The facade type in use
*/
private final Class facade;
/**
* The delegation target type
*/
private final Class type;
/**
* The delegation target object
*/
private final Object peer;
/**
* Constructs a delegator instance.
*
* @param target the target object in use
* @param source the source interface in use
*/
public Delegator(Object target, Class source) {
if (target instanceof Class) {
// static delegation
type = (Class)target;
peer = null;
} else {
// object delegation
type = target.getClass();
peer = target;
}
facade = source;
}
/**
* Constructs a delegator instance.
*
* @param target the target object in use
*/
public Delegator(Object target) {
this(target, null);
}
/**
* Delegates the specified method.
*
* @param name the accessed method name
* @param deftypes the method type
* @param args the method arguments
* @return the method invocation result
* @throws Throwable if the invocation fails
*/
/*
public Object invoke(String method, Class[] types, Object[] args)
throws Throwable {
String name = method.getName();
Class[] deftypes = method.getParameterTypes();
...
*/
public Object invoke(String name, Class[] deftypes, Object[] args)
throws Exception
{
int count = ((args != null) ? args.length : 0);
Class[] types = new Class[count];
Object[] param = new Object[count];
for (int i = 0; i < count; i++) {
Object arg = args[i];
// a delegator proxy being aware of its own, hence
// supplying its target object and interface
// type if applicable
Class art = null;
if (null != arg) {
art = arg.getClass();
/*
if (Proxy.isProxyClass(art)) {
InvocationHandler handler=Proxy.getInvocationHandler(arg);
if (Delegator.class == handler.getClass()) {
// supply the underlying peer instance
arg = ((Delegator)handler).peer;
// supply the wrapped interface type
Class[] cls = art.getInterfaces();
if ((null != cls)&&(cls.length == 1)) {
art = cls[0];
}
}
}
*/
if (arg instanceof Delegator) {
Delegator ref = (Delegator)arg;
arg = ref.peer;
if (null != ref.facade) {
art = ref.facade;
}
}
}
// runtime types overwrite interface types
types[i] = ((null != art) ? art :
((null != deftypes)&&(i < deftypes.length) ?
deftypes[i] : Object.class));
param[i] = arg;
}
Method m = type.getMethod(name, types);
return m.invoke(peer, param);
}
/**
* Supplies a proxy for this delegator.
*
* @param source the interface in use
* @return an delegation proxy instance
*/
/*
public Object getProxy(Class source) {
return Proxy.newProxyInstance(source.getClassLoader(),
new Class[] { source }, this);
}
*/
}
/**
* The client authentication support level indicator
* (for JSSE backward compatibility),
* which also indicates the api level in question.
*/
private static final boolean supportsOptionalClientAuth;
/**
* The implementation nmespace depending on the api level in question.
*/
private static final String implementationNamespace;
static {
boolean supported = false;
try {
Class c = javax.net.ssl.SSLServerSocket.class;
Class cp[] = { java.lang.Boolean.TYPE };
Method ic = null;
supported = (null != c.getMethod("setWantClientAuth", cp));
} catch (Exception ex) {
supported = false;
} finally {
supportsOptionalClientAuth = supported;
implementationNamespace = (supported ?
"javax.net.ssl." :
"com.sun.net.ssl.");
}
}
/**
* The property key for the system protocol package lookup
*/
public static final String PROTOCOL_HANDLER_S="java.protocol.handler.pkgs";
/**
* static flag for enabling debug output if applicable
*/
private static boolean debug = false;
/**
* The context used for creating a server socket factory
*/
private SSLContext context = null;
/**
* The daemon of this factory
*/
private httpd daemon = null;
/**
* The daemon bind address for this factory
*/
private InetAddress bindAddr = null;
/**
* The daemon bind address for this factory
*/
private int maxClients = 0;
/**
* factory method for creating a secure server socket
* @return a new server socket instance
* @throws java.io.IOException due to socket creation problems
*/
public ServerSocket createServerSocket()
throws IOException
{
int port = daemon.getPort();
int clients = Math.max(128, maxClients);
ServerSocket serversocket = null;
if (bindAddr == null) {
serversocket = getFactory().createServerSocket(port, clients);
} else {
serversocket = getFactory().createServerSocket(port, clients,
bindAddr);
}
// tk, 1 February 2004,
// added optional client authentication,
// which is forced, if a truststore is configured and
// the org.w3c.jigsaw.ssl.authenticate is not set to false
if (serversocket instanceof SSLServerSocket) {
ObservableProperties props = daemon.getProperties();
// decide client authentication based on trust configuration
boolean mandatory;
mandatory = props.getBoolean(SSLProperties.MUST_AUTHENTICATE_P,
false);
boolean generic;
generic = props.getBoolean(SSLProperties.TRUSTSTORE_GENERIC_P,
false);
String trust;
trust = props.getString(SSLProperties.TRUSTSTORE_PATH_P,
null);
boolean authenticate = mandatory||generic||
((null != trust)&&(trust.length() > 0));
if (authenticate) {
SSLServerSocket sslsocket = (SSLServerSocket)serversocket;
if (mandatory) {
sslsocket.setNeedClientAuth(true);
} else {
if (supportsOptionalClientAuth) {
sslsocket.setWantClientAuth(true);
} else {
throw new SSLProtocolException("Optional client "+
"authentication not supported by the"+
" current api level. Consider upgrading"+
" your api or using obligatory client"+
" authentication or using server "+
"authentication only");
}
}
}
}
return serversocket;
}
/**
* Adds a security provider.
*
* @param provider the provider class name in question
* @throws java.lang.ClassNotFoundException if the provider is unavailable
* @throws java.lang.IllegalAccessException if the provider has no
* accessible default constructor
* @throws java.lang.InstantiationException if the provider cannot be
* instantiated
*/
private static final void addProvider(String provider)
throws ClassNotFoundException, IllegalAccessException,
InstantiationException
{
if (null != provider) {
if (null == Security.getProvider(provider)) {
Class support = Class.forName(provider);
Provider supplier = (Provider)support.newInstance();
Security.addProvider(supplier);
if (debug) {
System.out.println("Added new security provider: " +
supplier.getInfo() + ".");
}
}
}
}
/**
* Sets the protocol handler.
*
* @param handler the handler class name in question
*/
private static final void setHandler(String handler) {
if (null != handler) {
System.setProperty(PROTOCOL_HANDLER_S, handler);
if (debug) {
System.out.println("Set new protocol handler: "+handler+".");
}
}
}
/**
* Loads a key store for read access.
*
* @param props the underlying property set
* @param typekey the store type property key
* @param pathkey the keystore path property key
* @param passkey the password property key
* @return the loaded keystore
* @throws KeyStoreException if initialization fails
* @throws java.io.IOException if keystore cannot be loaded
* @throws NoSuchAlgorithmException if the integrity check is inavailable
* @throws CertificateException if the a certificate is inaccessible
*/
private static final KeyStore getStore(ObservableProperties props,
String typekey, String pathkey,
String passkey)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException
{
String storepath = props.getString(pathkey, null);
if (null != storepath) {
if ("".equals(storepath.trim())) storepath = null;
}
String storepass = props.getString(passkey, null);
if ((null != storepath)||(null != storepass)) {
String storetype = props.getString(typekey,
KeyStore.getDefaultType());
KeyStore store = KeyStore.getInstance(storetype);
store.load((null != storepath) ?
new BufferedInputStream(new FileInputStream(storepath)):
null,
(null != storepass) ? storepass.toCharArray() : new char[0]);
return store;
} else {
return null;
}
}
/**
* Instantiates a manager factory parameters object.
*
* @param props the underlying property set
* @param typekey the store type property key
* @param pathkey the keystore path property key
* @param passkey the password property key
* @return the specified manager factory parameters instance
* @throws InvalidParameterSpecException if api support is not sufficient
* @throws InvalidAlgorithmParameterException if generic initialization
* fails
* @throws ClassNotFoundException if the provider is unavailable
*/
private static final Object getParams(ObservableProperties props,
String typekey, String pathkey,
String passkey)
throws InvalidParameterSpecException,
InvalidAlgorithmParameterException,
ClassNotFoundException
{
if (supportsOptionalClientAuth) {
String paratype = props.getString(typekey, null);
if ((null != paratype)&&(paratype.length() > 0)) {
Class parameterFactory = Class.forName(paratype);
String path = props.getString(pathkey, null);
String pass = props.getString(passkey, null);
ManagerFactoryParametersFactory mfpboot;
mfpboot =new ManagerFactoryParametersFactory(parameterFactory);
Object mfpload = null;
try {
mfpload = mfpboot.getInstance(path, pass);
} catch (NoSuchMethodException ex) {
try {
mfpload = mfpboot.getInstance(path);
} catch (NoSuchMethodException sub) {
try {
mfpload = mfpboot.getInstance();
} catch (NoSuchMethodException next) {
throw new InvalidAlgorithmParameterException(
"Factory specified by type property has no "+
"suitable instantiation method");
}
}
}
Class managerFactoryParameters =
Class.forName(implementationNamespace +
"ManagerFactoryParameters");
if (managerFactoryParameters.isInstance(mfpload)) {
return new Delegator(mfpload, managerFactoryParameters);
} else {
throw new InvalidAlgorithmParameterException(
"Factory specified by type property does not "+
"supply manager factory parameters");
}
} else {
throw new InvalidAlgorithmParameterException("No manager "+
"factory parameter class specified as the type property");
}
} else {
throw new InvalidParameterSpecException("Generic manager "+
"factory parameters not supported by the current api level. " +
"Consider upgrading your api or using a classic keystore");
}
}
/**
* Creates an ssl context.
*
* @param props the underlying property set
* @return the ssl context used for the socket factory
* @throws ClassNotFoundException if the provider is unavailable
* @throws KeyStoreException if keystore initialization fails
* @throws IOException if keystore cannot be loaded
* @throws NoSuchAlgorithmException if the integrity check is unavailable
* @throws InvalidParameterSpecException if api support is not sufficient
* @throws InvalidAlgorithmParameterException if generic initialization
* fails
* @throws CertificateException if the a certificate is inaccessible
* @throws UnrecoverableKeyException if the key in question cannot
* be recovered
* @throws InstantiationException if the specified factory is abstract
* @throws IllegalAccessException if factory constructor is unreachable
* @throws InvocationTargetException if factory initialization fails
* @throws KeyManagementException if initialization fails
*/
private static final SSLContext createContext(ObservableProperties props)
throws ClassNotFoundException, KeyStoreException, IOException,
NoSuchAlgorithmException, InvalidParameterSpecException,
InvalidAlgorithmParameterException, CertificateException,
UnrecoverableKeyException, InstantiationException,
IllegalAccessException, InvocationTargetException,
KeyManagementException
{
// switch according to api-level
Class keyManagerFactory = Class.forName(implementationNamespace +
"KeyManagerFactory");
Class trustManagerFactory = Class.forName(implementationNamespace +
"TrustManagerFactory");
Class sslContext = Class.forName(implementationNamespace +
"SSLContext");
// the ugly but legal key manager factory bootstrap
KeyManagerFactory kmfboot = new KeyManagerFactory(keyManagerFactory);
String kmftype = props.getString(SSLProperties.KEYMANAGER_TYPE_P,null);
Object kmfload = kmfboot.getInstance((null != kmftype) ?
kmftype :
kmfboot.getDefaultAlgorithm());
KeyManagerFactory kmf = new KeyManagerFactory(kmfload);
boolean kgen = props.getBoolean(SSLProperties.KEYSTORE_GENERIC_P,
SSLProperties.DEFAULT_KEYSTORE_GENERIC);
if (kgen) {
// generic key material instantiation (not prior to before JDK 1.4)
Object kmfp = getParams(props,
SSLProperties.TRUSTSTORE_TYPE_P,
SSLProperties.TRUSTSTORE_PATH_P,
SSLProperties.TRUSTSTORE_PASSWORD_P);
kmf.init(kmfp);
} else {
KeyStore ks = getStore(props,
SSLProperties.KEYSTORE_TYPE_P,
SSLProperties.KEYSTORE_PATH_P,
SSLProperties.KEYSTORE_PASSWORD_P);
// reusing the store password for key access
String keypass = props.getString(SSLProperties.KEYSTORE_PASSWORD_P,
null);
kmf.init(ks, (null != keypass ? keypass.toCharArray() :
new char[0]));
}
// the ugly but legal trust manager factory bootstrap
TrustManagerFactory tmfboot;
tmfboot = new TrustManagerFactory(trustManagerFactory);
String tmftype = props.getString(SSLProperties.TRUSTMANAGER_TYPE_P,
null);
Object tmfload = tmfboot.getInstance((null != tmftype) ? tmftype :
tmfboot.getDefaultAlgorithm());
TrustManagerFactory tmf = new TrustManagerFactory(tmfload);
boolean tgen = props.getBoolean(SSLProperties.TRUSTSTORE_GENERIC_P,
SSLProperties.DEFAULT_TRUSTSTORE_GENERIC);
if (tgen) {
// generic trust material instantiation (not < JDK 1.4)
Object tmfp = getParams(props,
SSLProperties.TRUSTSTORE_TYPE_P,
SSLProperties.TRUSTSTORE_PATH_P,
SSLProperties.TRUSTSTORE_PASSWORD_P);
tmf.init(tmfp);
} else {
KeyStore ts = getStore(props,
SSLProperties.TRUSTSTORE_TYPE_P,
SSLProperties.TRUSTSTORE_PATH_P,
SSLProperties.TRUSTSTORE_PASSWORD_P);
tmf.init(ts);
}
// accessing the protocol type
String protocol = props.getString(SSLProperties.PROTOCOL_NAME_P,
SSLProperties.DEFAULT_PROTOCOL_NAME);
// the ugly but legal ssl context bootstrap
SSLContext ctxboot = new SSLContext(sslContext);
Object ctxload = ctxboot.getInstance(protocol);
SSLContext context = new SSLContext(ctxload);
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
new SecureRandom());
return context;
}
/**
* method for intializing this factory
* @param server the daemon of this factory
*/
public void initialize(httpd server) {
super.initialize(server);
daemon = server;
daemon.registerPropertySet(new SSLProperties(daemon));
ObservableProperties props = daemon.getProperties();
try {
// Providers or protocols are switched by default
// in a compatible way as postulated by the JDK 1.4 policies
String provider;
provider = props.getString(SSLProperties.SECURITY_PROVIDER_P,
(supportsOptionalClientAuth ? null :
SSLProperties.DEFAULT_SECURITY_PROVIDER));
addProvider(provider);
String handler = props.getString(SSLProperties.PROTOCOL_HANDLER_P,
(supportsOptionalClientAuth ? null :
SSLProperties.DEFAULT_PROTOCOL_HANDLER));
setHandler(handler);
context = createContext(props);
String bindAddrName = props.getString(BINDADDR_P, null);
if (bindAddrName != null) {
try {
bindAddr = InetAddress.getByName(bindAddrName);
} catch (Exception ex) {
bindAddr = null;
}
} else {
bindAddr = null;
}
maxClients = props.getInteger(MAXCLIENTS_P, MAXCLIENTS);
} catch (Exception ex) {
String mes;
mes = "Unable to initialize secure socket provider";
daemon.fatal(ex, mes);
if (debug) {
System.err.println(mes);
ex.printStackTrace();
}
RuntimeException rex;
rex = new RuntimeException(mes);
SSLAdapter.fillInStackTrace(rex, ex);
throw rex;
}
}
/**
* method for handling a dynamic property modification
* @param name the name of the property modified
* @return true if and only if the modification has been handled
* successfully
*/
public boolean propertyChanged(String name) {
if (super.propertyChanged(name)) {
ObservableProperties props = daemon.getProperties();
try {
if (name.equals(MAXCLIENTS_P)) {
int newmax = props.getInteger(MAXCLIENTS_P, -1);
if (newmax > maxClients) {
for (int i = maxClients-newmax; --i >= 0; ) {
addClient(true);
}
} else if (newmax > 0) {
maxClients = newmax;
}
return true;
} else if (name.equals(BINDADDR_P)) {
bindAddr = InetAddress.getByName(
props.getString(BINDADDR_P, null));
return true;
} else if ((name.equals(SSLProperties.KEYSTORE_GENERIC_P)) ||
(name.equals(SSLProperties.KEYSTORE_PATH_P)) ||
(name.equals(SSLProperties.KEYSTORE_TYPE_P)) ||
(name.equals(SSLProperties.KEYSTORE_PASSWORD_P)) ||
(name.equals(SSLProperties.TRUSTSTORE_GENERIC_P)) ||
(name.equals(SSLProperties.TRUSTSTORE_PATH_P)) ||
(name.equals(SSLProperties.TRUSTSTORE_TYPE_P)) ||
(name.equals(SSLProperties.TRUSTSTORE_PASSWORD_P))||
(name.equals(SSLProperties.PROTOCOL_NAME_P))) {
context = createContext(props);
return true;
} else {
return true;
}
} catch (Exception ex) {
String mes;
mes = "Unable to re-initialize secure socket provider";
daemon.fatal(ex, mes);
if (debug) {
System.err.println(mes);
ex.printStackTrace();
}
// RuntimeException rex;
// rex = new RuntimeException(sub);
// SSLAdapter.fillInStackTrace(rex, cause);
// throw rex;
return false;
}
} else {
return false;
}
}
/**
* server sockt factory creation
* @return the secure server socket factory
* @throws java.io.IOException due to factory creation problems
*/
private ServerSocketFactory getFactory()
throws SSLKeyException
{
ServerSocketFactory factory;
factory = ((null != context) ?
context.getServerSocketFactory() :
// make best effort to obtain a factory
SSLServerSocketFactory.getDefault());
String[] supported =
((SSLServerSocketFactory)factory).getSupportedCipherSuites();
if (debug) {
System.out.println("Supported suites:");
for (int i = 0; i < supported.length; i++) {
System.out.println(" " + supported[i]);
}
String[] enabled =
((SSLServerSocketFactory)factory).getDefaultCipherSuites();
System.out.println("Enabled suites:");
for (int i = 0; i < enabled.length; i++) {
System.out.println(" " + enabled[i]);
}
}
if (supported.length < 1) {
SSLKeyException ex = new SSLKeyException(
"No cipher suites supported by this "
+ "SSL socket factory. "
+ "Please check your factory, key store, "
+ "store password and cerificates");
daemon.fatal(ex, ex.getMessage());
if (debug) {
ex.printStackTrace();
}
throw ex;
}
return factory;
}
/**
* Factory for creating a new client for this pool.
* @param server the target http daemon
* @param state the client state holder
* @return a new socket client
*/
protected SocketClient createClient(httpd server,
SocketClientState state) {
return new SSLSocketClient(server, this, state);
}
}