We are using a slightly modified AuthSSLProtocolSocketFactory provided as a sample on the apache commons file server (attached).
I tried this on both 1.4.2 and 1.5.0_03. -----Original Message----- From: Oleg Kalnichevski [mailto:[EMAIL PROTECTED] Sent: Tuesday, August 09, 2005 3:19 PM To: HttpClient User Discussion Subject: Re: manager, timeout, and SSL - PLEASE HELP!!! Ivan, (1) Please post the source code of the AuthSSLProtocolSocketFactory class (2) Let me know which JRE you you using. Connection timeouts are handled completely differently for JRE < 1.4 and those >= 1.4 I'll look at it tomorrow. It is kind of late here Oleg On Tue, 2005-08-09 at 15:08 -0700, Ivan B wrote: > I would appreciate anyone's input on this it is driving me crazy. > > Basically, we are accessing a host that has personal cert installed > over https. In the sample code bellow everything works just fine until > I uncomment the commented line. Once I do that I get "Peer not > verified" exception. > > > > public SmapleTestCraft() throws Exception { > CertificateManager certManager = CertificateManager.getInstance(); > ProtocolSocketFactory sf = new > AuthSSLProtocolSocketFactory(certManager);; > > Protocol.registerProtocol("https", new Protocol("https", sf, > 443)); > > MultiThreadedHttpConnectionManager connectionManager = new > MultiThreadedHttpConnectionManager(); > // THIS LINE BREAKS EVERYTHING, BUT WE NEED TIMEOUT VALUE > //connectionManager.getParams().setConnectionTimeout(5000); > > HttpClient httpclient = new HttpClient(connectionManager); > > httpclient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBIL > ITY); > > GetMethod httpget = new > GetMethod("https://127.0.0.1/banner.txt"); > > try { > httpclient.executeMethod(httpget); > System.out.println(httpget.getStatusLine()); > } finally { > httpget.releaseConnection(); > } > } > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: > [EMAIL PROTECTED] > > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
/* * $Revision: 1.5 $ * $Date: 2005/08/09 00:19:38 $ * * ==================================================================== * * Copyright 2002-2004 The Apache Software Foundation * * Licensed 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 * <http://www.apache.org/>. * */ package com.infoblox.client; import java.io.IOException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Enumeration; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; 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.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import javax.swing.JOptionPane; import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory; import org.apache.commons.httpclient.protocol.ReflectionSocketFactory; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; import com.infoblox.util.IDebug; /** * <p> * AuthSSLProtocolSocketFactory can be used to validate the identity of the HTTPS * server against a list of trusted certificates and to authenticate to the HTTPS * server using a private key. * </p> * * <p> * AuthSSLProtocolSocketFactory will enable server authentication when supplied with * a [EMAIL PROTECTED] KeyStore truststore} file containg one or several trusted certificates. * The client secure socket will reject the connection during the SSL session handshake * if the target HTTPS server attempts to authenticate itself with a non-trusted * certificate. * </p> * * <p> * Use JDK keytool utility to import a trusted certificate and generate a truststore file: * <pre> * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore * </pre> * </p> * * <p> * AuthSSLProtocolSocketFactory will enable client authentication when supplied with * a [EMAIL PROTECTED] KeyStore keystore} file containg a private key/public certificate pair. * The client secure socket will use the private key to authenticate itself to the target * HTTPS server during the SSL session handshake if requested to do so by the server. * The target HTTPS server will in its turn verify the certificate presented by the client * in order to establish client's authenticity * </p> * * <p> * Use the following sequence of actions to generate a keystore file * </p> * <ul> * <li> * <p> * Use JDK keytool utility to generate a new key * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> * For simplicity use the same password for the key as that of the keystore * </p> * </li> * <li> * <p> * Issue a certificate signing request (CSR) * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> * </p> * </li> * <li> * <p> * Send the certificate request to the trusted Certificate Authority for signature. * One may choose to act as her own CA and sign the certificate request using a PKI * tool, such as OpenSSL. * </p> * </li> * <li> * <p> * Import the trusted CA root certificate * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> * </p> * </li> * <li> * <p> * Import the PKCS#7 file containg the complete certificate chain * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> * </p> * </li> * <li> * <p> * Verify the content the resultant keystore file * <pre>keytool -list -v -keystore my.keystore</pre> * </p> * </li> * </ul> * <p> * Example of using custom protocol socket factory for a specific host: * <pre> * Protocol authhttps = new Protocol("https", * new AuthSSLProtocolSocketFactory( * new URL("file:my.keystore"), "mypassword", * new URL("file:my.truststore"), "mypassword"), 443); * * HttpClient client = new HttpClient(); * client.getHostConfiguration().setHost("localhost", 443, authhttps); * // use relative url only * GetMethod httpget = new GetMethod("/"); * client.executeMethod(httpget); * </pre> * </p> * <p> * Example of using custom protocol socket factory per default instead of the standard one: * <pre> * Protocol authhttps = new Protocol("https", * new AuthSSLProtocolSocketFactory( * new URL("file:my.keystore"), "mypassword", * new URL("file:my.truststore"), "mypassword"), 443); * Protocol.registerProtocol("https", authhttps); * * HttpClient client = new HttpClient(); * GetMethod httpget = new GetMethod("https://localhost/"); * client.executeMethod(httpget); * </pre> * </p> * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a> * * <p> * DISCLAIMER: HttpClient developers DO NOT actively support this component. * The component is provided as a reference material, which may be inappropriate * to be used without additional customization. * </p> */ public class AuthSSLProtocolSocketFactory implements SecureProtocolSocketFactory { private static final String USER_DECLINED = "User Declined to continue"; private static final String LINE_SEP = System.getProperty("line.separator"); /** Host name verify flag. */ private boolean verifyHostname = true; private boolean verified = false; private URL keystoreUrl = null; private String keystorePassword = null; private URL truststoreUrl = null; private String truststorePassword = null; private SSLContext sslcontext = null; /** * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file * must be given. Otherwise SSL context initialization error will result. * * @param keystoreUrl URL of the keystore file. May be <tt>null</tt> if HTTPS client * authentication is not to be used. * @param keystorePassword Password to unlock the keystore. IMPORTANT: this implementation * assumes that the same password is used to protect the key and the keystore itself. * @param truststoreUrl URL of the truststore file. May be <tt>null</tt> if HTTPS server * authentication is not to be used. * @param truststorePassword Password to unlock the truststore. * @deprecated by [EMAIL PROTECTED] #AuthSSLProtocolSocketFactory(CertificateManager)} */ public AuthSSLProtocolSocketFactory( final URL keystoreUrl, final String keystorePassword, final URL truststoreUrl, final String truststorePassword) { super(); this.keystoreUrl = keystoreUrl; this.keystorePassword = keystorePassword; this.truststoreUrl = truststoreUrl; this.truststorePassword = truststorePassword; } /** * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file * must be initialized in passed structure. Otherwise SSL context initialization error * will result. * * @param certManager * @throws MalformedURLException */ public AuthSSLProtocolSocketFactory(final CertificateManager certManager) throws MalformedURLException { super(); this.keystoreUrl = certManager.getKeyStoreURIPath(); this.keystorePassword = CertificateManager.KEYSTORE_PASSWORD; this.truststoreUrl = certManager.getTrustStoreURIPath(); this.truststorePassword = CertificateManager.TRUSTORE_PASSWORD; } private KeyStore createKeyStore(final URL url, final String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { if (url == null) { throw new IllegalArgumentException("Keystore url may not be null"); } IDebug.print("Initializing key store"); KeyStore keystore = KeyStore.getInstance("jks"); keystore.load(url.openStream(), password != null ? password.toCharArray(): null); return keystore; } private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { if (keystore == null) { throw new IllegalArgumentException("Keystore may not be null"); } IDebug.print("Initializing key manager"); KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(keystore, password != null ? password.toCharArray(): null); return kmfactory.getKeyManagers(); } private TrustManager[] createTrustManagers(KeyStore truststore, KeyStore keystore) throws KeyStoreException, NoSuchAlgorithmException { if (truststore == null) { throw new IllegalArgumentException("Trust Store may not be null"); } IDebug.print("Initializing trust manager"); TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmfactory.init(truststore); TrustManager[] trustmanagers = tmfactory.getTrustManagers(); for (int i = 0; i < trustmanagers.length; i++) { if (trustmanagers[i] instanceof X509TrustManager) { trustmanagers[i] = new AuthSSLX509TrustManager( truststore, keystore, keystoreUrl.getPath(), keystorePassword.toCharArray()); } } return trustmanagers; } private SSLContext createSSLContext() { try { KeyManager[] keymanagers = null; TrustManager[] trustmanagers = null; KeyStore keystore = null; if (this.keystoreUrl != null) { keystore = createKeyStore(this.keystoreUrl, this.keystorePassword); if (IDebug.isDebugEnabled()) { Enumeration aliases = keystore.aliases(); while (aliases.hasMoreElements()) { String alias = (String)aliases.nextElement(); Certificate[] certs = keystore.getCertificateChain(alias); if (certs != null) { IDebug.print("Certificate chain '" + alias + "':"); for (int c = 0; c < certs.length; c++) { if (certs[c] instanceof X509Certificate) { X509Certificate cert = (X509Certificate)certs[c]; IDebug.print(" Certificate " + (c + 1) + ":"); IDebug.print(" Subject DN: " + cert.getSubjectDN()); IDebug.print(" Signature Algorithm: " + cert.getSigAlgName()); IDebug.print(" Valid from: " + cert.getNotBefore() ); IDebug.print(" Valid until: " + cert.getNotAfter()); IDebug.print(" Issuer: " + cert.getIssuerDN()); } } } } } keymanagers = createKeyManagers(keystore, this.keystorePassword); } if (this.truststoreUrl != null) { KeyStore truststore = createKeyStore(this.truststoreUrl, this.truststorePassword); if (IDebug.isDebugEnabled()) { Enumeration aliases = truststore.aliases(); while (aliases.hasMoreElements()) { String alias = (String)aliases.nextElement(); IDebug.print("Trusted certificate '" + alias + "':"); Certificate trustedcert = truststore.getCertificate(alias); if (trustedcert != null && trustedcert instanceof X509Certificate) { X509Certificate cert = (X509Certificate)trustedcert; IDebug.print(" Subject DN: " + cert.getSubjectDN()); IDebug.print(" Signature Algorithm: " + cert.getSigAlgName()); IDebug.print(" Valid from: " + cert.getNotBefore() ); IDebug.print(" Valid until: " + cert.getNotAfter()); IDebug.print(" Issuer: " + cert.getIssuerDN()); } } } trustmanagers = createTrustManagers(truststore, keystore); } SSLContext sslcontext = SSLContext.getInstance("SSL"); //sslcontext.init(keymanagers, trustmanagers, new SecureRandom()); sslcontext.init(keymanagers, trustmanagers, null); return sslcontext; } catch (NoSuchAlgorithmException e) { IDebug.print(e); throw new AuthSSLInitializationError("Unsupported algorithm exception: " + e.getMessage()); } catch (KeyStoreException e) { IDebug.print(e); throw new AuthSSLInitializationError("Keystore exception: " + e.getMessage()); } catch (GeneralSecurityException e) { IDebug.print(e); throw new AuthSSLInitializationError("Key management exception: " + e.getMessage()); } catch (IOException e) { IDebug.print(e); throw new AuthSSLInitializationError("I/O error reading keystore/truststore file: " + e.getMessage()); } } private SSLContext getSSLContext() { if (this.sslcontext == null) { this.sslcontext = createSSLContext(); } return this.sslcontext; } /** * @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 { SSLContext sslc = createSSLContext(); SSLSocketFactory sslsf = sslc.getSocketFactory(); HttpsURLConnection.setDefaultSSLSocketFactory( sslsf ); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(host, port, clientHost, clientPort); verifyHostname(host, sslSocket); return sslSocket; } /** * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) */ public Socket createSocket(String host, int port) throws IOException, UnknownHostException { Socket socket = getSSLContext().getSocketFactory().createSocket(host, port); verifyHostname(host, (SSLSocket)socket); return socket; } /** * @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 { Socket newSocket = getSSLContext().getSocketFactory().createSocket( socket, host, port, autoClose); verifyHostname(host, (SSLSocket)newSocket); return newSocket; } /** * Attempts to get a new socket connection to the given host within the given time limit. * <p> * 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 [EMAIL PROTECTED] ConnectTimeoutException} * </p> * * @param host the host name/IP * @param port the port on the host * @param clientHost the local host name/IP to bind the socket to * @param clientPort the port on the local machine * @param params [EMAIL PROTECTED] 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(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); } int timeout = params.getConnectionTimeout(); if (timeout == 0) { return createSocket(host, port, localAddress, localPort); } else { // To be eventually deprecated when migrated to Java 1.4 or above SSLSocket sslSocket = (SSLSocket) ReflectionSocketFactory.createSocket( "javax.net.ssl.SSLSocketFactory", host, port, localAddress, localPort, timeout); if (sslSocket == null) { sslSocket = (SSLSocket) ControllerThreadSocketFactory.createSocket( this, host, port, localAddress, localPort, timeout); } verifyHostname(host, sslSocket); return sslSocket; } } /** * Describe <code>verifyHostname</code> method here. * * @param socket a <code>SSLSocket</code> 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. */ private void verifyHostname(String host, SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException { if ((!verifyHostname) || (verified)) { return; } SSLSession session = socket.getSession(); String hostname = session.getPeerHost(); try { InetAddress addr = InetAddress.getByName(hostname); } catch (UnknownHostException uhe) { throw new UnknownHostException("Could not resolve SSL sessions " + "server hostname: " + hostname); } javax.security.cert.X509Certificate[] certs = session.getPeerCertificateChain(); if (certs == null || certs.length == 0) throw new SSLPeerUnverifiedException("No server certificates found!"); //get the servers DN in its string representation String dn = certs[0].getSubjectDN().getName(); //might be useful to print out all certificates we receive from the //server, in case one has to debug a problem with the installed certs. //IDebug.print("Server certificate chain:"); //for (int i = 0; i < certs.length; i++) { //debug.print("X509Certificate[" + i + "]=" + certs[i]); //} //get the common name from the first cert String cn = getCN(dn); //mark verified if hostnames match if (hostname.equalsIgnoreCase(cn)) { verified = true; //IDebug.print("Target hostname valid: " + cn); } else { StringBuffer sb = new StringBuffer(); sb.append("The hostname on the server security certificate"); sb.append(LINE_SEP); sb.append("does not match the name of the server"); sb.append(LINE_SEP); sb.append(LINE_SEP); sb.append("Hostname of the URL: "); sb.append(hostname); sb.append(LINE_SEP); sb.append("Hostname from the certificate : "); sb.append(cn); sb.append(LINE_SEP); sb.append(LINE_SEP); sb.append("Do you want to proceed?"); sb.append(LINE_SEP); int returnValue = JOptionPane.showConfirmDialog( null, sb.toString(), "Hostname Mismatch", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (returnValue > 0) { throw new SSLPeerUnverifiedException(USER_DECLINED); } verified = true; } } /** * Parses a X.500 distinguished name for the value of the * "Common Name" field. * This is done a bit sloppy right now and should probably be done a bit * more according to <code>RFC 2253</code>. * * @param dn a X.500 distinguished name. * @return the value of the "Common Name" field. */ private String getCN(String dn) { int i = 0; i = dn.indexOf("CN="); if (i == -1) { return null; } //get the remaining DN without CN= dn = dn.substring(i + 3); // System.out.println("dn=" + dn); char[] dncs = dn.toCharArray(); for (i = 0; i < dncs.length; i++) { if (dncs[i] == ',' && i > 0 && dncs[i - 1] != '\\') { break; } } return dn.substring(0, i); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
