在 Android 中覆盖 SSL 信任管理器 [英] Overriding the SSL Trust Manager in Android

查看:26
本文介绍了在 Android 中覆盖 SSL 信任管理器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试覆盖 Android 中的信任管理器.我想让底层信任管理器检查证书,但我需要确定证书是否已过期.如果证书已过期,我需要忽略它并接受证书.如果取出电池,某些移动设备会将日期重置为旧日期,从而导致证书看起来好像已过期.即使发生这种情况,我的应用也必须继续运行.

I am trying to override the trust manager in Android. I want to let the underlying trust manager check certificates but I need to determine if a certificate is expired. If the certificate is expired, I need to ignore it and accept the certificate. Some mobile devices will reset the date to an old date if the battery is removed, causing the certificate to appear as though it expired. My app must continue to keep running even if this happens.

我遇到的问题是这行代码抛出了 NullPointerException:

The problem I am having is that this line of code throws a NullPointerException:

origTrustmanager.checkServerTrusted(certs, authType);

根据文档,checkServerTrusted 不应该抛出 NullPointerExeption.certs 中有两个项目.authType 设置为RSA".如果我不实现自定义信任管理器,则会抛出异常,明确表明证书已过期,因此我知道底层信任管理器正在执行其工作.即使我将设备上的日期和时间设置在证书的有效期内,上面的 checkServerTrusted 行也会生成异常.为什么?显然我做错了什么.这是我的自定义信任管理器的代码以及我如何访问 URL:

According to the docs, checkServerTrusted should never throw a NullPointerExeption. certs has two items in it. authType is set to "RSA". If I don't implement a custom Trust Manager, an exception will be thrown that clearly indicates that the certificate has expired, so I know that the underlying Trust Manager is doing its job. Even if I set the date and time on my device to be within the validity time of the certificate, the checkServerTrusted line above generates an exception. Why? Clearly I'm doing something wrong. Here is the code for my custom Trust Manager and how I am accessing the Url:

class SSLTrustManager
{
  private X509TrustManager origTrustmanager;

  public SSLTrustManager()
  {
    try
    {
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      tmf.init((KeyStore) null);
      TrustManager[] trustManagers = tmf.getTrustManagers();
      this.origTrustmanager = (X509TrustManager) trustManagers[0];
    }
    catch (Exception ex)
    {
    }
  }

  public javax.net.ssl.SSLSocketFactory GetSocketFactory()
  {
    try
    {
      TrustManager[] wrappedTrustManagers = new TrustManager[] {
          new X509TrustManager()
          {
            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
              return origTrustmanager.getAcceptedIssuers();
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType)
            {
              try
              {
                origTrustmanager.checkClientTrusted(certs, authType);
              }
              catch (CertificateException e)
              {
              }
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException
            {
              try
              {
                origTrustmanager.checkServerTrusted(certs, authType);
              }
              catch(Exception ex)
              {
              }
            }
          }
       };

      SSLContext sslContext = SSLContext.getInstance("SSL");
      sslContext.init(null, wrappedTrustManagers, new java.security.SecureRandom());
      javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
      return sslSocketFactory;
    }
    catch (Exception ex)
    {
      return null;
    }
  }
}    

访问网址的代码:

SSLTrustManager sslTrustManager = new SSLTrustManager();
HttpsURLConnection.setDefaultSSLSocketFactory(sslTrustManager.GetSocketFactory());

URL siteUrl = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) siteUrl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);

DataOutputStream out = new DataOutputStream(conn.getOutputStream());

推荐答案

如果你从不初始化 origTrustmanager 实例变量,它的默认值为 null,即确实会在您尝试使用它的任何时候导致 NPE.

If you never initialise the origTrustmanager instance variable, it will have its default value of null, which will indeed cause an NPE any time you're trying to use it.

我刚刚编辑了我之前的答案,以展示 TrustManager 初始化的示例.(我没有在 Android 上尝试过,但它在普通 Java 中运行良好.)

I've just edited my previous answer on this to show an example of TrustManager initialisation. (I haven't tried on Android, but it works fine in plain Java.)

注意不要抓太多.在这里,您将在您的信任管理器中捕获 CertificateExceptionException:这与什么都没有一样好,因为这些方法旨在抛出这些异常.如果您想忽略到期日期,请确保只捕获 CertificateExpiredException.

Be careful not to catch too much. Here, you're catching CertificateException and Exception in your trust manager: this is as good as having nothing, since these methods are meant to throw those exceptions. Make sure you only catch CertificateExpiredException if you want to ignore expiration dates.

请注意,这只是一个技巧,它依赖于这样一个事实:在实践中,证书验证是在一般信任验证之后完成的(至少在 OpenJDK 实现中).据我所知,规范中没有任何内容说明证书过期在之后被验证.它是在对信任元素的其他验证之前完成的,而您忽略了该异常,您可以让更多的证书通过.

Note that this is only a trick that relies on the fact that, in practice, the certificate verification is done after the general trust verification (at least in the OpenJDK implementation). As far as I know, there's nothing in the specifications that says that the certificate expiration is verified after. It was done before other verification on elements of trust and you ignored that exception, you could let more certificate through than you want.

这篇关于在 Android 中覆盖 SSL 信任管理器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