View Javadoc

1   /*
2    * ====================================================================
3    *
4    *  Licensed to the Apache Software Foundation (ASF) under one or more
5    *  contributor license agreements.  See the NOTICE file distributed with
6    *  this work for additional information regarding copyright ownership.
7    *  The ASF licenses this file to You under the Apache License, Version 2.0
8    *  (the "License"); you may not use this file except in compliance with
9    *  the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *  Unless required by applicable law or agreed to in writing, software
14   *  distributed under the License is distributed on an "AS IS" BASIS,
15   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *  See the License for the specific language governing permissions and
17   *  limitations under the License.
18   * ====================================================================
19   *
20   * This software consists of voluntary contributions made by many
21   * individuals on behalf of the Apache Software Foundation.  For more
22   * information on the Apache Software Foundation, please see
23   * <http://www.apache.org/>.
24   *
25   */
26  
27  package org.apache.commons.httpclient.contrib.ssl;
28  
29  import java.io.IOException;
30  import java.net.InetAddress;
31  import java.net.InetSocketAddress;
32  import java.net.Socket;
33  import java.net.SocketAddress;
34  import java.net.UnknownHostException;
35  
36  import javax.net.SocketFactory;
37  import javax.net.ssl.SSLContext;
38  import javax.net.ssl.TrustManager;
39  
40  import org.apache.commons.httpclient.ConnectTimeoutException;
41  import org.apache.commons.httpclient.HttpClientError;
42  import org.apache.commons.httpclient.params.HttpConnectionParams;
43  import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  
47  /**
48   * <p>
49   * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s 
50   * that accept self-signed certificates. 
51   * </p>
52   * <p>
53   * This socket factory SHOULD NOT be used for productive systems 
54   * due to security reasons, unless it is a concious decision and 
55   * you are perfectly aware of security implications of accepting 
56   * self-signed certificates
57   * </p>
58   *
59   * <p>
60   * Example of using custom protocol socket factory for a specific host:
61   *     <pre>
62   *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
63   *
64   *     URI uri = new URI("https://localhost/", true);
65   *     // use relative url only
66   *     GetMethod httpget = new GetMethod(uri.getPathQuery());
67   *     HostConfiguration hc = new HostConfiguration();
68   *     hc.setHost(uri.getHost(), uri.getPort(), easyhttps);
69   *     HttpClient client = new HttpClient();
70   *     client.executeMethod(hc, httpget);
71   *     </pre>
72   * </p>
73   * <p>
74   * Example of using custom protocol socket factory per default instead of the standard one:
75   *     <pre>
76   *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
77   *     Protocol.registerProtocol("https", easyhttps);
78   *
79   *     HttpClient client = new HttpClient();
80   *     GetMethod httpget = new GetMethod("https://localhost/");
81   *     client.executeMethod(httpget);
82   *     </pre>
83   * </p>
84   * 
85   * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
86   * 
87   * <p>
88   * DISCLAIMER: HttpClient developers DO NOT actively support this component.
89   * The component is provided as a reference material, which may be inappropriate
90   * for use without additional customization.
91   * </p>
92   */
93  
94  public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
95  
96      /** Log object for this class. */
97      private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class);
98  
99      private SSLContext sslcontext = null;
100 
101     /**
102      * Constructor for EasySSLProtocolSocketFactory.
103      */
104     public EasySSLProtocolSocketFactory() {
105         super();
106     }
107 
108     private static SSLContext createEasySSLContext() {
109         try {
110             SSLContext context = SSLContext.getInstance("SSL");
111             context.init(
112               null, 
113               new TrustManager[] {new EasyX509TrustManager(null)}, 
114               null);
115             return context;
116         } catch (Exception e) {
117             LOG.error(e.getMessage(), e);
118             throw new HttpClientError(e.toString());
119         }
120     }
121 
122     private SSLContext getSSLContext() {
123         if (this.sslcontext == null) {
124             this.sslcontext = createEasySSLContext();
125         }
126         return this.sslcontext;
127     }
128 
129     /**
130      * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
131      */
132     public Socket createSocket(
133         String host,
134         int port,
135         InetAddress clientHost,
136         int clientPort)
137         throws IOException, UnknownHostException {
138 
139         return getSSLContext().getSocketFactory().createSocket(
140             host,
141             port,
142             clientHost,
143             clientPort
144         );
145     }
146 
147     /**
148      * Attempts to get a new socket connection to the given host within the given time limit.
149      * <p>
150      * To circumvent the limitations of older JREs that do not support connect timeout a 
151      * controller thread is executed. The controller thread attempts to create a new socket 
152      * within the given limit of time. If socket constructor does not return until the 
153      * timeout expires, the controller terminates and throws an {@link ConnectTimeoutException}
154      * </p>
155      *  
156      * @param host the host name/IP
157      * @param port the port on the host
158      * @param clientHost the local host name/IP to bind the socket to
159      * @param clientPort the port on the local machine
160      * @param params {@link HttpConnectionParams Http connection parameters}
161      * 
162      * @return Socket a new socket
163      * 
164      * @throws IOException if an I/O error occurs while creating the socket
165      * @throws UnknownHostException if the IP address of the host cannot be
166      * determined
167      */
168     public Socket createSocket(
169         final String host,
170         final int port,
171         final InetAddress localAddress,
172         final int localPort,
173         final HttpConnectionParams params
174     ) throws IOException, UnknownHostException, ConnectTimeoutException {
175         if (params == null) {
176             throw new IllegalArgumentException("Parameters may not be null");
177         }
178         int timeout = params.getConnectionTimeout();
179         SocketFactory socketfactory = getSSLContext().getSocketFactory();
180         if (timeout == 0) {
181             return socketfactory.createSocket(host, port, localAddress, localPort);
182         } else {
183             Socket socket = socketfactory.createSocket();
184             SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
185             SocketAddress remoteaddr = new InetSocketAddress(host, port);
186             socket.bind(localaddr);
187             socket.connect(remoteaddr, timeout);
188             return socket;
189         }
190     }
191 
192     /**
193      * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
194      */
195     public Socket createSocket(String host, int port)
196         throws IOException, UnknownHostException {
197         return getSSLContext().getSocketFactory().createSocket(
198             host,
199             port
200         );
201     }
202 
203     /**
204      * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
205      */
206     public Socket createSocket(
207         Socket socket,
208         String host,
209         int port,
210         boolean autoClose)
211         throws IOException, UnknownHostException {
212         return getSSLContext().getSocketFactory().createSocket(
213             socket,
214             host,
215             port,
216             autoClose
217         );
218     }
219 
220     public boolean equals(Object obj) {
221         return ((obj != null) && obj.getClass().equals(EasySSLProtocolSocketFactory.class));
222     }
223 
224     public int hashCode() {
225         return EasySSLProtocolSocketFactory.class.hashCode();
226     }
227 
228 }