001    /**
002     * Copyright 2005-2014 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.log4j.Logger;
034    
035    /**
036     * <p>
037     * EasySSLProtocolSocketFactory can be used to creats SSL {@link Socket}s 
038     * that accept self-signed certificates. 
039     * </p>
040     * <p>
041     * This socket factory SHOULD NOT be used for productive systems 
042     * due to security reasons, unless it is a concious decision and 
043     * you are perfectly aware of security implications of accepting 
044     * self-signed certificates
045     * </p>
046     *
047     * <p>
048     * Example of using custom protocol socket factory for a specific host:
049     *     <pre>
050     *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
051     *
052     *     URI uri = new URI("https://localhost/", true);
053     *     // use relative url only
054     *     GetMethod httpget = new GetMethod(uri.getPathQuery());
055     *     HostConfiguration hc = new HostConfiguration();
056     *     hc.setHost(uri.getHost(), uri.getPort(), easyhttps);
057     *     HttpClient client = new HttpClient();
058     *     client.executeMethod(hc, httpget);
059     *     </pre>
060     * </p>
061     * <p>
062     * Example of using custom protocol socket factory per default instead of the standard one:
063     *     <pre>
064     *     Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
065     *     Protocol.registerProtocol("https", easyhttps);
066     *
067     *     HttpClient client = new HttpClient();
068     *     GetMethod httpget = new GetMethod("https://localhost/");
069     *     client.executeMethod(httpget);
070     *     </pre>
071     * </p>
072     * 
073     * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
074     * 
075     * <p>
076     * DISCLAIMER: HttpClient developers DO NOT actively support this component.
077     * The component is provided as a reference material, which may be inappropriate
078     * for use without additional customization.
079     * </p>
080     */
081    
082    public class EasySSLProtocolSocketFactory implements SecureProtocolSocketFactory {
083    
084        /** Log object for this class. */
085        private static final Logger LOG = Logger.getLogger(EasySSLProtocolSocketFactory.class);
086    
087        private SSLContext sslcontext = null;
088    
089        /**
090         * Constructor for EasySSLProtocolSocketFactory.
091         */
092        public EasySSLProtocolSocketFactory() {
093            super();
094        }
095    
096        private static SSLContext createEasySSLContext() {
097            try {
098                SSLContext context = SSLContext.getInstance("SSL");
099                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    }