如何在Android应用程序中绕过SSL证书验证?

8

我的Android应用程序应该能够与任何启用SSL的服务器进行通信。由于我的应用程序是演示应用程序,而且我的客户在登录时会添加自己的SSL服务器详细信息到应用程序中,因此我事先不知道需要验证哪个SSL证书。

以下是我的早期代码。

public SSLSocketFactory getSSLSocketFactory(String hostname) {
        try {
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new X509TrustManager[] { new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            } }, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  HttpsURLConnection.getDefaultSSLSocketFactory();
    }

当我更新我的应用到Play Store时,它被拒绝了,原因如下:
为了正确处理SSL证书验证,请更改您的代码,在自定义X509TrustManager接口的checkServerTrusted方法中,当服务器呈现的证书不符合您的期望时,引发CertificateException或IllegalArgumentException异常。如有技术问题,您可以在Stack Overflow上发布并使用“android-security”和“TrustManager”标签。
我想将我的代码更新为类似于这样的代码。
public static HttpClient wrapClient(HttpClient base) {
try {
    SSLContext ctx = SSLContext.getInstance("TLS");
    X509TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };
    ctx.init(null, new TrustManager[]{tm}, null);
    SSLSocketFactory ssf = new SSLSocketFactory(ctx);
    ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    ClientConnectionManager ccm = base.getConnectionManager();
    SchemeRegistry sr = ccm.getSchemeRegistry();
    sr.register(new Scheme("https", ssf, 443));
    return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
    return null;
}

Playstore是否接受这个?有没有更好的处理方法?

提前感谢。

3个回答

8
引用自以下解决方案: https://gist.github.com/aembleton/889392 下面的代码可禁用任何新的HttpsUrlConnection实例的SSL证书检查:
 /**
 * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
 * aid testing on a local box, not for use on production.
 */
public static void disableSSLCertificateChecking() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }
    } };

    try {
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } });
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

我需要从哪里调用这个方法? - Farmer
1
无论在代码的哪个位置,都可以只调用一次应用程序生命周期函数。 - Elad

2

如果有人想要在NativeScript Android应用程序中禁用SSL证书检查,以下是将Elad的答案代码转换为JavaScript的方法:

  const disableSSLCertificateCheckin = function () {
    const trustAllCerts = [new javax.net.ssl.X509TrustManager({
      getAcceptedIssuers: function () {
        return null
      },

      checkClientTrusted: function (arg0, arg1) {
        // Not implemented
      },

      checkServerTrusted: function (arg0, arg1) {
        // Not implemented
      },
    })]

    try {
      const sc = javax.net.ssl.SSLContext.getInstance('TLS')
      sc.init(null, trustAllCerts, new java.security.SecureRandom())

      javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
      javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier({
          verify: function (hostname, session) {
            return true
          }
        })
      )
    } catch (e) {
      console.log('e :>> ', e);
    }
  }

  disableSSLCertificateCheckin()

Thanks @Elad!


它可以工作,但文本中有一个小错误。应该是javax.net.ssl.HttpsURLConnection而不是javax.net.ssl.HttpsURLConnection.HttpsURLConnection。 - Maxitors

0
这是一篇旧文章,但我希望它能帮助其他人。 问题已经通过 netcipher 版本 2.1.0 得到解决(v1.2.1 出现 SSLHandshakeException)。
文件 app/build.gradle。
...
dependencies {
    implementation 'info.guardianproject.netcipher:netcipher:2.1.0'
...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接