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