KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > cornerstone > blocks > sockets > TLSSocketFactory


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.cornerstone.blocks.sockets;
19
20 import java.io.IOException JavaDoc;
21 import java.net.InetAddress JavaDoc;
22 import java.net.Socket JavaDoc;
23 import javax.net.ssl.SSLSession;
24 import javax.net.ssl.SSLSocket;
25 import javax.net.ssl.SSLSocketFactory;
26 import org.apache.avalon.cornerstone.services.sockets.SocketFactory;
27 import org.apache.avalon.framework.activity.Initializable;
28 import org.apache.avalon.framework.configuration.Configurable;
29 import org.apache.avalon.framework.configuration.Configuration;
30 import org.apache.avalon.framework.configuration.ConfigurationException;
31 import org.apache.avalon.framework.context.Contextualizable;
32
33 /**
34  * Manufactures TLS client sockets. Configuration element inside a
35  * SocketManager would look like:
36  * <pre>
37  * &lt;factory name="secure"
38  * class="org.apache.avalon.cornerstone.blocks.sockets.TLSSocketFactory" &gt;
39  * &lt;ssl-factory /&gt; &lt;!-- see {@link SSLFactoryBuilder} --&gt;
40  * &lt;timeout&gt; 0 &lt;/timeout&gt;
41  * &lt;!-- if the value is greater than zero, a read() call on the
42  * InputStream associated with this Socket will block for only this
43  * amount of time in milliseconds. Default value is 0. --&gt;
44  * &lt;verify-server-identity&gt;true|false&lt;/verify-server-identity&gt;
45  * &lt;!-- whether or not the server identity should be verified.
46  * Defaults to false. --&gt;
47  * &lt;/factory&gt;
48  * </pre>
49  * <p>
50  * Server identity verification currently includes only comparing the
51  * certificate Common Name received with the host name in the
52  * passed address. Identity verification requires that SSL
53  * handshake is completed for the socket, so it takes longer
54  * to get a verified socket (and won't play well with non-blocking
55  * application like SEDA).
56  * </p>
57  * <p>
58  * Another thing to keep in mind when using identity verification is
59  * that <tt>InetAddress</tt> objects for the remote hosts should be
60  * built using {@link java.net.InetAddress#getByName} with
61  * the host name (matching the certificate CN) as the
62  * argument. Failure to do so may cause relatively costly DNS lookups
63  * and false rejections caused by inconsistencies between forward and
64  * reverse resolution.
65  * </p>
66  *
67  * @author Peter Donald
68  * @author <a HREF="mailto:fede@apache.org">Federico Barbieri</a>
69  * @author <a HREF="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
70  * @author <a HREF="mailto:">Harish Prabandham</a>
71  * @author <a HREF="mailto:">Costin Manolache</a>
72  * @author <a HREF="mailto:">Craig McClanahan</a>
73  * @author <a HREF="mailto:myfam@surfeu.fi">Andrei Ivanov</a>
74  * @author <a HREF="mailto:greg-avalon-apps at nest.cx">Greg Steuck</a>
75  */

