0

I'm trying to implement client and server authentication with Apache HTTPClient, and to test it with a self signed certificate. I've tried to follow several tutorials and answers to similar questions here but with no success. I've tried to detail as much as possible all the steps that I've been doing, hopefully someone can point out what I'm doing wrong:

  1. Created a file req.conf for configuration

    [req]
    prompt=no
    distinguished_name = req_distinguished_name
    
    [ req_distinguished_name ]
    O=selfSignedO
    CN=selfSignedCn
    DC=selfSignedDc
    
  2. Generated server private key and the self-signed certificate

    openssl req \
     -config req.conf \
     -x509 \
     -newkey rsa:4096 \
     -keyout server/server-private-key.pem \
     -out server/server.crt \
     -days 3650 \
     -nodes 
    
  3. Created PKCS12 keystore containing the private key and certificate created in the previous step

     openssl pkcs12 \
     -export \
     -out server/server-key-store.p12 \
     -inkey server/server-private-key.pem \
     -in server/server.crt
    

    let's say the password I used was 123456

  4. Generated a client private key and a certificate signing request

    openssl req \
     -config req.conf \
     -new \
     -newkey rsa:4096 \
     -out client/client-request.csr \
     -keyout client/client-private-key.pem \
     -nodes
    
  5. Signed the client's certificate signing request with the server's private key and certificate

    openssl x509 \
     -req \
     -days 360 \
     -in client/client-request.csr \
     -CA server/server.crt \
     -CAkey server/server-private-key.pem \
     -CAcreateserial \
     -out client/client-signed-cert.crt \
     -sha256
    
  6. Created a PKCS12 keystore containing the client's private key and certificate certificate created in the previous step.

     openssl pkcs12 \
      -export \
      -out client/client-keystore.p12 \
      -inkey client/client-private-key.pem \
      -in client/client-signed-cert.crt \
      -certfile server/server.crt
    

    we used 123456 as password again.

  7. Generated server trust store containing the client signed certificate

    keytool \
     -import \
     -trustcacerts \
     -alias root \
     -file client/client-signed-cert.crt \
     -keystore server/server-trust-store.jks
    

    password? 123456

  8. Curl is working, but only with -k

    curl -k \
       --cert client/client-signed-cert.crt \
       --key client/client-private-key.pem \
       https://localhost:443:/my/endpoint
    

    without the -k I get the error:

    curl: (60) SSL certificate problem: self signed certificate
    More details here: https://curl.haxx.se/docs/sslcerts.html
    
    curl performs SSL certificate verification by default, using a "bundle"
     of Certificate Authority (CA) public keys (CA certs). If the default
     bundle file isn't adequate, you can specify an alternate file
     using the --cacert option.
    If this HTTPS server uses a certificate signed by a CA represented in
     the bundle, the certificate verification probably failed due to a
     problem with the certificate (it might be expired, or the name might
     not match the domain name in the URL).
    If you'd like to turn off curl's verification of the certificate, use
     the -k (or --insecure) option.
    HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
    
  9. Configured Apache HTTPClient:

    private HttpClient createClient() throws Exception {
        String keyPassword = "123456";
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(resourceAsStream("/client/client-key-store.p12"), keyPassword.toCharArray());
        SSLContext sslContext = new SSLContextBuilder()
            .setProtocol("TLSv1.2")
            .loadKeyMaterial(ks, keyPassword.toCharArray())
            .loadTrustMaterial(null, new TrustSelfSignedStrategy())
            .build()
        return HttpClients.custom()
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(new NoopHostnameVerifier())
                .build();
    }
    

    (The construction is done via multiple methods that I squeezed here to one, so if something is weird or missing please let me know, perhaps I miscopy-pasted something.)

but when trying to send the same request as with Curl I'm getting:

Caused by: 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:2038)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1135)
    at sun.security.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1779)
    at sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:124)
    at sun.security.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:1156)
    at sun.security.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:1266)
    at sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1178)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:348)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
apines
  • 1,254
  • 14
  • 36
  • You might have to check key-usage of your cert. – beat Feb 15 '19 at 09:14
  • @beat - can you please elaborate? – apines Feb 15 '19 at 09:18
  • When creating with OpenSSL you might need to set the clientAuth keyusage, see this question: https://stackoverflow.com/questions/17089889/openssl-x509v3-extended-key-usage – beat Feb 15 '19 at 09:41
  • Also: try to enable debugging with -Djavax.net.debug=all, which might give you some hints – beat Feb 15 '19 at 09:44
  • tried adding the `keyUsage` and `extendedKeyUsage` to `req.conf` - getting the same error. Regarding adding the debug - I'm getting a lot of information, not sure where to start and look, the bottom line is `main, RECV TLSv1.2 ALERT: fatal, bad_certificate` – apines Feb 15 '19 at 09:59

0 Answers0