android - 使用 Httpsurlconnection 连接到服务器 (SSL)

标签 android ssl lets-encrypt httpsurlconnection

我试图从自签名证书切换到众所周知的 CA(通过 Let's Encrypted)。下面的原始代码可以很好地通过首先传递 JSON 例如从 Android 应用程序连接到服务器。 {"name": "xxx"},同时服务端回显相应的数据。

 try {

      URL url = new URL(intent.getStringExtra(URL));
      HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
      conn.setSSLSocketFactory(buildSslSocketFactory());
      conn.setConnectTimeout(15000);
      conn.setRequestMethod("POST");
      conn.setDoInput(true);
      conn.setDoOutput(true);
      conn.setChunkedStreamingMode(0);
      conn.setRequestProperty("Accept", "application/json");
      conn.setRequestProperty("Content-Type", "application/json");
      conn.connect();
      OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
      Log.d(TAG,data +"sending to " + actStr(action) );
      writer.write(data);
      writer.close();
      int respC = conn.getResponseCode();
      if (respC < 200 || respC >= 400 ) { // OK is 200-399
          Log.e(TAG, "sending to " + actStr(action) + ", got response code " + respC + "=" + conn.getResponseMessage());
          conn.disconnect();
          return;
      }
      reader = new BufferedReader( new InputStreamReader( conn.getInputStream()));
      makeStr = new StringBuilder();
      data = "";
      while ((data = reader.readLine()) != null) {
            makeStr.append(data);
            Log.d(TAG, "read"+data);
      }
      conn.disconnect();
 } catch (Exception e) {
      Log.d(TAG, "Failed sending to " + actStr(action) + ", cause: " + e.getCause() + " message: " + e.getMessage());
 } finally {
      if (reader != null) {
          try {
               makeStr.append("");
               reader.close();
          } catch (IOException ioe) {
                ioe.printStackTrace();
          }
       }
 }

它与标准的 SslSocketFactory 一起出现:

private static SSLSocketFactory buildSslSocketFactory() {
    // Add support for self-signed (local) SSL certificates
    // Based on http://developer.android.com/training/articles/security-ssl.html#UnknownCa
    try {

        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        // From https://www.washington.edu/itconnect/security/ca/load-der.crt
        InputStream is = App.getAppResources().getAssets().open(ManageActivity.certificate);
        InputStream caInput = new BufferedInputStream(is);
        Certificate ca;
        try {
            ca = cf.generateCertificate(caInput);
            //System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
            Log.d(TAG,"ca=" +((X509Certificate) ca).getSubjectDN());
        } finally {
            caInput.close();
        }

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext.getSocketFactory();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;

}

因此,为了切换到具有受信任证书的站点,我保持上面的代码相同,但更改了证书,根据 nKn 在这里的回答,这是一个中间证书 + 站点证书组合的 crt:Android: how to do HttpPost with a certificate (我实际上已经尝试了根证书、中间证书和站点证书之间的所有组合,并分别测试了单个证书。但没有一个有效)。与其他人的问题不同,我没有收到任何错误消息说我有一个不受信任的证书或某事。

从服务器站点,它似乎收到了一个发布请求(我从错误日志中得到了一条消息),但是输入中没有任何内容,因此该应用程序也没有从服务器获得任何响应。但是,与自签名证书设置相比,服务器站点的代码完全相同,而且当我使用 curl 从终端传递相同的 JSON 时,它响应我想要的,所以我怀疑是不是证书问题。

有谁知道如何解决,或者我实际上不需要包含证书或其他东西?

最佳答案

所以最后才找到问题所在。 大概是这条线

conn.setChunkedStreamingMode(0);

不允许向服务器传递任何数据(虽然我不知道为什么它在我之前使用自签名证书时有效...)

此外,在这种情况下不需要 SslSocketFactory。

关于android - 使用 Httpsurlconnection 连接到服务器 (SSL),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51172016/

相关文章:

node.js - 在 GKE 上的 StatefulSet 中运行的 node.js API 上的 SSL

android.permission.WRITE_EXTERNAL_STORAGE 授予但访问被拒绝

android - 如何从第三个 Activity 回到第一个 Activity ?

android - 任务 generateJsonModelArmDebug 执行失败

android - Firebase 规则如何在其父级被删除时防止添加或更新值

node.js - 保护 Socket.io

security - JAVA - 我们可以忽略本地网络的 SSL 验证吗

ssl - 无法在 Ubuntu 上验证 SMTP 证书,但适用于 Windows

mysql - 如何为所有远程用户要求 SSL

ssl - Kubernetes Ingress TLS 不是使用 headless 服务创建的