Java – Mutual authentication failed on Android w/javax.net.ssl.SSLHandshakeException : Handshake failed

Mutual authentication failed on Android w/javax.net.ssl.SSLHandshakeException : Handshake failed… here is a solution to the problem.

Mutual authentication failed on Android w/javax.net.ssl.SSLHandshakeException : Handshake failed

I’m trying to get a mutual authentication request that works on android. I’m testing against my own server, so I have a self-signed CA and client certificate.

So I will have to allow untrusted server certificates.

Here’s what I’m doing :

KeyStore clientCertificate = KeyStore.getInstance("PKCS12");
InputStream client_inputStream = getResources().openRawResource(R.raw.client);
clientCertificate.load(client_inputStream, "password".toCharArray());
new SSLRequest(clientCertificate).execute();

Then AsyncTask goes to execute the request:

class SSLRequest extends AsyncTask<Void, Void, Void> {

private Exception exception;
    private KeyStore clientCertificate;

public SSLRequest(KeyStore clientCertificate) {
        this.clientCertificate = clientCertificate;
    }

protected Void doInBackground(Void... params) {
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);
            SSLSocketFactory sf = new SSLSocketFactory(clientCertificate, null, trustStore);

HttpParams httpParameters = new BasicHttpParams();
            SchemeRegistry schemeRegistry = new SchemeRegistry();
            schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            schemeRegistry.register(new Scheme("https", sf, 443));
            ClientConnectionManager cm = new SingleClientConnManager(httpParameters, schemeRegistry);

DefaultHttpClient httpClient = new DefaultHttpClient(cm, httpParameters);

HttpGet request = new HttpGet("https://myserver.com/some.json");
            HttpResponse response = httpClient.execute(request);
            response.getEntity().consumeContent();
        } catch (Exception e) {
            this.exception = e;
        }

return null;
    }

protected void onPostExecute(Void params) {
        if (exception != null) {
            status.setText("Error making SSL handshake");
        } else {
            status.setText("Successful SSL handshake");
        }
    }
}

I’ve tested this request in my browser and iOS client, but I can’t get it to work in Android.

I think this is the right way to allow untrusted server certificates :

KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new SSLSocketFactory(clientCertificate, "password", trustStore);

Not sure why I get :

javax.net.ssl.SSLPeerUnverifiedException

Edit:

I have to add my own SSLSocketFactory to trust the self-signed server certificate:

public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

public MySSLSocketFactory(KeyStore keystore, String keystorePassword, KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(keystore, keystorePassword, truststore);

TrustManager trustManager = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

sslContext.init(null, new TrustManager[] {trustManager}, null);
    }

@Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

@Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

Once I use it, I get the following information:

javax.net.ssl.SSLHandshakeException: Handshake failed

Edit 2:

I use apache on Lollipop as my web server. My Apache web server is configured with:

#   SSL Protocol support:
# List the enable protocol levels with which clients will be able to
# connect.  Disable SSLv2 access by default:
SSLProtocol all -SSLv2 -SSLv3

#   SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate.
# See the mod_ssl documentation for a complete list.
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL ! LOW !3DES ! MD5 ! EXP ! PSK ! SRP ! DSS"

This may be my problem:
HttpClient fails with Handshake Failed in Android 5.0 Lollipop

Not quite sure what I need to change. I looked at everything and this seems to be the recommended configuration (for now).

Edit 3:

Here is the full stack trace:

06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ javax.net.ssl.SSLHandshakeException: Handshake failed
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:390)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:623)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:585)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.io.SocketInputBuffer.<init>(SocketInputBuffer.java:75)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer( SocketHttpClientConnection.java:88)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer( DefaultClientConnection.java:175)
06-01 13:23:14.369  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:111)
06-01 13:23:14.371  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:134)
06-01 13:23:14.371  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection( DefaultClientConnectionOperator.java:177)
06-01 13:23:14.371  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
06-01 13:23:14.371  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:365)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:470)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at mil.nga.giat.handshake.HandshakeActivity$SSLRequest.doInBackground(HandshakeActivity.java:115)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at mil.nga.giat.handshake.HandshakeActivity$SSLRequest.doInBackground(HandshakeActivity.java:85)
06-01 13:23:14.372  24486-24571/mil.nga.giat.handshake W/System.err﹕ at android.os.AsyncTask$2.call(AsyncTask.java:292)
06-01 13:23:14.373  24486-24571/mil.nga.giat.handshake W/System.err﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:237)
06-01 13:23:14.373  24486-24571/mil.nga.giat.handshake W/System.err﹕ at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
06-01 13:23:14.373  24486-24571/mil.nga.giat.handshake W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
06-01 13:23:14.373  24486-24571/mil.nga.giat.handshake W/System.err﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
06-01 13:23:14.374  24486-24571/mil.nga.giat.handshake W/System.err﹕ at java.lang.Thread.run(Thread.java:818)
06-01 13:23:14.374  24486-24571/mil.nga.giat.handshake W/System.err﹕ Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0xa21b8800: Failure in SSL library, usually a protocol error
06-01 13:23:14.374  24486-24571/mil.nga.giat.handshake W/System.err﹕ error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure (external/openssl/ssl/s3_pkt.c :1303 0xb4b57be0:0x00000003)
06-01 13:23:14.375  24486-24571/mil.nga.giat.handshake W/System.err﹕ at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
06-01 13:23:14.375  24486-24571/mil.nga.giat.handshake W/System.err﹕ at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:318)

Solution

I never put a client certificate in KeyManager:

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
    kmf.init(keystore, "password".toCharArray());
    sslContext.init(kmf.getKeyManagers(), new TrustManager[]{tm}, null);

Related Problems and Solutions