【问题标题】:Overriding the SSL Trust Manager in Android在 Android 中覆盖 SSL 信任管理器
【发布时间】:2012-11-24 09:19:11
【问题描述】:

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

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

origTrustmanager.checkServerTrusted(certs, authType);

根据文档,checkServerTrusted 永远不应该抛出 NullPointerExeption。 certs 里面有两个项目。 authType 设置为“RSA”。如果我不实现自定义信任管理器,则会抛出一个异常,清楚地表明证书已过期,因此我知道底层信任管理器正在执行它的工作。即使我将设备上的日期和时间设置在证书的有效期内,上面的 checkServerTrusted 行也会产生异常。为什么?显然我做错了什么。这是我的自定义信任管理器的代码以及我如何访问 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());

【问题讨论】:

  • 注意tmf.init((KeyStore) null); 将导致TMF 返回默认系统TrustManager。这在 API 文档中没有记录。我错误地使用了tmf.init(KeyStore.getDefaultKeyStore())
  • SSL 现已弃用。致电SSLContext.getInstance() 时请务必使用TLSv1.2 或类似名称。

标签: android ssl


【解决方案1】:

如果您从未初始化 origTrustmanager 实例变量,它将具有默认值 null,这确实会在您尝试使用它时导致 NPE。

我刚刚编辑了my previous answer 以显示 TrustManager 初始化的示例。 (我没有在 Android 上尝试过,但它在纯 Java 中运行良好。)

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

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

【讨论】:

  • 你是对的。我没有初始化 origTrustmanager。本来我是,但是在移动代码之后,我以某种方式删除了它。我将它添加到上面的代码中。现在它按预期工作,但我确实对这段代码有另一个问题。最初我说我的异常确实捕获了过期的证书。确实如此,但我得到的只是带有 checkServerTrusted 的 CertificateException。这可能是任何证书例外。我怎么知道它已经过期了?以及如何判断它是我网站的证书而不是其他人的证书?
  • CertificateExpiredExceptionCertificateException 的子类。您可能遇到的其他问题之一是,如果手机的时间完全超出范围,那么 CA 证书在当时也可能无效。在这种情况下,您可能必须完全完成整个 PKI 验证(使用纯 Java,在 Certification Path API Programmer's Guide 中记录,我不确定 Android),这有点复杂。关于名称验证,您需要通过查看证书主题备用名称来执行此操作(如果失败,请查看主题 DN 中的 CN)。
【解决方案2】:

使用此代码 这对我有用

TrustManager tm = new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};

// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
    }
};
SSLContext sslContext = null;

try {
    sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[] { tm }, null);
} catch (Exception e1) {
    e1.printStackTrace();
    return;
}

AsyncSSLSocketMiddleware sslMiddleWare = Ion.getDefault(context).getHttpClient().getSSLSocketMiddleware();
sslMiddleWare.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
sslMiddleWare.setSSLContext(sslContext);

Ion.getDefault(context).getHttpClient().getSSLSocketMiddleware().setTrustManagers(trustAllCerts);
Ion.getDefault(context).getHttpClient().getSSLSocketMiddleware().setSSLContext(sslContext);

Ion.with(context).load("POST", serverUrl)
    .setHeader("Content-Type", "application/json")
    .setHeader("Accept", "application/json")
    .setLogging("ION_LOGGING", Log.VERBOSE).setJsonObjectBody(json)

【讨论】:

    猜你喜欢
    • 2016-03-12
    • 2016-09-04
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 2019-11-28
    • 1970-01-01
    相关资源
    最近更新 更多