Android HttpsUrlConnection javax.net.ssl.SSLException Connection closed by peer handshake error when using local truststore

后端 未结 2 1344
耶瑟儿~
耶瑟儿~ 2020-12-23 23:19

I\'m having trouble with getting Android to connect to a simple OpenSSL server using the HttpsUrlConnection object (I\'ve combed through StackOverf

2条回答
  •  萌比男神i
    2020-12-23 23:54

    I solved my problem - I needed to use a certificate with 10.0.2.2 as the common name (CN) so it matched Android localhost ip address of 10.0.2.2 instead of 'localhost' or '127.0.0.1'.

    Edit: you could probably create a certificate with localhost as the CN and '127.0.0.1' and '10.0.2.2' as Subject Alternative Names (SAN).

    Once I created 10.0.2.2 cert and private key pem files, I was able to hit my server running with the following command:

    openssl s_server -accept 8888 -cert 10.0.2.2-cert.pem -key 10.0.2.2-key.pem  -state -www
    

    If you want to force the client to provide a certificate (though it won't be checked), add the flag -Verify 1 to the command above.

    To test the server at the command line you can use the following (note openssl is able to connect via 127.0.0.1):

    openssl s_client -connect 127.0.0.1:8888
    

    And to add a client cert if the server requires it, add the flags -cert client-cert.pem -key client-key.pem

    In my Android client I used the following code to connect (error checking removed):

    // use local trust store (CA)
    TrustManagerFactory tmf;
    KeyStore trustedStore = null;
    InputStream in = context.getResources().openRawResource(R.raw.mycatruststore); // BKS in res/raw
    trustedStore = KeyStore.getInstance("BKS");
    trustedStore.load(in, "insertBksPasswordHere".toCharArray());
    tmf = TrustManagerFactory.getInstance("X509");
    tmf.init(trustedStore);
    
    // load client certificate
    KeyStore clientKeyStore = loadClientKeyStore(getApplicationContext());
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
    kmf.init(clientKeyStore, "insertPasswordHere".toCharArray());
    
    SSLContext context = SSLContext.getInstance("TLS");
    
    // provide client cert - if server requires client cert this will pass
    context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
    
    // connect to url
    URL u = new URL("https://10.0.2.2:8888/");
    HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());
    urlConnection.setHostnameVerifier(hostnameVerifier);
    urlConnection.connect();
    System.out.println("Response Code: " + urlConnection.getResponseCode());
    

    You should get a response code of 200, and can dissect the response from there.

    Here's the code to load the client credentials, which is identical to loading the server key store but with a different resource filename and password:

    private KeyStore loadClientKeyStore(Context context) {
        InputStream in = context.getResources().openRawResource(R.yourKeyStoreFile);
        KeyStore trusted = null;
        trusted = KeyStore.getInstance("BKS");
        trusted.load(in, "yourClientPassword".toCharArray());
        in.close();
        return trusted;
    }
    

提交回复
热议问题