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>
  + *  &lt;factory name="secure"
  + *            class="org.apache.avalon.cornerstone.blocks.sockets.TLSServerSocketFactory" &gt;
  + *   &lt;ssl-factory /&gt; &lt;!-- see {@link SSLFactoryBuilder} --&gt;
  + *   &lt;timeout&gt; 0 &lt;/timeout&gt;
  + *   &lt;!-- 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. --&gt;
  + *   &lt;authenticate-client&gt;false&lt;/authenticate-client&gt;
  + *   &lt;!-- Whether or not the client must present a certificate to
  + *      confirm its identity. Defaults to false. --&gt;
  + * &lt;/factory&gt;
  + * </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>
  + *  &lt;factory name="secure"
  + *            class="org.apache.avalon.cornerstone.blocks.sockets.TLSSocketFactory" &gt;
  + *   &lt;ssl-factory /&gt; &lt;!-- see {@link SSLFactoryBuilder} --&gt;
  + *   &lt;timeout&gt; 0 &lt;/timeout&gt;
  + *   &lt;!-- 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. --&gt;
  + *   &lt;verify-server-identity&gt;true|false&lt;/verify-server-identity&gt;
  + *   &lt;!-- whether or not the server identity should be verified.
  + *           Defaults to false. --&gt;
  + * &lt;/factory&gt;
  + * </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>
   * &lt;ssl-factory&gt;
   *    &lt;keystore&gt;
   *      &lt;file&gt;conf/keystore&lt;/file&gt; &lt;!-- keystore file location --&gt;
   *      &lt;password&gt;&lt;/password&gt; &lt;!-- Key Store file password, only used to check keystore integrity --&gt;
   *      &lt;key-password&gt;&lt;/key-password&gt; &lt;!-- Only required when you need to decrypt a private key --&gt;
   *     &lt;type&gt;JKS&lt;/type&gt; &lt;!-- Key Store file format, defaults to JKS --&gt;
   *     &lt;algorithm&gt;SunX509&lt;/algorithm&gt; &lt;!-- Cryptography provider ID, defaults to SunX509 --&gt;
   *   &lt;/keystore&gt;
   *   &lt;!-- SSL protocol to use, defaults to TLS, another possible value is SSL --&gt;
   *   &lt;protocol&gt;TLS&lt;/protocol&gt;
   * &lt;/ssl-factory&gt;
   * </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>