簡體   English   中英

為什么 Java / Apache HttpClient 使用與我的瀏覽器不同(且已過期)的 X509 根證書?

[英]Why does Java / Apache HttpClient use a different (and expired) X509 root certificate than my browser?

對於Stack Exchange 項目,我正在使用 Java 程序從 Internet 上下載各種鏈接,該程序使用Apache HttpClient 它檢查過期的 SSL 證書,這可能是圖像不再可見的原因之一。

我注意到有時,Java 程序認為 SSL 證書已過期,而我的瀏覽器認為它沒有過期。 示例如下 URL: https://www.dewharvest.com/uploads/3/4/5/4/34546214/oak-from-seed_orig.jpg

我的瀏覽器(macOS 上的 Firefox)認為它是有效的:

但是當我運行下面的剝離 Java 程序時,這就是我得到的:

Name:       CN=www.dewharvest.com
Not after:  Tue Feb 09 00:59:59 CET 2021
Not before: Fri Feb 07 01:00:00 CET 2020
Serial #:   6f698f95c0f23b77fb181bf76141b080
Thumbprint: 580d0025564d9603ede46f6aba03dfc5045d207a

Name:       CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
Not after:  Sat May 30 12:48:38 CEST 2020
Not before: Tue May 30 12:48:38 CEST 2000
Serial #:   13ea28705bf4eced0c36630980614336
Thumbprint: eab040689a0d805b5d6fd654fc168cff00b78be3

Name:       CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
Not after:  Wed Jan 01 00:59:59 CET 2031
Not before: Fri Nov 02 01:00:00 CET 2018
Serial #:   7d5b5126b476ba11db74160bbc530da7
Thumbprint: 33e4e80807204c2b6182a3a14b591acd25b5f0db

順序不同(最終實體;根;中間),您可以看到與我的瀏覽器匹配的最終實體和中間實體的指紋和序列號。 根證書不同,主要問題是該證書僅在去年 5 月之前有效。 這里發生了什么事? 我檢查了我的 Java 密鑰庫(使用keytool -list ),它包含與我的瀏覽器相同的根證書,但不是上面過期的根證書:

usertrustrsaca [jdk], Aug 25, 2016, trustedCertEntry, 
Certificate fingerprint (SHA1): 2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E

Java程序在這里; 我正在使用Apache HttpClient 的 4.5.11 版本,但更新到 4.5.13 沒有幫助。

import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.xml.bind.DatatypeConverter;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

public class Test {
    public static void main(String[] args) throws Exception {
        X509TrustManager x509TrustManager = getDefaultX509TrustManager();
        SSLContext sslContext = SSLContext.getInstance("TLS");
        final MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        sslContext.init(new KeyManager[0], new TrustManager[] { new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                x509TrustManager.checkClientTrusted(chain, authType);
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                for (X509Certificate certificate : chain) {
                    System.out.println("Name:       " + certificate.getSubjectX500Principal().getName());
                    System.out.println("Not after:  " + certificate.getNotAfter());
                    System.out.println("Not before: " + certificate.getNotBefore());
                    System.out.println("Serial #:   " + certificate.getSerialNumber().toString(16));
                    System.out.println("Thumbprint: " + DatatypeConverter
                            .printHexBinary(messageDigest.digest(certificate.getEncoded())).toLowerCase());
                    System.out.println();
                }
                x509TrustManager.checkServerTrusted(chain, authType);
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return x509TrustManager.getAcceptedIssuers();
            }
        } }, new SecureRandom());
        final CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
        client.execute(new HttpGet("https://www.dewharvest.com/"));
    }

    private static X509TrustManager getDefaultX509TrustManager() throws Exception {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init((KeyStore)null);
        for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
            if (trustManager instanceof X509TrustManager)
                return (X509TrustManager)trustManager;
        }
        throw new Exception("No X509 trust manager found.");
    }
}

Certigo 存在一個特定問題,請參閱: 如果我在具有備用信任路徑的鏈中的其他證書過期會發生什么?

如果我理解正確,網站的所有者應該已經從他們的證書鏈中刪除了舊的根證書,因為新的根證書默認安裝在瀏覽器和 Java 的信任庫中,如您所見。

SSLMate 有一個不錯的在線測試儀: https://whatsmychaincert.com/?www.dewharvest.com

瀏覽器和 Java 的 TrustManager 的區別在於,在 Java 中,如果證書鏈中的證書過期,則不再檢查替代證書(在本地信任庫中)。

我記得在 Java 6 或 7 中,有一個不同的問題,即僅在最終證書上檢查到期日期,而不是在中間證書上檢查,但我不記得他們是什么時候修復的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM