Android HttpsUrlConnection javax.net.ssl.SSLException当使用本地信任库时,由对等握手错误closures的连接

我在使用HttpsUrlConnection对象获取Android连接到一个简单的OpenSSL服务器时遇到了麻烦(我已经通过StackOverflow和一些在线教程进行了梳理,并且非常HttpsUrlConnection线路的示例,我仍然无法弄清楚为什么当我使用我的本地信任库时我的信息被破坏了)。

我目前有一个Android活动,尝试连接到一个简单的OpenSSL server (我可以使用OpenSSL客户端连接到我的服务器),一旦HttpsUrlConnection.connect()被调用,我收到一个“ javax.net.ssl.SSLException: Connection closed by peer" error during the SSL handshake. 也许我正在设置我的示例服务器不正确?

注意事项:

  • 目前没有客户授权
  • 加载默认信任存储时,能够连接到https://www.google.com
  • 无法使用自签名证书连接到本地主机上的服务器
  • 不想相信所有的证书
  • 不想使用Apache HttpClient
  • 只想使用本地信任库
  • 与充气城堡创build当地的信任存储
  • 我能够正确加载信任库
  • 在代理防火墙后面,在我的android虚拟设备上设置代理
  • AVD设置为Android 4.1 API 16

我已经尝试过的事情:

  • 连接到127.0.0.1 and 10.0.2.2
  • 使用新的SecureRandom() with the SSLContext.init()
  • 使用'URL u = new URL("https", "10.0.2.2", 443, "/");'创buildURL 'URL u = new URL("https", "10.0.2.2", 443, "/");'
  • 使用TrustManagerFactory.getDefaultAlgorithms()而不是“X509”
    • 给出"Unexpected response code error 503"而不是“由对等closures的连接”

提前感谢您花时间审查我的问题!

简单的服务器启动命令:

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

客户端连接testing命令:

 $ openssl s_client -connect 127.0.0.1:443 

Android活动代码(编辑删除完整的运行代码简化 – 请让我知道是否需要更多的代码) – 错误输出低于代码。

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

正确连接到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 

尝试使用自签名证书连接到我的服务器时输出:

 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) 

再次感谢!!

我解决了我的问题 – 我需要使用10.0.2.2作为通用名称(CN)的证书,所以它匹配10.0.2.2的Android本地主机IP地址,而不是“本地主机”或“127.0.0.1”。

编辑:你可能可以创build一个证书与本地主机CN和'127.0.0.1'和'10 .0.2.2'作为主题替代名称(SAN)。

一旦我创build了10.0.2.2证书和私钥pem文件,我就可以使用以下命令打开我的服务器:

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

如果您想要强制客户端提供证书(虽然不会被检查),那么在上面的命令中添加-Verify 1标志。

要在命令行testing服务器,可以使用以下命令(注意openssl能够通过127.0.0.1连接):

 openssl s_client -connect 127.0.0.1:8888 

如果服务器需要添加客户端证书,请添加标志-cert client-cert.pem -key client-key.pem

在我的Android客户端,我用下面的代码连接(错误检查删除):

 // 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()); 

你应该得到一个200的响应代码,并且可以从那里parsing响应。

以下是加载客户端凭据的代码,与加载服务器密钥库相同,但是具有不同的资源文件名和密码:

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

我浪费了我的6-7小时,解决了这个问题,最后还是有效的

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

它工作!!!!!!