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

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

I'm having trouble with getting Android to connect to a simple OpenSSL server using the HttpsUrlConnection object (I've combed through StackOverflow and a bunch of online tutorials, and followed the examples pretty much line for line and I still can't figure out why mine is broken when I use my local truststore).

I currently have an Android activity that attempts to connect to a simple OpenSSL server (I can connect to my server using a OpenSSL client), once the HttpsUrlConnection.connect() is called I receive a "javax.net.ssl.SSLException: Connection closed by peer" error during the SSL handshake. Perhaps I am setting up my sample server incorrectly?

Things to note:

  • no client authorization at the moment
  • am able to connect to https://www.google.com when loading default trust store
  • am not able to connect to server on localhost with self-signed certificate
  • do not want to trust all certificates
  • do not want to use Apache HttpClient
  • want to use local truststore only
  • created local truststore with bouncy castle
  • am able to correctly load truststore into
  • behind a proxy firewall, proxy is set on my android virtual device
  • AVD set to Android 4.1 API 16.

Things I have already tried:

  • connecting to both 127.0.0.1 and 10.0.2.2
  • using a new SecureRandom() with the SSLContext.init()
  • creating the URL with 'URL u = new URL("https", "10.0.2.2", 443, "/");'
  • using TrustManagerFactory.getDefaultAlgorithms() instead of the "X509"
    • gives "Unexpected response code error 503" instead of "Connection closed by peer"

Thank you in advance for taking the time to review my question!

Simple server started with command:

$ sudo openssl s_server -accept 443 -cert server-cert.pem -key server-key.pem -pass file:passphrase.txt -state -www -verify 0 

Client connection tested with command:

$ openssl s_client -connect 127.0.0.1:443  

Android activity code (edited to remove complete running code for simplification - please let me know if more code is needed) - error output is below the code.

    try {         TrustManagerFactory tmf;          // local trust store         tmf = TrustManagerFactory.getInstance("X509");         tmf.init(loadLocalKeyStore(getApplicationContext()));          // default trust store - works for https://www.google.com         // tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());         // tmf.init((KeyStore) null);          SSLContext context = SSLContext.getInstance("TLS");         context.init(null, tmf.getTrustManagers(), null);          HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;         URL u = new URL("https://10.0.2.2");          HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();          urlConnection.setSSLSocketFactory(context.getSocketFactory());         urlConnection.setHostnameVerifier(hostnameVerifier);         urlConnection.connect();          System.out.println("Response Code: " + urlConnection.getResponseCode());         System.out.println("Response Code: " + urlConnection.getCipherSuite());     }       ...      private KeyStore loadLocalKeyStore(Context context) {         InputStream in = context.getResources().openRawResource(R.raw.newserverkeystore);         KeyStore trusted = null;         try {            trusted = KeyStore.getInstance("BKS");            trusted.load(in, "thisisasecret".toCharArray());         } finally {            in.close();         }        return trusted;     } 

Output when connecting correctly to https://www.google.com:

09-09 21:58:09.947: I/System.out(669): Response Code: 200 09-09 21:58:09.947: I/System.out(669): Response Code: TLS_ECDHE_RSA_WITH_RC4_128_SHA 

Output when trying to connect to my server with self-signed certificate:

09-09 22:03:23.377: D/HttpsProxy(717): Https Request error 09-09 22:03:23.377: D/HttpsProxy(717): javax.net.ssl.SSLException: Connection closed by peer 09-09 22:03:23.377: D/HttpsProxy(717):  at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method) 09-09 22:03:23.377: D/HttpsProxy(717):  at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:395) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:442) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80) 09-09 22:03:23.377: D/HttpsProxy(717):  at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:165) 09-09 22:03:23.377: D/HttpsProxy(717):  at com.example.myfirstapp.HttpsUrlConnectionActivity$3.doInBackground(HttpsUrlConnectionActivity.java:257) 09-09 22:03:23.377: D/HttpsProxy(717):  at com.example.myfirstapp.HttpsUrlConnectionActivity$3.doInBackground(HttpsUrlConnectionActivity.java:1) 09-09 22:03:23.377: D/HttpsProxy(717):  at android.os.AsyncTask$2.call(AsyncTask.java:287) 09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.FutureTask.run(FutureTask.java:137) 09-09 22:03:23.377: D/HttpsProxy(717):  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) 09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 09-09 22:03:23.377: D/HttpsProxy(717):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 09-09 22:03:23.377: D/HttpsProxy(717):  at java.lang.Thread.run(Thread.java:856) 

Thanks again!!

回答1:

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; } 


回答2:

I wasted my 6 - 7 hours fixed this problem and finally it worked with

public void URLConnection(String webUrl) throws IOException, NoSuchAlgorithmException, KeyManagementException {         //TLSSocketFactory objTlsSocketFactory = new TLSSocketFactory();         URL url = new URL(webUrl);         HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();         urlConnection.setRequestMethod("GET");         //urlConnection.setSSLSocketFactory(objTlsSocketFactory);          int responseCode = urlConnection.getResponseCode();         System.out.println("\nSending 'GET' request to URL : " + url);         System.out.println("Response Code : " + responseCode);          BufferedReader in = new BufferedReader(                 new InputStreamReader(urlConnection.getInputStream()));         String inputLine;         StringBuffer response = new StringBuffer();          while ((inputLine = in.readLine()) != null) {             response.append(inputLine);         }         in.close();          //print result         System.out.println(response.toString());     } 

And it worked !!!!!!



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!