You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by do...@apache.org on 2002/09/22 07:46:20 UTC
cvs commit: jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets AbstractTLSSocketFactory.java SSLFactoryBuilder.java TLSServerSocketFactory.java TLSSocketFactory.java
donaldp 2002/09/21 22:46:20
Modified: src/java/org/apache/avalon/cornerstone/blocks/sockets
TLSServerSocketFactory.java TLSSocketFactory.java
Added: src/java/org/apache/avalon/cornerstone/blocks/sockets
AbstractTLSSocketFactory.java
SSLFactoryBuilder.java
Log:
* better javadocs
* previously duplicate code moved into common classes
* weak/anonymous ssl ciphers are no longer enabled, it was a dangerous
thing to do
* client socket factory boasts server identity verification feature
(weak and looks wrong though, I don't know if/where Sun has
implementation of complete SSL restrictions checking)
* client socket factory no longer has an option to authenticate-clients,
it didn't make sense to anyone on the list
* configuration format changed to include all SSLSocketContext related
items as a separate element, backward compatibility provided
* keystores can be loaded from any location on the filesystem to allow
placing keystores outside .sars, javadocs describes why it makes sense
Submitted by: Greg Steuck <gr...@nest.cx>
Revision Changes Path
1.13 +30 -113 jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/TLSServerSocketFactory.java
Index: TLSServerSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/TLSServerSocketFactory.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- TLSServerSocketFactory.java 17 Aug 2002 02:24:25 -0000 1.12
+++ TLSServerSocketFactory.java 22 Sep 2002 05:46:19 -0000 1.13
@@ -7,30 +7,32 @@
*/
package org.apache.avalon.cornerstone.blocks.sockets;
-import com.sun.net.ssl.KeyManagerFactory;
-import com.sun.net.ssl.SSLContext;
-import com.sun.net.ssl.TrustManagerFactory;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
-import java.security.KeyStore;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
-import javax.security.cert.X509Certificate;
import org.apache.avalon.cornerstone.services.sockets.ServerSocketFactory;
-import org.apache.avalon.framework.activity.Initializable;
-import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.context.Context;
-import org.apache.avalon.framework.context.Contextualizable;
-import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.phoenix.BlockContext;
/**
- * Factory implementation for TLS TCP sockets.
+ * Manufactures TLS server sockets. Configuration element inside a
+ * SocketManager would look like:
+ * <pre>
+ * <factory name="secure"
+ * class="org.apache.avalon.cornerstone.blocks.sockets.TLSServerSocketFactory" >
+ * <ssl-factory /> <!-- see {@link SSLFactoryBuilder} -->
+ * <timeout> 0 </timeout>
+ * <!-- With this option set to a non-zero timeout, a call to
+ * accept() for this ServerSocket will block for only this amount of
+ * time. If the timeout expires, a java.io.InterruptedIOException is
+ * raised, though the ServerSocket is still valid. Default value is 0. -->
+ * <authenticate-client>false</authenticate-client>
+ * <!-- Whether or not the client must present a certificate to
+ * confirm its identity. Defaults to false. -->
+ * </factory>
+ * </pre>
*
* @author <a href="mailto:peter at apache.org">Peter Donald</a>
* @author <a href="mailto:fede@apache.org">Federico Barbieri</a>
@@ -38,41 +40,18 @@
* @author <a href="mailto:">Harish Prabandham</a>
* @author <a href="mailto:">Costin Manolache</a>
* @author <a href="mailto:">Craig McClanahan</a>
- * @author <a href="mailto:myfam@surfeu.fi">Andrei Ivanov</a>
+ * @author <a href="mailto:myfam@surfeu.fi">Andrei Ivanov</a>
+ * @author <a href="mailto:greg-avalon-apps at nest.cx">Greg Steuck</a>
*/
public class TLSServerSocketFactory
- extends AbstractLogEnabled
- implements ServerSocketFactory, Contextualizable, Configurable, Initializable
+ extends AbstractTLSSocketFactory
+ implements ServerSocketFactory
{
- protected SSLServerSocketFactory m_factory;
- protected File m_baseDirectory;
-
- protected String m_keyStoreFile;
- protected String m_keyStorePassword;
- protected String m_keyPassword;
- protected String m_keyStoreType;
- protected String m_keyStoreProtocol;
- protected String m_keyStoreAlgorithm;
+ private SSLServerSocketFactory m_factory;
protected boolean m_keyStoreAuthenticateClients;
- public void contextualize( final Context context )
- {
- final BlockContext blockContext = (BlockContext)context;
- m_baseDirectory = blockContext.getBaseDirectory();
- }
-
/**
- * Configure factory. Sample config is
- *
- * <keystore>
- * <file>conf/keystore</file> <!-- location of keystore relative to .sar base directory -->
- * <password></password> <!-- Password for the Key Store file -->
- * <key-password></key-password> <!-- Optional private Key Password -->
- * <type>JKS</type> <!-- Type of the Key Store file -->
- * <protocol>TLS</protocol> <!-- SSL protocol to use -->
- * <algorithm>SunX509</algorithm> <!-- Certificate encoding algorithm -->
- * <authenticate-client>false</authenticate-client> <!-- Require client authentication? -->
- * <keystore>
+ * Configures the factory.
*
* @param configuration the Configuration
* @exception ConfigurationException if an error occurs
@@ -80,75 +59,14 @@
public void configure( final Configuration configuration )
throws ConfigurationException
{
- final Configuration keyStore = configuration.getChild( "keystore" );
- m_keyStoreFile = keyStore.getChild( "file" ).getValue( "conf/keystore" );
- m_keyStorePassword = keyStore.getChild( "password" ).getValue();
- m_keyPassword = keyStore.getChild( "key-password" ).getValue(null);
- m_keyStoreType = keyStore.getChild( "type" ).getValue( "JKS" );
- m_keyStoreProtocol = keyStore.getChild( "protocol" ).getValue( "TLS" );
- m_keyStoreAlgorithm = keyStore.getChild( "algorithm" ).getValue( "SunX509" );
+ super.configure( configuration );
m_keyStoreAuthenticateClients =
- keyStore.getChild( "authenticate-client" ).getValueAsBoolean( false );
- }
-
- public void initialize()
- throws Exception
- {
- final KeyStore keyStore = initKeyStore();
- initSSLFactory( keyStore );
+ configuration.getChild( "authenticate-client" ).getValueAsBoolean( false );
}
- protected KeyStore initKeyStore()
- throws Exception
+ protected void visitBuilder( SSLFactoryBuilder builder )
{
- try
- {
- final KeyStore keyStore = KeyStore.getInstance( m_keyStoreType );
- final File keyStoreFile = new File( m_baseDirectory, m_keyStoreFile );
- final FileInputStream input = new FileInputStream( keyStoreFile );
-
- keyStore.load( input, m_keyStorePassword.toCharArray() );
- getLogger().info( "Keystore loaded from: " + keyStoreFile );
-
- return keyStore;
- }
- catch( final Exception e )
- {
- getLogger().error( "Exception loading keystore from: " + m_keyStoreFile, e );
- throw e;
- }
- }
-
- protected void initSSLFactory( final KeyStore keyStore )
- throws Exception
- {
-
- java.security.Security.addProvider( new sun.security.provider.Sun() );
- java.security.Security.addProvider( new com.sun.net.ssl.internal.ssl.Provider() );
-
- // set up key manager to do server authentication
- final SSLContext sslContext = SSLContext.getInstance( m_keyStoreProtocol );
- final KeyManagerFactory keyManagerFactory =
- KeyManagerFactory.getInstance( m_keyStoreAlgorithm );
-
- if ( null == m_keyPassword )
- {
- keyManagerFactory.init( keyStore, m_keyStorePassword.toCharArray() );
- } else
- {
- keyManagerFactory.init( keyStore, m_keyPassword.toCharArray() );
- }
-
-
- final TrustManagerFactory tmf = TrustManagerFactory.getInstance( m_keyStoreAlgorithm );
- tmf.init( keyStore );
-
- sslContext.init( keyManagerFactory.getKeyManagers(),
- tmf.getTrustManagers(),
- new java.security.SecureRandom() );
-
- // Create socket factory
- m_factory = sslContext.getServerSocketFactory();
+ m_factory = builder.buildServerSocketFactory();
}
/**
@@ -202,15 +120,14 @@
}
protected void initServerSocket( final ServerSocket serverSocket )
+ throws IOException
{
- final SSLServerSocket socket = (SSLServerSocket)serverSocket;
-
- // Enable all available cipher suites when the socket is connected
- final String[] cipherSuites = socket.getSupportedCipherSuites();
- socket.setEnabledCipherSuites( cipherSuites );
+ final SSLServerSocket socket = (SSLServerSocket) serverSocket;
// Set client authentication if necessary
socket.setNeedClientAuth( m_keyStoreAuthenticateClients );
+ // Sets socket timeout
+ socket.setSoTimeout( m_socketTimeOut );
}
}
1.6 +150 -173 jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/TLSSocketFactory.java
Index: TLSSocketFactory.java
===================================================================
RCS file: /home/cvs/jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/TLSSocketFactory.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- TLSSocketFactory.java 17 Aug 2002 02:24:25 -0000 1.5
+++ TLSSocketFactory.java 22 Sep 2002 05:46:19 -0000 1.6
@@ -7,31 +7,52 @@
*/
package org.apache.avalon.cornerstone.blocks.sockets;
-import com.sun.net.ssl.KeyManagerFactory;
-import com.sun.net.ssl.SSLContext;
-import com.sun.net.ssl.TrustManagerFactory;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.KeyStore;
+import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import javax.security.cert.X509Certificate;
import org.apache.avalon.cornerstone.services.sockets.SocketFactory;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
-import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.phoenix.BlockContext;
/**
- * Factory implementation for client TLS TCP sockets.
+ * Manufactures TLS client sockets. Configuration element inside a
+ * SocketManager would look like:
+ * <pre>
+ * <factory name="secure"
+ * class="org.apache.avalon.cornerstone.blocks.sockets.TLSSocketFactory" >
+ * <ssl-factory /> <!-- see {@link SSLFactoryBuilder} -->
+ * <timeout> 0 </timeout>
+ * <!-- if the value is greater than zero, a read() call on the
+ * InputStream associated with this Socket will block for only this
+ * amount of time in milliseconds. Default value is 0. -->
+ * <verify-server-identity>true|false</verify-server-identity>
+ * <!-- whether or not the server identity should be verified.
+ * Defaults to false. -->
+ * </factory>
+ * </pre>
+ * <p>
+ * Server identity verification currently includes only comparing the
+ * certificate Common Name received with the host name in the
+ * passed address. Identity verification requires that SSL
+ * handshake is completed for the socket, so it takes longer
+ * to get a verified socket (and won't play well with non-blocking
+ * application like SEDA).
+ * </p>
+ * <p>
+ * Another thing to keep in mind when using identity verification is
+ * that <tt>InetAddress</tt> objects for the remote hosts should be
+ * built using {@link java.net.InetAddress#getByName} with
+ * the host name (matching the certificate CN) as the
+ * argument. Failure to do so may cause relatively costly DNS lookups
+ * and false rejections caused by inconsistencies between forward and
+ * reverse resolution.
+ * </p>
*
* @author <a href="mailto:peter at apache.org">Peter Donald</a>
* @author <a href="mailto:fede@apache.org">Federico Barbieri</a>
@@ -40,41 +61,17 @@
* @author <a href="mailto:">Costin Manolache</a>
* @author <a href="mailto:">Craig McClanahan</a>
* @author <a href="mailto:myfam@surfeu.fi">Andrei Ivanov</a>
+ * @author <a href="mailto:greg-avalon-apps at nest.cx">Greg Steuck</a>
*/
public class TLSSocketFactory
- extends AbstractLogEnabled
+ extends AbstractTLSSocketFactory
implements SocketFactory, Contextualizable, Configurable, Initializable
{
private SSLSocketFactory m_factory;
-
- private File m_baseDirectory;
-
- private String m_keyStoreFile;
- private String m_keyStorePassword;
- private String m_keyPassword;
- private String m_keyStoreType;
- private String m_keyStoreProtocol;
- private String m_keyStoreAlgorithm;
- private boolean m_keyStoreAuthenticateClients;
-
- public void contextualize( final Context context )
- {
- final BlockContext blockContext = (BlockContext)context;
- m_baseDirectory = blockContext.getBaseDirectory();
- }
+ private boolean m_verifyServerIdentity;
/**
- * Configure factory. Sample config is
- *
- * <keystore>
- * <file>conf/keystore</file> <!-- location of keystore relative to .sar base directory -->
- * <password></password> <!-- Password for the Key Store file -->
- * <key-password></key-password> <!-- Optional private Key Password -->
- * <type>JKS</type> <!-- Type of the Key Store file -->
- * <protocol>TLS</protocol> <!-- SSL protocol to use -->
- * <algorithm>SunX509</algorithm> <!-- Certificate encoding algorithm -->
- * <authenticate-client>false</authenticate-client> <!-- Require client authentication? -->
- * <keystore>
+ * Configures the factory.
*
* @param configuration the Configuration
* @exception ConfigurationException if an error occurs
@@ -82,153 +79,110 @@
public void configure( final Configuration configuration )
throws ConfigurationException
{
- final Configuration keyStore = configuration.getChild( "keystore" );
- m_keyStoreFile = keyStore.getChild( "file" ).getValue( "conf/keystore" );
- m_keyStorePassword = keyStore.getChild( "password" ).getValue();
- m_keyPassword = keyStore.getChild( "key-password" ).getValue( null );
- m_keyStoreType = keyStore.getChild( "type" ).getValue( "JKS" );
- m_keyStoreProtocol = keyStore.getChild( "protocol" ).getValue( "TLS" );
- m_keyStoreAlgorithm = keyStore.getChild( "algorithm" ).getValue( "SunX509" );
- m_keyStoreAuthenticateClients
- = keyStore.getChild( "authenticate-client" ).getValueAsBoolean( false );
-
+ super.configure( configuration );
+ m_verifyServerIdentity = configuration.getChild( "verify-server-identity" ).getValueAsBoolean( false );
}
- public void initialize()
- throws Exception
+ protected void visitBuilder( SSLFactoryBuilder builder )
{
- final KeyStore keyStore = initKeyStore();
- initSSLFactory( keyStore );
+ m_factory = builder.buildSocketFactory();
}
- private KeyStore initKeyStore()
- throws Exception
+ /**
+ * Performs the unconditional part of socket initialization that
+ * applies to all Sockets.
+ */
+ private Socket initSocket( final Socket socket )
+ throws IOException
{
- try
- {
- final KeyStore keyStore = KeyStore.getInstance( m_keyStoreType );
- File keyStoreFile = new File( m_baseDirectory, m_keyStoreFile );
- if( !keyStoreFile.exists() ) keyStoreFile = new File( m_baseDirectory + m_keyStoreFile );
- final FileInputStream input = new FileInputStream( keyStoreFile );
-
- keyStore.load( input, m_keyStorePassword.toCharArray() );
- getLogger().info( "Keystore loaded from: " + keyStoreFile );
-
- return keyStore;
- }
- catch( final Exception e )
- {
- getLogger().error( "Exception loading keystore from: " + m_keyStoreFile, e );
- throw e;
- }
+ socket.setSoTimeout( m_socketTimeOut );
+ return socket;
}
- private void initSSLFactory( final KeyStore keyStore )
- throws Exception
- {
-
- java.security.Security.addProvider( new sun.security.provider.Sun() );
- java.security.Security.addProvider( new com.sun.net.ssl.internal.ssl.Provider() );
-
- // set up key manager to do server authentication
- final SSLContext sslContext = SSLContext.getInstance( m_keyStoreProtocol );
- final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( m_keyStoreAlgorithm );
-
- if( null == m_keyPassword )
+ /**
+ * Wraps an ssl socket over an existing socket and compares the
+ * host name from the address to the common name in the server
+ * certificate.
+ * @param bareSocket plain socket connected to the server
+ * @param address destination of the <tt>bareSocket</tt>
+ * @param port destination of the <tt>bareSocket</tt>
+ * @return SSL socket wrapped around original socket with server
+ * identity verified
+ */
+ private SSLSocket sslWrap( Socket bareSocket, InetAddress address,
+ int port )
+ throws IOException
+ {
+ final String hostName = address.getHostName();
+ final SSLSocket sslSocket = (SSLSocket)
+ m_factory.createSocket( bareSocket, hostName, port, true );
+ sslSocket.startHandshake();
+ final SSLSession session = sslSocket.getSession();
+ final String DN =
+ session.getPeerCertificateChain()[ 0 ].getSubjectDN().getName();
+ final String CN = getCN( DN );
+ if( !hostName.equals( CN ) )
{
- keyManagerFactory.init( keyStore, m_keyStorePassword.toCharArray() );
+ final String message = "Host name mismatch, expected '" +
+ hostName + "' recevied DN is " + DN;
+ throw new IOException( message );
}
- else
+ if( getLogger().isDebugEnabled() )
{
- keyManagerFactory.init( keyStore, m_keyPassword.toCharArray() );
+ final String message = "DN of the server " + DN;
+ getLogger().debug( message );
+ final String message2 = "Session id " +
+ bytesToString( session.getId() );
+ getLogger().debug( message2 );
}
-
- final TrustManagerFactory tmf = TrustManagerFactory.getInstance( m_keyStoreAlgorithm );
- tmf.init( keyStore );
-
- sslContext.init( keyManagerFactory.getKeyManagers(),
- tmf.getTrustManagers(),
- new java.security.SecureRandom() );
-
- // Create socket factory
- m_factory = sslContext.getSocketFactory();
+ return sslSocket;
}
- private void initSocket( final Socket socket )
- {
- final SSLSocket sslSocket = (SSLSocket)socket;
-
- // Enable all available cipher suites when the socket is connected
- final String[] cipherSuites = sslSocket.getSupportedCipherSuites();
- sslSocket.setEnabledCipherSuites( cipherSuites );
-
- // Set client authentication if necessary
- sslSocket.setNeedClientAuth( m_keyStoreAuthenticateClients );
- }
-
- /**
- * Returns a socket layered over an existing socket connected to the named
- * host, at the given port. This constructor can be used when tunneling SSL
- * through a proxy or when negotiating the use of SSL over an existing socket.
- * The host and port refer to the logical peer destination. This socket is
- * configured using the socket options established for this factory.
- *
- * @param s - the existing socket
- * @param host - the server host
- * @param port - the server port
- * @param autoClose - close the underlying socket when this socket is closed
- *
- * @exception IOException - if the connection can't be established
- * @exception UnknownHostException - if the host is not known
- */
- public Socket createSocket( Socket s, String host, int port, boolean autoClose ) throws IOException
+ private StringBuffer bytesToString( byte[] data )
{
- final Socket socket = m_factory.createSocket( s, host, port, autoClose );
- initSocket( socket );
- return socket;
- }
-
- /**
- * Returns a socket connected to a ServerSocket at the specified network
- * address and port. This socket is configured using the socket options
- * established for this factory.
- *
- * @param host - the server host
- * @param port - the server port
- *
- * @exception IOException - if the connection can't be established
- * @exception UnknownHostException - if the host is not known
- */
- public Socket createSocket( String host, int port ) throws IOException, UnknownHostException
- {
- InetAddress address = InetAddress.getByName( host );
- return this.createSocket( address, port );
+ final StringBuffer result = new StringBuffer( data.length * 3 );
+ String sep = "";
+ for( int i = 0; i < data.length; i++ )
+ {
+ final byte signedValue = data[ i ];
+ final int unsignedByteValue =
+ ( signedValue >= 0 ) ? signedValue : 256 + signedValue;
+ result.append( sep )
+ .append( Integer.toHexString( unsignedByteValue ) );
+ sep = ":";
+ }
+ return result;
}
/**
- * Returns a socket connected to a ServerSocket on the named host, at the
- * given port. The client address address is the specified host and port.
- * This socket is configured using the socket options established for this
- * factory.
- *
- * @param host - the server host
- * @param port - the server port
- * @param localAddress - the client host
- * @param localPort - the client port
+ * Extracts the Common Name from the given Distinguished
+ * Name. Normally CN is the first part of the DN.
+ * <b>If you know of a more direct way to determine the CN,
+ * please let us know</b>.
*
- * @exception IOException - if the connection can't be established
- * @exception UnknownHostException - if the host is not known
+ * @return the common name or null if DN is malformed
*/
- public Socket createSocket( String host, int port, InetAddress localAddress, int localPort )
- throws IOException, UnknownHostException
+ private String getCN( String DN )
{
- InetAddress address = InetAddress.getByName( host );
- return this.createSocket( address, port, localAddress, localPort );
-
+ final int startOfCN = DN.indexOf( "CN=" );
+ if( startOfCN < 0 )
+ {
+ return null;
+ }
+ final int startOfHostName = startOfCN + "CN=".length();
+ final int endOfHostName = DN.indexOf( ',', startOfHostName );
+ if( endOfHostName > 0 )
+ {
+ return DN.substring( startOfHostName, endOfHostName );
+ }
+ else
+ {
+ return null;
+ }
}
/**
- * Create a socket and connect to remote address specified.
+ * Creates a socket connected to the specified remote address.
*
* @param address the remote address
* @param port the remote port
@@ -237,13 +191,22 @@
*/
public Socket createSocket( InetAddress address, int port ) throws IOException
{
- final Socket socket = m_factory.createSocket( address, port );
- initSocket( socket );
- return socket;
+ // Uses 2 different approaches to socket construction, due to
+ // sslWrap dependency on wrapping createSocket which in turn
+ // requires that address be resolved to the host name.
+ if( m_verifyServerIdentity )
+ {
+ return sslWrap( initSocket( new Socket( address, port ) ),
+ address, port );
+ }
+ else
+ {
+ return initSocket( m_factory.createSocket( address, port ) );
+ }
}
/**
- * Create a socket and connect to remote address specified
+ * Creates a socket and connected to the specified remote address
* originating from specified local address.
*
* @param address the remote address
@@ -253,12 +216,26 @@
* @return the socket
* @exception IOException if an error occurs
*/
- public Socket createSocket( InetAddress address, int port, InetAddress localAddress, int localPort ) throws IOException
- {
- final Socket socket = m_factory.createSocket( address, port, localAddress, localPort );
- initSocket( socket );
- return socket;
+ public Socket createSocket( final InetAddress address,
+ final int port,
+ final InetAddress localAddress,
+ final int localPort )
+ throws IOException
+ {
+ // Uses 2 different approaches to socket construction, due to
+ // sslWrap dependency on wrapping createSocket which in turn
+ // requires that address be resolved to the host name.
+ if( m_verifyServerIdentity )
+ {
+ return sslWrap( initSocket( new Socket( address, port,
+ localAddress,
+ localPort ) ),
+ address, port );
+ }
+ else
+ {
+ return initSocket( m_factory.createSocket( address, port ) );
+ }
}
}
-
1.1 jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/AbstractTLSSocketFactory.java
Index: AbstractTLSSocketFactory.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.cornerstone.blocks.sockets;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
/**
* Contains the code common for both TLS socket factories. They both
* need to use an SSLFactoryBuilder which is configured using
* configuration and context given by the container. Then, they both
* set timeouts on the manufactured sockets.
*
* @author <a href="mailto:greg-avalon-apps at nest.cx">Greg Steuck</a>
*/
public abstract class AbstractTLSSocketFactory
extends AbstractLogEnabled
implements Contextualizable, Configurable, Initializable
{
private final static int WAIT_FOREVER = 0;
protected int m_socketTimeOut;
private Context m_context;
private Configuration m_childConfig;
public void contextualize( final Context context )
{
m_context = context;
}
/**
* Configures the factory.
*
* @param configuration the Configuration
* @exception ConfigurationException if an error occurs
*/
public void configure( final Configuration configuration )
throws ConfigurationException
{
m_socketTimeOut = configuration.getChild( "timeout" ).getValueAsInteger( WAIT_FOREVER );
m_childConfig = configuration.getChild( "ssl-factory", false );
if( m_childConfig == null )
{
final String message = "ssl-factory child not found, please" +
" update your configuration according to" +
" the documentation. Reverting to the" +
" old configuration format.";
getLogger().warn( message );
// not completely compatible though
m_childConfig = configuration;
}
}
/**
* Creates an SSL factory using the confuration values.
*/
public void initialize() throws Exception
{
final SSLFactoryBuilder builder = new SSLFactoryBuilder();
setupLogger( builder );
ContainerUtil.contextualize( builder, m_context );
ContainerUtil.configure( builder, m_childConfig );
ContainerUtil.initialize( builder );
visitBuilder( builder );
ContainerUtil.shutdown( builder );
m_context = null;
m_childConfig = null;
}
/**
* The child factories have to use an instance of
* <tt>SSLFactoryBuilder</tt> to obtain their factories. So they
* are given an instance when it's ready. Another alternative was
* to have the SSLFactoryBuilder export buildContext method, but
* that would mean SSLContext which is deep in Sun guts will be
* aired in 3-4 classes instead of 1.
*/
protected abstract void visitBuilder( SSLFactoryBuilder builder );
}
1.1 jakarta-avalon-cornerstone/src/java/org/apache/avalon/cornerstone/blocks/sockets/SSLFactoryBuilder.java
Index: SSLFactoryBuilder.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.avalon.cornerstone.blocks.sockets;
import com.sun.net.ssl.KeyManagerFactory;
import com.sun.net.ssl.SSLContext;
import com.sun.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.phoenix.BlockContext;
/**
* Builds SSLContexts with desired properties. Hides all the gory
* details of SSLContext productions behind nice Avalon
* interfaces. Married to Sun JCA implementation.
* <p>
* Configuration looks like:
* <pre>
* <ssl-factory>
* <keystore>
* <file>conf/keystore</file> <!-- keystore file location -->
* <password></password> <!-- Key Store file password, only used to check keystore integrity -->
* <key-password></key-password> <!-- Only required when you need to decrypt a private key -->
* <type>JKS</type> <!-- Key Store file format, defaults to JKS -->
* <algorithm>SunX509</algorithm> <!-- Cryptography provider ID, defaults to SunX509 -->
* </keystore>
* <!-- SSL protocol to use, defaults to TLS, another possible value is SSL -->
* <protocol>TLS</protocol>
* </ssl-factory>
* </pre>
* </p>
* <p>
* Notes on keystore files. Absolute paths are supported. Relative
* paths are interpreted relative to .sar base directory. Defaults to
* conf/keystore. Since keystore usually contains sensitive keys it
* maybe beneficial to <b>not</b> include the keystores into the .sar
* files.
* </p>
* @author <a href="mailto:greg-avalon-apps at nest.cx">Greg Steuck</a>
*/
public class SSLFactoryBuilder extends AbstractLogEnabled
implements Configurable, Contextualizable, Disposable, Initializable
{
private File m_baseDirectory;
private File m_keystoreFile;
private String m_keystorePassword;
private String m_keyPassword;
private String m_protocol;
private String m_provider;
private String m_keystoreFormat;
private SSLContext m_ctx;
static
{
// Registers Sun's providers
java.security.Security.addProvider( new sun.security.provider.Sun() );
java.security.Security.addProvider( new com.sun.net.ssl.internal.ssl.Provider() );
}
/**
* Requires a BlockContext. We'll see how we end up expressing
* these dependencies.
*/
public void contextualize( final Context context )
{
final BlockContext blockContext = (BlockContext) context;
m_baseDirectory = blockContext.getBaseDirectory();
}
public void configure( final Configuration configuration )
throws ConfigurationException
{
final Configuration storeConfig = configuration.getChild( "keystore" );
final String fileName = storeConfig.getChild( "file" ).getValue( "conf/keystore" );
final File configuredFile = new File( fileName );
if ( ! configuredFile.isAbsolute() )
{
m_keystoreFile = new File ( m_baseDirectory, fileName );
}
else
{
m_keystoreFile = configuredFile;
}
m_keystorePassword = storeConfig.getChild( "password" ).getValue( null );
m_keyPassword = storeConfig.getChild( "key-password" ).getValue( null );
// key is named incorrectly, left as is for compatibility
m_provider = storeConfig.getChild( "algorithm" ).getValue( "SunX509" );
// key is named incorrectly, left as is for compatibility
m_keystoreFormat = storeConfig.getChild( "type" ).getValue( "JKS" );
// ugly compatibility workaround follows
m_protocol = configuration.getChild( "protocol" ).
getValue( storeConfig.getChild( "protocol" ).getValue( "TLS" ) );
}
/**
* Produces a fresh ssl socket factory with configured parameters.
*/
public SSLSocketFactory buildSocketFactory()
{
return m_ctx.getSocketFactory();
}
/**
* Produces a fresh ssl server socket factory with configured
* parameters.
*/
public SSLServerSocketFactory buildServerSocketFactory()
{
return m_ctx.getServerSocketFactory();
}
public void initialize()
throws IOException, GeneralSecurityException
{
final FileInputStream keyStream = new FileInputStream( m_keystoreFile );
try
{
m_ctx = makeContext( keyStream, m_keystorePassword,
m_keyPassword, m_protocol,
m_provider, m_keystoreFormat );
}
finally
{
try
{
keyStream.close();
}
catch ( IOException e )
{
// avoids hiding exceptions from makeContext
// by catching this IOException
getLogger().error( "Error keyStream.close failed", e );
}
}
}
public void dispose()
{
m_keystorePassword = null;
m_keyPassword = null;
}
/**
* Creates an SSL context which uses the keys and certificates
* provided by the given <tt>keyStream</tt>. For simplicity the
* same key stream (keystore) is used for both key and trust
* factory.
*
* @param keyStream to read the keys from
* @param keystorePassword password for the keystore, can be null
* if integrity verification is not desired
* @param keyPassword passphrase which unlocks the keys in the key file
* (should really be a char[] so that it can be cleaned after use)
* @param protocol the standard name of the requested protocol
* @param provider the standard name of the requested algorithm
* @param keystoreFormat the type of keystore
*
* @return context configured with these keys and certificates
* @throws IOException if files can't be read
* @throws GeneralSecurityException is something goes wrong inside
* cryptography framework
*/
private static SSLContext makeContext (InputStream keyStream,
String keystorePassword,
String keyPassword,
String protocol,
String provider,
String keystoreFormat )
throws IOException, GeneralSecurityException
{
final KeyStore keystore = loadKeystore( keyStream,
keystorePassword,
keystoreFormat );
final KeyManagerFactory kmf = KeyManagerFactory.getInstance( provider );
final char [] passChars = keyPassword.toCharArray();
try
{
kmf.init( keystore, passChars );
}
finally
{
Arrays.fill( passChars, (char) 0 );
}
final TrustManagerFactory tmf =
TrustManagerFactory.getInstance( provider );
tmf.init( keystore );
final SSLContext result = SSLContext.getInstance( protocol );
result.init( kmf.getKeyManagers(),
tmf.getTrustManagers(),
new java.security.SecureRandom() );
return result;
}
/**
* Builds a keystore loaded from the given stream. The passphrase
* is used to verify the keystore file integrity.
* @param file to load from
* @param passphrase for the store integrity verification (or null if
* integrity check is not wanted)
* @param keystoreFormat the type of keystore
* @return loaded key store
* @throws IOException if file can not be read
* @throws GeneralSecurityException if key store can't be built
*/
private static KeyStore loadKeystore( InputStream keyStream,
String passphrase,
String keystoreFormat )
throws GeneralSecurityException, IOException
{
final KeyStore ks = KeyStore.getInstance( keystoreFormat );
if ( passphrase != null )
{
final char [] passChars = passphrase.toCharArray();
try
{
ks.load( keyStream, passChars );
}
finally
{
Arrays.fill( passChars, (char) 0 );
}
}
else
{
ks.load( keyStream, null );
}
return ks;
}
}
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>