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 }