Java - 忽略过期的 SSL 证书

8
URL myUrl = new URL("https://www.....");

网站的SSL证书已过期。如何避免此问题并使URL()正常工作?


这个 URL 是否抛出了 CertificateExpiredException?@Dikobraz - Manu
3个回答

28

您应该构建一个TrustManager,它包装了默认的信任管理器,捕获CertificiateExpiredException并忽略它。

注意:正如这个答案所详细说明的那样,这是否安全非常依赖于实现。特别是,它依赖于日期验证在所有其他内容正确检查之后最后进行。

以下内容应该可以工作:

TrustManagerFactory tmf = TrustManagerFactory.getInstance(
    TrustManagerFactory.getDefaultAlgorithm());
// Initialise the TMF as you normally would, for example:
tmf.init((KeyStore)null); 

TrustManager[] trustManagers = tmf.getTrustManagers();
final X509TrustManager origTrustmanager = (X509TrustManager)trustManagers[0];

TrustManager[] wrappedTrustManagers = new TrustManager[]{
   new X509TrustManager() {
       public java.security.cert.X509Certificate[] getAcceptedIssuers() {
          return origTrustmanager.getAcceptedIssuers();
       }

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

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

SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, wrappedTrustManagers, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

信任管理器在证书出现问题时会抛出CertificateException(有关详细信息,请参见子类)。请明确要捕获/忽略的内容。您真正想要验证的所有内容都必须在可能抛出的内容之前进行检查,否则您将不得不手动验证它。任何比此更轻松的行为(特别是什么都不做,因此不会抛出任何异常)都将完全忽略证书验证和验证,这与使用匿名密码套件或忽略身份验证几乎相同。这将破坏使用SSL/TLS的安全目的(与仅在到期日期上更具灵活性不同)。

这段代码根本不起作用。当执行tmf.getTrustManagers()时,会出现“TrustManagerFactory未初始化”的异常。请在发布代码之前进行测试。我花了一个多小时来解决这个问题。 - Johann
1
@AndroidDev,抱歉,我假设OP会知道TMF应该像通常一样初始化,但你可能是对的,这并不明显。(刚刚编辑过。) - Bruno
始终出现javax.net.ssl.SSLHandshakeException:连接被对等方关闭。 - Igor Ronner
请注意,在 SSL 客户端身份验证的情况下,服务器本身可能会检查客户端证书的到期日期。在这种情况下没有解决方案:https://stackoverflow.com/questions/2762080/how-to-ignore-expired-certificates-from-outside-a-java-application/55334566#55334566 - Vadzim
@Bruno 我正在使用 Retrofit 进行网络调用,在我的 Android 应用程序中,我必须从哪里调用这个 TrustManager? - Md Shahnawaz
显示剩余2条评论

3

你需要创建一个自定义的X509验证器,它将忽略过期的证书。实际上,不会执行任何检查。

代码取自这里:http://exampledepot.com/egs/javax.net.ssl/TrustAll.html

// 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) {
        }
    }
};

// Install the all-trusting trust manager
try {
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
}

// Now you can access an https URL without having the certificate in the truststore
// It should work with expired certificate as well
try {
    URL myUrl = new URL("https://www.....");
} catch (MalformedURLException e) {
}

2
请避免复制/粘贴/传播完全禁用安全检查的代码,就像这个TrustManager一样:这将首先破坏使用SSL/TLS的目的。 - Bruno
当某些移动设备的电池被取出时,日期会重置为旧日期,如1980年,这将导致SSL证书因到期而失效。这可能会导致您的应用程序失败,如果您的应用程序即使在证书过期的情况下仍需要继续工作,则忽略到期日期非常重要。 - Johann
@AndroidDev 如果你的批评是针对Bruno的评论的话,请注意接受任何证书和过时的可信证书之间存在巨大的区别。此外,一个认真的用户会因为很多原因调整日期。 - class stacker
始终出现javax.net.ssl.SSLHandshakeException:连接被对等方关闭。 - Igor Ronner

2
我编写了一个自定义的TrustManager来解决这个问题,您可以在https://gist.github.com/divergentdave/9a68d820e3610513bd4fcdc4ae5f91a1 上查看它。这个TrustManager将有问题的X509证书包装在另一个类中,以禁用过期检查,同时保留所有其他验证(即匹配主机名、链到受信任的CA、签名有效等)。

不错的例子,我试了一下,发现没有任何 checkValidity 方法被调用。唯一触发的回调是 getEncodedgetPublicKey。不确定是否与我使用的本地密钥库有关,其中加载了固定证书? - jayeffkay
@jayeffkay 我在本地试过,看到了几个方法被调用。我确实看到checkValidity被调用了,但是我不得不设置方法断点而不是行号断点,因为这些方法的主体为空。我不确定固定证书是否会有所不同,但如果您使用自签名证书作为服务器证书(而不是叶子证书),那么可能会改变行为。如果TLS服务器的公共部分与信任存储中的自签名证书的公共密钥相同,则其余检查都无关紧要。(未测试发生了什么) - dncook

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