2013-10-24 77 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()); 

让我知道如果您有任何问题!