hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jim Scarborough (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (HTTPCLIENT-1585) Client certificate not submitted half the time
Date Thu, 04 Dec 2014 20:29:12 GMT

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

Jim Scarborough commented on HTTPCLIENT-1585:
---------------------------------------------

Oleg, thanks for taking a look.  This is a problem I am experiencing using HttpClient.  I
do not have the same problem using curl with the same server. I understand that the problem
might well be in sun.security.ssl, but I lack the expertise to isolate it from HttpClient.

> 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