hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Oleg Kalnichevski (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (HTTPCLIENT-1585) Client certificate not submitted half the time
Date Tue, 09 Dec 2014 08:57:12 GMT

    [ https://issues.apache.org/jira/browse/HTTPCLIENT-1585?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14239151#comment-14239151
] 

Oleg Kalnichevski commented on HTTPCLIENT-1585:
-----------------------------------------------

I fixed the test case and raised a PR at Github. The issue is essentially caused by a combination
of several factors
(1) What looks to me like a mess with key / trust material in the test case. 
(2) Quirkiness of the JSSE APIs. While SSLContext#init can accept multiple KeyManager instances
the default JSSE provider actually only makes use of the very first one while silently ignoring
the rest. 
(3) SSLContextBuilder using plain HashSet to keep track of KeyManager instances, which results
in unstable ordering when those KeyManager instances are passed to SSLContext#init causing
SSLContext to pick up a different instance of KeyManager. 

Oleg      

> Client certificate not submitted half the time
> ----------------------------------------------
>
>                 Key: HTTPCLIENT-1585
>                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-1585
>             Project: HttpComponents HttpClient
>          Issue Type: Bug
>          Components: HttpClient
>    Affects Versions: 4.3.5, 5.0
>         Environment: Fedora 20 on a Thinkpad T540p, Sun JDK 1.6,1.7,1.8
>            Reporter: Jim Scarborough
>              Labels: clientcertificate
>         Attachments: sbs
>
>
> h1. Overview
> The http client submits the client certificate when requested about half the time.  This
may well be a bug in the underlying SSLSocketImpl or nearby code.  I welcome assistance in
determining which layer has the bug.
> h1. Stack trace
> {code:none}
> javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
> 	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
> 	at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
> 	at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1959)
> 	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
> 	at sun.security.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1705)
> 	at sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:122)
> 	at sun.security.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:982)
> 	at sun.security.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:1154)
> 	at sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1066)
> 	at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:341)
> 	at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878)
> 	at sun.security.ssl.Handshaker.process_record(Handshaker.java:814)
> 	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
> 	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
> 	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
> 	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
> 	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:275)
> 	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:254)
> 	at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:123)
> 	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:318)
> 	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
> 	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
> 	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
> 	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
> 	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
> 	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
> 	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
> 	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
> {code}
> h1. Steps to reproduce
> h2. 1. Set up certificates
> create server key/cert pair. use FQDN as First Last name
> {{keytool -keystore server_keystore -alias server -genkey -keyalg RSA}}
>   
> export server cert to a curl usable format.
> {{keytool -exportcert -alias server -keystore server_keystore -rfc -file server.pem}}
>  
> create client key/cert pair. use email as First Last name
> {{keytool -keystore client_keystore -alias client -genkey -keyalg RSA}}
>  
>  export client key/cert pair to a firefox usablabe format.
> {{keytool -importkeystore -srckeystore client_keystore -destkeystore client_keystore.pkcs12
-srcstoretype JKS -deststoretype PKCS12 -srcstorepass password -deststorepass password -srcalias
client -destalias client -srckeypass password -destkeypass password}}
>  
>  export client key/cert pair to a curl usable format.
> {{openssl pkcs12 -in client_keystore.pkcs12 -out client.crt.pem -clcerts -nokeys}}
> {{openssl pkcs12 -in client_keystore.pkcs12 -out client.key.pem -nocerts -nodes}}
>  
> h2. 2. Set up Jetty, configure it to require a client cert
>  
> {{vim jetty-ssl.xml}}
>  
> {code:xml}
>    <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property
name="jetty.keystore" default="etc/server_keystore"/></Set>
>    <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="password"/></Set>
>    <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password"
default="password"/></Set>
>    <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property
name="jetty.truststore" default="etc/client_keystore"/></Set>
>    <Set name="TrustStorePassword"><Property name="jetty.truststore.password"
default="password"/></Set>
>    <Set name="NeedClientAuth"><Property name="jetty.ssl.needClientAuth" default="true"/></Set>
> {code}
> {{vim jetty-https.xml}}
>  
> {code:xml}
>     <Set name="port"><Property name="https.port" default="8443" /></Set>
> {code}
> {{vim start.ini}}
>  
> {code:none}
>     jetty.dump.stop=false
>     etc/jetty-ssl.xml
>     etc/jetty-https.xml
> {code}
> h2. 3. Verify Jetty works
>  
> Copy the exported certs to your machine. 
>  
> Go to https://FQDN:8443 in the browser with and without the cert to make sure it works
>  
> or use curl
>  
> curl -L -v --cacert server.pem -E ./client.crt.pem --key ./client.key.pem https://localhost:8443
> h2. 4. Run this code against it:
> {code:title=HttpsClientCertificateTest.java|borderStyle=solid}
> import javax.net.ssl.SSLHandshakeException;
> import javax.net.ssl.SSLParameters;
> import javax.net.ssl.SSLSocket;
> import javax.net.ssl.SSLSocketFactory;
> import java.net.Socket;
> import com.google.common.io.Resources;
> import junit.framework.AssertionFailedError;
> import org.junit.Test;
> import static org.junit.Assert.*;
> public class HttpsClientCertificateTest {
>     @Test
>     public void testClientCertificate() throws Exception {
>         int success=0;
>         int failure=0;
>         for (int i=0;i<40;i++) {
>             try {
>                 String testKeystorePath = Resources.getResource("test-keystore").toString();
>                 String testClientCertPath = Resources.getResource("test-clientstore").toString();
>                 assertEquals("protected\n",secureContentFor("https://localhost:8443/",
testKeystorePath, testClientCertPath));
>                 success++;
>             } catch (AssertionFailedError e) {
>                 e.printStackTrace();
>                 failure++;
>             } catch (SSLHandshakeException e) {
>                 e.printStackTrace();
>                 failure++;
>             }
>         }
>         assertEquals("failed " + failure + "/" + (success+failure) + " succeeded " +
(success*100/(success+failure)) + "%",0,failure);
>     }
>     static String secureContentFor(String url, String clientKeyStore, String clientTrustStore)
throws Exception {
>         KeyStore trustStore = readKeyStore(clientTrustStore);
>         KeyStore keyStore = readKeyStore(clientKeyStore);
>         // Trust own CA and all self-signed certs
>         SSLContext sslcontext = SSLContexts.custom()
>                 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
>                 .loadKeyMaterial(keyStore, "password".toCharArray())
>                 .loadKeyMaterial(trustStore, "password".toCharArray())
>                 .useTLS()
>                 .build();
>         // Allow TLSv1 protocol only
>         SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
>                 sslcontext,
>                 new String[] { "TLSv1" }, // supported protocols
>                 null,  // supported cipher suites
>                 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
>         CloseableHttpClient httpClient = HttpClients.custom()
>                 .setSSLSocketFactory(sslsf)
>                 .build();
>         HttpGet get = new HttpGet(url);
>         HttpResponse response = httpClient.execute(get);
>         String content = EntityUtils.toString(response.getEntity());
>         return content;
>     }
>     static KeyStore readKeyStore(String resourceURL) throws KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException {
>         KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
>         FileInputStream instream = new FileInputStream(new URL(resourceURL).getFile());
>         try {
>             trustStore.load(instream, "password".toCharArray());
>         } finally {
>             instream.close();
>         }
>         return trustStore;
>     }
> }
> {code}
> h1. Actual results
> Exceptions as noted above, and some ultimate failure note like this:
> java.lang.AssertionError: failed 19/40 succeeded 52% 
> Expected :0
> Actual   :19
> h2. Expected results
> No exceptions, test passes



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org


Mime
View raw message