76 public class TLSSocketFactory
77     extends AbstractTLSSocketFactory
78     implements SocketFactory, Contextualizable, Configurable, Initializable
79 {
80     private SSLSocketFactory m_factory;
81     private boolean m_verifyServerIdentity;
82
83     /**
84      * Configures the factory.
85      *
86      * @param configuration the Configuration
87      * @exception ConfigurationException if an error occurs
88      */

89     public void configure( final Configuration configuration )
90         throws ConfigurationException
91     {
92         super.configure( configuration );
93         m_verifyServerIdentity = configuration.getChild( "verify-server-identity" ).getValueAsBoolean( false );
94     }
95
96     protected void visitBuilder( SSLFactoryBuilder builder )
97     {
98         m_factory = builder.buildSocketFactory();
99     }
100
101     /**
102      * Performs the unconditional part of socket initialization that
103      * applies to all Sockets.
104      */

105     private Socket JavaDoc initSocket( final Socket JavaDoc socket )
106         throws IOException JavaDoc
107     {
108         socket.setSoTimeout( m_socketTimeOut );
109         return socket;
110     }
111
112     /**
113      * Wraps an ssl socket over an existing socket and compares the
114      * host name from the address to the common name in the server
115      * certificate.
116      * @param bareSocket plain socket connected to the server
117      * @param address destination of the <tt>bareSocket</tt>
118      * @param port destination of the <tt>bareSocket</tt>
119      * @return SSL socket wrapped around original socket with server
120      * identity verified
121      */

122     private SSLSocket sslWrap( Socket JavaDoc bareSocket, InetAddress JavaDoc address,
123                                int port )
124         throws IOException JavaDoc
125     {
126         final String JavaDoc hostName = address.getHostName();
127         final SSLSocket sslSocket = (SSLSocket)
128             m_factory.createSocket( bareSocket, hostName, port, true );
129         sslSocket.startHandshake();
130         final SSLSession session = sslSocket.getSession();
131         final String JavaDoc DN =
132             session.getPeerCertificateChain()[ 0 ].getSubjectDN().getName();
133         final String JavaDoc CN = getCN( DN );
134         if( !hostName.equals( CN ) )
135         {
136             final String JavaDoc message = "Host name mismatch, expected '" +
137                 hostName + "' recevied DN is " + DN;
138             throw new IOException JavaDoc( message );
139         }
140         if( getLogger().isDebugEnabled() )
141         {
142             final String JavaDoc message = "DN of the server " + DN;
143             getLogger().debug( message );
144             final String JavaDoc message2 = "Session id " +
145                 bytesToString( session.getId() );
146             getLogger().debug( message2 );
147         }
148         return sslSocket;
149     }
150
151     private StringBuffer JavaDoc bytesToString( byte[] data )
152     {
153         final StringBuffer JavaDoc result = new StringBuffer JavaDoc( data.length * 3 );
154         String JavaDoc sep = "";
155         for( int i = 0; i < data.length; i++ )
156         {
157             final byte signedValue = data[ i ];
158             final int unsignedByteValue =
159                 ( signedValue >= 0 ) ? signedValue : 256 + signedValue;
160             result.append( sep )
161                 .append( Integer.toHexString( unsignedByteValue ) );
162             sep = ":";
163         }
164         return result;
165     }
166
167     /**
168      * Extracts the Common Name from the given Distinguished
169      * Name. Normally CN is the first part of the DN.
170      * <b>If you know of a more direct way to determine the CN,
171      * please let us know</b>.
172      *
173      * @return the common name or null if DN is malformed
174      */

175     private String JavaDoc getCN( String JavaDoc DN )
176     {
177         final int startOfCN = DN.indexOf( "CN=" );
178         if( startOfCN < 0 )
179         {
180             return null;
181         }
182         final int startOfHostName = startOfCN + "CN=".length();
183         final int endOfHostName = DN.indexOf( ',', startOfHostName );
184         if( endOfHostName > 0 )
185         {
186             return DN.substring( startOfHostName, endOfHostName );
187         }
188         else
189         {
190             return null;
191         }
192     }
193
194     /**
195      * Creates a socket connected to the specified remote address.
196      *
197      * @param address the remote address
198      * @param port the remote port
199      * @return the socket
200      * @exception IOException if an error occurs
201      */

202     public Socket JavaDoc createSocket( InetAddress JavaDoc address, int port ) throws IOException JavaDoc
203     {
204         // Uses 2 different approaches to socket construction, due to
205
// sslWrap dependency on wrapping createSocket which in turn
206
// requires that address be resolved to the host name.
207
if( m_verifyServerIdentity )
208         {
209             return sslWrap( initSocket( new Socket JavaDoc( address, port ) ),
210                             address, port );
211         }
212         else
213         {
214             return initSocket( m_factory.createSocket( address, port ) );
215         }
216     }
217
218     /**
219      * Creates a socket and connected to the specified remote address
220      * originating from specified local address.
221      *
222      * @param address the remote address
223      * @param port the remote port
224      * @param localAddress the local address
225      * @param localPort the local port
226      * @return the socket
227      * @exception IOException if an error occurs
228      */

229     public Socket JavaDoc createSocket( final InetAddress JavaDoc address,
230                                 final int port,
231                                 final InetAddress JavaDoc localAddress,
232                                 final int localPort )
233         throws IOException JavaDoc
234     {
235         // Uses 2 different approaches to socket construction, due to
236
// sslWrap dependency on wrapping createSocket which in turn
237
// requires that address be resolved to the host name.
238
if( m_verifyServerIdentity )
239         {
240             return sslWrap( initSocket( new Socket JavaDoc( address, port,
241                                                     localAddress,
242                                                     localPort ) ),
243                             address, port );
244         }
245         else
246         {
247             return initSocket( m_factory.createSocket( address, port ) );
248         }
249     }
250
251 }
252
Popular Tags