2012-09-10 31 views
8

我在使用HttpsUrlConnection對象將Android連接到簡單的OpenSSL服務器時遇到問題(我梳理了StackOverflow和a大量的在線教程,並遵循很多線路的例子,我仍然無法弄清楚爲什麼當我使用我的本地信任庫時我的資源被破壞)。Android HttpsUrlConnection javax.net.ssl.SSLException當使用本地信任庫時,由對等方握手錯誤關閉的連接

目前,我有試圖連接到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
  • 要使用本地信任只
  • 創建的本地與充氣城堡
  • 時能夠正確地信任加載到
  • 代理防火牆後面的信任庫,代理服務器設爲我的Android虛擬設備
  • AVD設置爲上。

事情我已經嘗試:

  • 連接到兩個127.0.0.1 and 10.0.2.2
  • 使用新SecureRandom() with the SSLContext.init()
  • 創建'URL u = new URL("https", "10.0.2.2", 443, "/");'
  • 使用TrustManagerFactory.getDefaultAlgorithms()而不是 「X509」
      與網址
    • 改爲"Unexpected response code error 503"
  • 「對方將連接關閉」

預先感謝您在百忙之中閱讀我的問題的時候了!

簡單的服務器開始使用命令:

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

與命令測試客戶端連接:

$ openssl s_client -connect 127.0.0.1:443 

Android的活動代碼(編輯以刪除完整的運行代碼簡化 - 請讓我知道如果有更多的代碼是必要的) - 錯誤輸出低於代碼。試圖用自簽名證書連接到我的服務器時

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 

輸出:正確連接到https://www.google.com

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

再次輸出

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) 

謝謝!

+0

我忘記提及當我嘗試使用Android模擬器連接時,只有使用我的OpenSSL客戶端時,我沒有看到任何服務器活動。 – aspergillusOryzae

+0

非常難以格式化這個問題;-) –

+0

對不起,有沒有一種方法可以讓我們在投票問題之前更容易理解?我正在嘗試解釋我在重新定向到他們之前從其他帖子中採取的方法。 – aspergillusOryzae

回答

6

我解決了我的問題 - 我需要使用10.0.2.2作爲通用名稱(CN)的證書,所以它匹配10.0.2.2而不是'localhost'或'127.0.0.1'的Android本地主機IP地址。

編輯:您可以創建一個證書,其中localhost作爲CN,'127.0.0.1'和'10 .0.2.2'作爲主題備用名稱(SAN)。

一旦我創建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到上面的命令。

爲了你可以使用下面的命令行測試服務器(注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()); 

你應該得到的2響應代碼00,並且可以從那裏解析響應。

下面是加載客戶端證書的代碼,這是相同的加載服務器密鑰存儲,但使用不同的資源文件名和密碼:

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

我浪費了我的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()); 
    } 

它的工作!!!!!!

相關問題