2013-10-24 78 views
1

我想了解Android到服務器的TLS連接。任何人都可以糾正我嗎?Android TLS握手

有兩種啓動TLS連接的方式。首先,只有服務器有證書,客戶決定是否信任它。其次,客戶端和服務器都獲得了證書。我對嗎?

如何爲Android設備上的TLS連接生成自定義唯一證書並將其用於連接到服務器?我發現只有第一種連接的實現。

任何人都可以幫助我嗎?

+0

雖然不完全重複,但您可以查看我關於使用客戶端證書的問題,在這裏:http://stackoverflow.com/questions/24406266/creating-an-https-connecion-with-client-side-certificate - 從-PKCS10與 - 海棉 – Felix

回答

2

要通過TLS實現雙方身份驗證,您需要在服務器和客戶端擁有密鑰庫。

我通常使用Portecle工具來創建密鑰庫,這是一個基於GUI的工具,非常易於使用。您可以在portecle.sourceforge.net上下載它

您需要使用該工具爲服務器端創建JKS格式的密鑰庫,併爲客戶端創建另一個BKS格式的密鑰庫。格式不同,因爲Android本身不支持使用JKS密鑰庫,僅支持BKS,反之亦然。

一旦你創建了兩個密鑰庫,你需要爲它們中的每一個生成一個密鑰對(公鑰和私鑰)。 Portecle在'Tools'子菜單下的工具欄上有一個按鈕來執行此操作。常見的密鑰算法和大小是RSA 2048位。之後,您將需要爲證書設置幾個參數,如組織unti,名稱,地點等。

現在您有兩個密鑰庫和兩個密鑰對。

要允許客戶端解密服務器接收的消息,必須爲客戶端密鑰庫提供服務器密鑰庫的公鑰。要做到這一點,只需右鍵單擊服務器密鑰庫上的密鑰對,然後單擊「導出」選項。然後選擇「證書鏈」並在文件系統上選擇一個位置來存儲證書。

現在您已準備好將公鑰導入客戶端密鑰庫中。點擊'工具'工具欄上的'導入可信證書',然後查找上一步導出的證書文件。一些警報消息將出現,表明您的信任路徑無法建立,現在不擔心。

因此,現在您擁有密鑰對和服務器證書的客戶端密鑰庫。這在客戶端就足夠了。

現在,有必要將客戶端密鑰對導入服務器密鑰庫。在客戶端密鑰庫上,右鍵單擊密鑰對並選擇「導出」。在打開的彈出窗口中選擇「私鑰和證書」,並選擇PKCS#12格式。

然後,打開服務器密鑰庫並使用「工具」工具欄的「導入密鑰對」子菜單,然後選擇在上一步中導出的密鑰對。

請記住,保存BKS格式的客戶機密鑰庫和JKS格式的服務器密鑰庫非常重要。

好的,這就是密鑰倉庫,現在是時候編碼了。

讓我們從服務器代碼開始。下一個片段是從我運行的Java Spring項目中提取的。它是一個嵌入式的tomcat,所以配置是純java的,很容易找到如何在傳統的tomcat配置上配置ssl連接器,所以我只會放入嵌入式版本。

private Connector createSslConnector() { 

//print the client keystore 
printClientKeystore(); 

Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); 
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); 
try { 

    //read the keystore from the jar 
    //and write it to a tmp file 
    Resource keystoreResource = context.getResource("classpath:config/server.keystore"); 
    byte[] keystoreData = readKeystore(keystoreResource.getInputStream()); 
    File tmpKeystoreFile = File.createTempFile("keystore", ""); 
    writeKeystore(tmpKeystoreFile, keystoreData); 

    //keystore information 
    final String keystoreFile = tmpKeystoreFile.getAbsolutePath(); 
    final String keystorePass = "yourKeystorePass"; 
    final String keystoreType = "pkcs12"; 
    final String keystoreProvider = "SunJSSE"; 
    final String keystoreAlias = "comics_tomcat"; 

    connector.setScheme("https"); 
    connector.setAttribute("clientAuth", "true"); 
    connector.setPort(HTTPS_PORT); 
    connector.setSecure(true); 
    protocol.setSSLEnabled(true); 

    //keystore 
    protocol.setKeystoreFile(keystoreFile); 
    protocol.setKeystorePass(keystorePass); 
    protocol.setKeystoreType(keystoreType); 
    protocol.setProperty("keystoreProvider", keystoreProvider); 
    protocol.setKeyAlias(keystoreAlias); 

    //truststore 
    protocol.setTruststoreFile(keystoreFile); 
    protocol.setTruststorePass(keystorePass); 

    protocol.setPort(HTTPS_PORT); 

    return connector; 
} 
catch (IOException e) { 
    LOGGER.error(e.getMessage(), e); 
    throw new IllegalStateException("cant access keystore: [" + "keystore" 
      + "] or truststore: [" + "keystore" + "]", e); 
} 
} 

這是所有在服務器端,通過使用「setSecure(真)」的方法在真與安全標誌的SSL連接器。這允許您使用私鑰/公鑰驗證來要求用戶驗證。

以下代碼用於客戶端,它加載密鑰庫,信任庫(通過使用相同的密鑰庫),配置主機名驗證程序以允許連接到特定域,然後打開連接。

  KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 

      //load keystore stream 
      byte[] keystoreData = readInputStream(getAssets().open("client.keystore")); 

      //load keystore 
      ByteArrayInputStream bais = new ByteArrayInputStream(keystoreData); 
      keyStore.load(bais, KEYSTORE_PASSWORD.toCharArray()); 
      //load truststore 
      bais = new ByteArrayInputStream(keystoreData); 
      trustStore.load(bais, KEYSTORE_PASSWORD.toCharArray()); 
      //load trustmanager 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
      tmf.init(trustStore); 
      //init keymanager 
      KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
      kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray()); 
      //create ssl context 
      sslContext = SSLContext.getInstance("TLS"); 
      sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 


HostnameVerifier HOSTNAME_VERIFIER = new HostnameVerifier() { 
     @Override 
     public boolean verify(String hostname, SSLSession session) { 
      List<String> allowedHostnames = new ArrayList<String>(); 
      allowedHostnames.add("pinterest.com"); 
      allowedHostnames.add("192.168.1.43"); 
      allowedHostnames.add("10.0.2.2"); 
      return allowedHostnames.indexOf(hostname) != -1; 
     } 
    }; 

        //open https connection 
        URL url = new URL("https://" + SERVER_URL + ":" + SERVER_PORT + "/api/v1/publication/getDescriptor/" + publicationId); 
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); 
        urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); 
        urlConnection.setHostnameVerifier(HOSTNAME_VERIFIER); 

        //read server response 
        byte[] serverResult = readInputStream(urlConnection.getInputStream()); 

讓我知道如果您有任何問題!