将自签名 SSL 证书添加到 HttpURLConnection

Phi*_*lip 5 ssl android self-signed httpurlconnection

我知道还有一些关于同一主题的其他问题,但是这些问题都不等于我的具体问题。

HttpURLConnection在我的 Android 应用程序中,我通过一些 RESTful 端点发出 Http 请求。其中一些端点使用自签名证书,其他端点则不使用。因此,我需要一种方法将自定义 KeyStore 添加到默认 HttpURLConnection 行为。

这是我现在的代码:

        try {
            KeyStore keyStore = getKeyStore(context);

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, "pasword".toCharArray());

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyManagementException e) {
            e.printStackTrace();
        }
Run Code Online (Sandbox Code Playgroud)

它可以工作,但仅适用于自签名证书,任何其他请求都会产生java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

因此,我正在寻找一种方法让 HttpURLConnection 识别自定义自签名证书而不破坏正常行为。

Hak*_*n54 4

您基本上有两个选择:

  1. 接受任何证书,无论它是否通过忽略证书验证进行签名
  2. 通过将所有证书(甚至未签名的证书)添加到密钥库来信任每个证书,并使用与上面相同的代码加载它们,异常应该消失。

我不推荐的选项 1 是创建一个不验证证书的自定义 TrustManager:

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509ExtendedTrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {}
            public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
            public void checkClientTrusted(X509Certificate[] chain, String authType) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType) {}
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }
};

KeyStore keyStore = getKeyStore(context);

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "pasword".toCharArray());

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null);

HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
Run Code Online (Sandbox Code Playgroud)

如果服务器不需要客户端进行身份验证,您甚至可以删除 keymanagerfactory 并使用以下代码初始化 ssl 上下文:

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509ExtendedTrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {}
            public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {}
            public void checkClientTrusted(X509Certificate[] chain, String authType) {}
            public void checkServerTrusted(X509Certificate[] chain, String authType) {}
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }
};

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, null);

HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
Run Code Online (Sandbox Code Playgroud)