001    /**
002     * Copyright 2005-2011 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.httpclient.contrib.ssl;
017    
018    import java.io.IOException;
019    import java.net.InetAddress;
020    import java.net.InetSocketAddress;
021    import java.net.Socket;
022    import java.net.SocketAddress;
023    import java.net.UnknownHostException;
024    
025    import javax.net.SocketFactory;
026    import javax.net.ssl.SSLContext;
027    import javax.net.ssl.TrustManager;
028    
029    import org.apache.commons.httpclient.ConnectTimeoutException;
030    import org.apache.commons.httpclient.HttpClientError;
031    import org.apache.commons.httpclient.params.HttpConnectionParams;
032    import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    
036    /**
037     * <p>
038     * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s 
039     * that accept self-signed certificates. 
040     * </p>
041     * <p>
042     * This socket factory SHOULD NOT be used for productive systems 
043     * due to security reasons, unless it is a concious decision and 
044     * you are perfectly aware of security implications of accepting 
045     * self-signed certificates
046     * </p>
047     *
048     * <p>
049     * Example of using custom protocol socket factory for a specific host:
050     *     <pre>
051     *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
052     *
053     *     URI uri = new URI("https://localhost/", true);
054     *     // use relative url only
055     *     GetMethod httpget = new GetMethod(uri.getPathQuery());
056     *     HostConfiguration hc = new HostConfiguration();
057     *     hc.setHost(uri.getHost(), uri.getPort(), easyhttps);
058     *     HttpClient client = new HttpClient();
059     *     client.executeMethod(hc, httpget);
060     *     </pre>
061     * </p>
062     * <p>
063     * Example of using custom protocol socket factory per default instead of the standard one:
064     *     <pre>
065     *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
066     *     Protocol.registerProtocol("https", easyhttps);
067     *
068     *     HttpClient client = new HttpClient();
069     *     GetMethod httpget = new GetMethod("https://localhost/");
070     *     client.executeMethod(httpget);
071     *     </pre>
072     * </p>
073     * 
074     * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
075     * 
076     * <p>
077     * DISCLAIMER: HttpClient developers DO NOT actively support this component.
078     * The component is provided as a reference material, which may be inappropriate
079     * for use without additional customization.
080     * </p>
081     */
082    
083    public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
084    
085        /** Log object for this class. */
086        private static final Log LOG = LogFactory.getLog(EasySSLProtocolSocketFactory.class);
087    
088        private SSLContext sslcontext = null;
089    
090        /**
091         * Constructor for EasySSLProtocolSocketFactory.
092         */
093        public EasySSLProtocolSocketFactory() {
094            super();
095        }
096    
097        private static SSLContext createEasySSLContext() {
098            try {
099                SSLContext context = SSLContext.getInstance("SSL");
100                context.init(
101                  null, 
102                  new TrustManager[] {new EasyX509TrustManager(null)}, 
103                  null);
104                return context;
105            } catch (Exception e) {
106                LOG.error(e.getMessage(), e);
107                throw new HttpClientError(e.toString());
108            }
109        }
110    
111        private SSLContext getSSLContext() {
112            if (this.sslcontext == null) {
113                this.sslcontext = createEasySSLContext();
114            }
115            return this.sslcontext;
116        }
117    
118        /**
119         * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
120         */
121        public Socket createSocket(
122            String host,
123            int port,
124            InetAddress clientHost,
125            int clientPort)
126            throws IOException, UnknownHostException {
127    
128            return getSSLContext().getSocketFactory().createSocket(
129                host,
130                port,
131                clientHost,
132                clientPort
133            );
134        }
135    
136        /**
137         * Attempts to get a new socket connection to the given host within the given time limit.
138         * <p>
139         * To circumvent the limitations of older JREs that do not support connect timeout a 
140         * controller thread is executed. The controller thread attempts to create a new socket 
141         * within the given limit of time. If socket constructor does not return until the 
142         * timeout expires, the controller terminates and throws an {@link ConnectTimeoutException}
143         * </p>
144         *  
145         * @param host the host name/IP
146         * @param port the port on the host
147         * @param clientHost the local host name/IP to bind the socket to
148         * @param clientPort the port on the local machine
149         * @param params {@link HttpConnectionParams Http connection parameters}
150         * 
151         * @return Socket a new socket
152         * 
153         * @throws IOException if an I/O error occurs while creating the socket
154         * @throws UnknownHostException if the IP address of the host cannot be
155         * determined
156         */
157        public Socket createSocket(
158            final String host,
159            final int port,
160            final InetAddress localAddress,
161            final int localPort,
162            final HttpConnectionParams params
163        ) throws IOException, UnknownHostException, ConnectTimeoutException {
164            if (params == null) {
165                throw new IllegalArgumentException("Parameters may not be null");
166            }
167            int timeout = params.getConnectionTimeout();
168            SocketFactory socketfactory = getSSLContext().getSocketFactory();
169            if (timeout == 0) {
170                return socketfactory.createSocket(host, port, localAddress, localPort);
171            } else {
172                Socket socket = socketfactory.createSocket();
173                SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
174                SocketAddress remoteaddr = new InetSocketAddress(host, port);
175                socket.bind(localaddr);
176                socket.connect(remoteaddr, timeout);
177                return socket;
178            }
179        }
180    
181        /**
182         * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
183         */
184        public Socket createSocket(String host, int port)
185            throws IOException, UnknownHostException {
186            return getSSLContext().getSocketFactory().createSocket(
187                host,
188                port
189            );
190        }
191    
192        /**
193         * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
194         */
195        public Socket createSocket(
196            Socket socket,
197            String host,
198            int port,
199            boolean autoClose)
200            throws IOException, UnknownHostException {
201            return getSSLContext().getSocketFactory().createSocket(
202                socket,
203                host,
204                port,
205                autoClose
206            );
207        }
208    
209        public boolean equals(Object obj) {
210            return ((obj != null) && obj.getClass().equals(EasySSLProtocolSocketFactory.class));
211        }
212    
213        public int hashCode() {
214            return EasySSLProtocolSocketFactory.class.hashCode();
215        }
216    
217    }