All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsslutils.extra.apachehttpclient.SslContextedSecureProtocolSocketFactory Maven / Gradle / Ivy

The newest version!
/*
 * ====================================================================
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many individuals on
 * behalf of the Apache Software Foundation. For more information on the Apache
 * Software Foundation, please see .
 * 
 * [Additional notices, if required by prior licensing conditions]
 * 
 * Alternatively, the contents of this file may be used under the terms of the
 * GNU Lesser General Public License Version 2 or later (the "LGPL"), in which
 * case the provisions of the LGPL are applicable instead of those above. See
 * terms of LGPL at . If you wish to
 * allow use of your version of this file only under the terms of the LGPL and
 * not to allow others to use your version of this file under the Apache
 * Software License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the LGPL. If
 * you do not delete the provisions above, a recipient may use your version of
 * this file under either the Apache Software License or the LGPL.
 */

package org.jsslutils.extra.apachehttpclient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.security.auth.x500.X500Principal;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;

/**
 * This is a SecureProtocolSocketFactory for with the SSLContext can be
 * configured. It is based on Sebastian Hauer's StrictSSLProtocolSocketFactory,
 * available in the contribution directory of the Apache HTTP client library
 * 3.1. The main difference is that the SSLContext can be set, which means that
 * the use of client certificates or CRLs may be configured this way. The intent
 * was to use it in conjunction with jSSLutils, but it is not a
 * dependency.
 * 
 * If no SSLContext is set up, the default SSLSocketFactory is used.
 * 
 * @author Bruno Harbulot
 * @author Sebastian Hauer
 *         

* DISCLAIMER: HttpClient developers DO NOT actively support this * component. The component is provided as a reference material, which * may be inappropriate for use without additional customization. *

*/ public class SslContextedSecureProtocolSocketFactory implements SecureProtocolSocketFactory { private SSLContext sslContext; /** Host name verify flag. */ private boolean verifyHostname = true; /** * Constructor for SslContextedSecureProtocolSocketFactory. * * @param sslContext * The SSLContext to use for building the SSLSocketFactory. If * this is null, then the default SSLSocketFactory is used. * @param verifyHostname * The host name verification flag. If set to true * the SSL sessions server host name will be compared to the host * name returned in the server certificates "Common Name" field * of the "SubjectDN" entry. If these names do not match a * Exception is thrown to indicate this. Enabling host name * verification will help to prevent from man-in-the-middle * attacks. If set to false host name verification * is turned off. * * Code sample: * *
Protocol stricthttps = new Protocol( "https", new * SslContextedSecureProtocolSocketFactory(sslContext,true), * 443); * * HttpClient client = new HttpClient(); * client.getHostConfiguration().setHost("localhost", 443, * stricthttps);
* */ public SslContextedSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) { this.sslContext = sslContext; this.verifyHostname = verifyHostname; } /** * Constructor for SslContextedSecureProtocolSocketFactory. Host name * verification will be enabled by default. * * @param sslContext * The SSLContext to use for building the SSLSocketFactory. If * this is null, then the default SSLSocketFactory is used. */ public SslContextedSecureProtocolSocketFactory(SSLContext sslContext) { this(sslContext, true); } /** * Constructor for SslContextedSecureProtocolSocketFactory. The default * SSLSocketFactory will be used by default. * * @param verifyHostname * The host name verification flag. If set to true * the SSL sessions server host name will be compared to the host * name returned in the server certificates "Common Name" field * of the "SubjectDN" entry. If these names do not match a * Exception is thrown to indicate this. Enabling host name * verification will help to prevent from man-in-the-middle * attacks. If set to false host name verification * is turned off. */ public SslContextedSecureProtocolSocketFactory(boolean verifyHostname) { this(null, verifyHostname); } /** * Constructor for SslContextedSecureProtocolSocketFactory. By default, the * default SSLSocketFactory will be used and host name verification will be * enabled. */ public SslContextedSecureProtocolSocketFactory() { this(null, true); } /** * Set the host name verification flag. * * @param verifyHostname * The host name verification flag. If set to true * the SSL sessions server host name will be compared to the host * name returned in the server certificates "Common Name" field * of the "SubjectDN" entry. If these names do not match a * Exception is thrown to indicate this. Enabling host name * verification will help to prevent from man-in-the-middle * attacks. If set to false host name verification * is turned off. */ public synchronized void setHostnameVerification(boolean verifyHostname) { this.verifyHostname = verifyHostname; } /** * Gets the status of the host name verification flag. * * @return Host name verification flag. Either true if host * name verification is turned on, or false if host * name verification is turned off. */ public synchronized boolean getHostnameVerification() { return verifyHostname; } /** * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) */ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { SSLSocketFactory sf = (SSLSocketFactory) getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket) sf.createSocket(host, port, clientHost, clientPort); verifyHostname(sslSocket); return sslSocket; } /** * Attempts to get a new socket connection to the given host within the * given time limit. *

* This method employs several techniques to circumvent the limitations of * older JREs that do not support connect timeout. When running in JRE 1.4 * or above reflection is used to call Socket#connect(SocketAddress * endpoint, int timeout) method. When executing in older JREs a controller * thread is executed. The controller thread attempts to create a new socket * within the given limit of time. If socket constructor does not return * until the timeout expires, the controller terminates and throws an * {@link ConnectTimeoutException} *

* * @param host * the host name/IP * @param port * the port on the host * @param localAddress * the local host name/IP to bind the socket to * @param localPort * the port on the local machine * @param params * {@link HttpConnectionParams Http connection parameters} * * @return Socket a new socket * * @throws IOException * if an I/O error occurs while creating the socket * @throws UnknownHostException * if the IP address of the host cannot be determined */ public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); } int timeout = params.getConnectionTimeout(); Socket socket = null; SocketFactory socketfactory = getSslSocketFactory(); if (timeout == 0) { socket = socketfactory.createSocket(host, port, localAddress, localPort); } else { socket = socketfactory.createSocket(); SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); SocketAddress remoteaddr = new InetSocketAddress(host, port); socket.bind(localaddr); socket.connect(remoteaddr, timeout); } verifyHostname((SSLSocket) socket); return socket; } /** * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) */ public Socket createSocket(String host, int port) throws IOException, UnknownHostException { SSLSocketFactory sf = (SSLSocketFactory) getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket) sf.createSocket(host, port); verifyHostname(sslSocket); return sslSocket; } /** * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { SSLSocketFactory sf = (SSLSocketFactory) getSslSocketFactory(); SSLSocket sslSocket = (SSLSocket) sf.createSocket(socket, host, port, autoClose); verifyHostname(sslSocket); return sslSocket; } /** * Describe verifyHostname method here. * * @param socket * a SSLSocket value * @exception SSLPeerUnverifiedException * If there are problems obtaining the server certificates * from the SSL session, or the server host name does not * match with the "Common Name" in the server certificates * SubjectDN. * @exception UnknownHostException * If we are not able to resolve the SSL sessions returned * server host name. * @throws CertificateParsingException */ private void verifyHostname(SSLSocket socket) throws IOException, UnknownHostException { synchronized (this) { if (!verifyHostname) return; } SSLSession session = socket.getSession(); String hostname = session.getPeerHost(); try { InetAddress.getByName(hostname); } catch (UnknownHostException uhe) { throw new UnknownHostException("Could not resolve SSL sessions " + "server hostname: " + hostname); } X509Certificate[] certs = (X509Certificate[]) session .getPeerCertificates(); if (certs == null || certs.length == 0) throw new SSLPeerUnverifiedException( "No server certificates found!"); try { List cns = new ArrayList(); boolean foundDnsSan = false; Collection> subjectAltNames = certs[0] .getSubjectAlternativeNames(); if (subjectAltNames != null) { for (List san : subjectAltNames) { if (((Integer) san.get(0)).intValue() == 2) { foundDnsSan = true; String sanDns = (String) san.get(1); cns.add(sanDns); if (hostname.equalsIgnoreCase(sanDns)) { return; } } } } if (!foundDnsSan) { // get the common names from the first cert X500Principal subjectDN = certs[0].getSubjectX500Principal(); cns = getCNs(subjectDN); for (String cn : cns) { if (hostname.equalsIgnoreCase(cn)) { return; } } } throw new SSLPeerUnverifiedException( "HTTPS hostname invalid: expected '" + hostname + "', received '" + cns + "'"); } catch (CertificateParsingException e) { throw new IOException(e); } } /** * Parses a X.500 distinguished name for the values of the "Common Name" * fields. This is done a bit sloppy right now and should probably be done a * bit more according to RFC 2253. * * @param subjectDN * an X.500 Principal from an X.509 certificate. * @return the values of the "Common Name" fields. */ private List getCNs(X500Principal subjectDN) { List cns = new ArrayList(); StringTokenizer st = new StringTokenizer(subjectDN.getName(), ","); while (st.hasMoreTokens()) { String cnField = st.nextToken(); if (cnField.startsWith("CN=")) { cns.add(cnField.substring(3)); } } return cns; } /** * Returns the SSLSocketFactory to use to create the sockets. If the * sslContext is non-null, this is built from the sslContext; otherwise, * this is the default SSLSocketFactory. * * @return the SSLSocketFactory to use to create the sockets. */ protected SSLSocketFactory getSslSocketFactory() { SSLSocketFactory sslSocketFactory = null; synchronized (this) { if (this.sslContext != null) { sslSocketFactory = this.sslContext.getSocketFactory(); } } if (sslSocketFactory == null) { sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); } return sslSocketFactory; } /** * Sets the SSLContext to use. * * @param sslContext * SSLContext to use. */ public synchronized void setSSLContext(SSLContext sslContext) { this.sslContext = sslContext; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy