001 /*
002 * ====================================================================
003 *
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements. See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * ====================================================================
019 *
020 * This software consists of voluntary contributions made by many
021 * individuals on behalf of the Apache Software Foundation. For more
022 * information on the Apache Software Foundation, please see
023 * <http://www.apache.org/>.
024 *
025 */
026
027 package org.apache.commons.httpclient.contrib.ssl;
028
029 import java.io.IOException;
030 import java.net.InetAddress;
031 import java.net.InetSocketAddress;
032 import java.net.Socket;
033 import java.net.SocketAddress;
034 import java.net.UnknownHostException;
035
036 import javax.net.SocketFactory;
037 import javax.net.ssl.SSLContext;
038 import javax.net.ssl.TrustManager;
039
040 import org.apache.commons.httpclient.ConnectTimeoutException;
041 import org.apache.commons.httpclient.HttpClientError;
042 import org.apache.commons.httpclient.params.HttpConnectionParams;
043 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
044 import org.apache.commons.logging.Log;
045 import org.apache.commons.logging.LogFactory;
046
047 /**
048 * <p>
049 * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s
050 * that accept self-signed certificates.
051 * </p>
052 * <p>
053 * This socket factory SHOULD NOT be used for productive systems
054 * due to security reasons, unless it is a concious decision and
055 * you are perfectly aware of security implications of accepting
056 * self-signed certificates
057 * </p>
058 *
059 * <p>
060 * Example of using custom protocol socket factory for a specific host:
061 * <pre>
062 * Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
063 *
064 * URI uri = new URI("https://localhost/", true);
065 * // use relative url only
066 * GetMethod httpget = new GetMethod(uri.getPathQuery());
067 * HostConfiguration hc = new HostConfiguration();
068 * hc.setHost(uri.getHost(), uri.getPort(), easyhttps);
069 * HttpClient client = new HttpClient();
070 * client.executeMethod(hc, httpget);
071 * </pre>
072 * </p>
073 * <p>
074 * Example of using custom protocol socket factory per default instead of the standard one:
075 * <pre>
076 * Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
077 * Protocol.registerProtocol("https", easyhttps);
078 *
079 * HttpClient client = new HttpClient();
080 * GetMethod httpget = new GetMethod("https://localhost/");
081 * client.executeMethod(httpget);
082 * </pre>
083 * </p>
084 *
085 * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
086 *
087 * <p>
088 * DISCLAIMER: HttpClient developers DO NOT actively support this component.
089 * The component is provided as a reference material, which may be inappropriate
090 * for use without additional customization.
091 * </p>
092 */
093
094 public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
095
096 /** Log object for this class. */
097 private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class);
098
099 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 }