檢查證書是自簽名還是使用 Java 進行 CA 簽名
1. 概述
數字證書對於建立可信且安全的在線通信非常重要。我們經常使用它們來確保客戶端和 Web 服務器之間交換的數據保持安全。
在本教程中,我們將探討如何在 Java 中確定給定證書是自簽名還是由受信任的證書頒發機構 (CA) 簽名。
然而,由於證書和安全概念的多樣性,沒有一刀切的解決方案。我們經常需要根據我們的具體情況和要求選擇最佳方法。
2. 自簽名與自簽名CA 簽名證書
首先,我們來看看自簽名證書和 CA 簽名證書之間的區別。
簡單地說,自簽名證書是由同一實體生成和簽名的。儘管它提供加密,但它不會驗證獨立機構的信任。換句話說,它不涉及任何第三方證書頒發機構 (CA)。
因此,當用戶的 Web 瀏覽器遇到自簽名證書時,由於無法獨立驗證證書的真實性,它可能會發出安全警告。
我們經常在專用網絡中使用它們並用於測試目的。
另一方面,CA 簽名的證書是由受信任的證書頒發機構簽名的。大多數 Web 瀏覽器和操作系統都能識別並接受這些 CA。
此外,CA 簽名的證書證明持有證書的實體是域的合法所有者,幫助用戶相信他們正在與真正的服務器而不是中間人進行通信。
現在,讓我們看看如何使用 Java 檢查我們是否正在處理自簽名證書或 CA 簽名證書。
3. 檢查證書是否自簽名
在開始之前,讓我們生成將在整個示例中使用的證書。生成自簽名證書的最簡單方法是使用keytool
工具:
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -validity 365 -keysize 2048
在這裡,我們使用selfsigned
別名創建了一個自簽名證書。此外,我們將其存儲在keysore.jks
密鑰庫中。
3.1.比較發行人和主體價值
如前所述,同一實體生成並簽署自簽名證書。
證書的頒發者部分代表證書的簽名者。自簽名證書對於主體(Issued To)和頒發者(Issued By)具有相同的值。換句話說,為了確定我們是否正在處理自簽名證書,我們將比較其主題和頒發者信息。
此外,Java API 提供了java.security.cert.X509Certificate
類來處理證書。通過此類,我們可以與 X.509 證書交互並執行各種檢查和驗證。
讓我們檢查主題和發行者是否匹配。我們可以通過從X509Certificate
對像中提取相關字段並檢查它們是否匹配來實現此目的:
@Test
void whenCertificateIsSelfSigned_thenSubjectIsEqualToIssuer() throws Exception {
X509Certificate certificate = (X509Certificate) keyStore.getCertificate("selfsigned");
assertEquals(certificate.getSubjectDN(), certificate.getIssuerDN());
}
3.2.驗證簽名
我們檢查是否正在處理自簽名證書的另一種方法是使用其自己的公鑰對其進行驗證。
讓我們使用verify()
方法檢查自簽名證書:
@Test
void whenCertificateIsSelfSigned_thenItCanBeVerifiedWithItsOwnPublicKey() throws Exception {
X509Certificate certificate = (X509Certificate) keyStore.getCertificate("selfsigned");
assertDoesNotThrow(() -> certificate.verify(certificate.getPublicKey()));
}
但是,如果我們傳遞 CA 簽名的證書,該方法將拋出異常。
4. 檢查證書是否由 CA 簽名
要獲得 CA 簽名資格,證書必須是指向受信任根 CA 的信任鏈的一部分。簡單地說,證書鏈包含從根證書開始到用戶證書結束的證書列表。鏈中的每個證書都會簽署下一個證書。
當我們談論信任鏈時,有不同的證書類型:
- 根證書
- 中級證書
- 最終實體證書
此外,我們使用層次結構的根證書和中間證書來頒發和驗證最終實體證書。
出於本教程的目的,我們將使用從 Baeldung 站點獲取的證書:
如果最終實體證書是證書鏈的一部分,則檢查 CA 簽名證書的複雜性會增加。在這種情況下,我們可能需要檢查整個鏈以確定我們是否擁有 CA 簽名的證書。證書的頒發者可能不是直接的根 CA,而是與根 CA 簽署的中間 CA。
現在,如果我們檢查證書層次結構,我們可以看到該證書是證書鏈的一部分:
-
Baltimore CyberTrust Root
– 根 CA -
Cloudflare Inc ECC CA-3
– 中級 CA -
sni.cloudflaressl.com
– 最終實體(在 Baeldung 網站上使用)
4.1.使用信任庫
我們可以創建自己的信任庫來檢查我們信任的證書之一是否簽署了最終實體證書。
設置信任庫時,我們通常包括根 CA 證書以及構建信任鏈所需的任何中間 CA 證書。這樣,我們的應用程序就可以有效地驗證其他方提供的證書。
使用信任庫的一個優點是能夠決定我們信任哪些 CA 證書,不信任哪些 CA 證書。
在我們的示例中, Baltimore CyberTrust Root
證書籤署了中間 Cloudflare 證書,該證書籤署了我們的最終實體證書。
現在,讓我們將兩者添加到我們的信任庫中:
keytool -importcert -file cloudflare.cer -keystore truststore.jks -alias cloudflare
keytool -importcert -file root.cer -keystore truststore.jks -alias root
接下來,為了檢查我們是否信任給定的最終實體證書,我們需要找到一種獲取根證書的方法。讓我們創建搜索根證書的getRootCertificate()
方法:
X509Certificate getRootCertificate(X509Certificate endEntityCertificate, KeyStore trustStore)
throws Exception {
X509Certificate issuerCertificate = findIssuerCertificate(endEntityCertificate, trustStore);
if (issuerCertificate != null) {
if (isRoot(issuerCertificate)) {
return issuerCertificate;
} else {
return getRootCertificate(issuerCertificate, trustStore);
}
}
return null;
}
首先,我們嘗試在信任存儲區中找到所提供證書的頒發者:
static X509Certificate findIssuerCertificate(X509Certificate certificate, KeyStore trustStore)
throws KeyStoreException {
Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate cert = trustStore.getCertificate(alias);
if (cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) cert;
if (x509Cert.getSubjectX500Principal().equals(certificate.getIssuerX500Principal())) {
return x509Cert;
}
}
}
return null;
}
然後,如果發現匹配,我們將繼續驗證該證書是否是自簽名 CA 證書。如果驗證成功,我們將獲得根證書。如果沒有,我們將繼續搜索。
最後,讓我們測試我們的方法來檢查它是否正常工作:
@Test
void whenCertificateIsCASigned_thenRootCanBeFoundInTruststore() throws Exception {
X509Certificate endEntityCertificate = (X509Certificate) keyStore.getCertificate("baeldung");
X509Certificate rootCertificate = getRootCertificate(endEntityCertificate, trustStore);
assertNotNull(rootCertificate);
}
如果我們使用自簽名證書執行相同的測試,我們將無法獲得根,因為我們的信任存儲不包含它。
5. 檢查證書是否為CA證書
如果我們只處理根證書或中間證書,我們可能需要執行額外的檢查。
請務必注意,根證書也是自簽名證書。但是,根證書和用戶自簽名證書之間的區別在於前者將啟用keyCertSign
標誌(因為我們可以使用它來簽署其他證書)。
我們可以通過檢查密鑰用法來識別根證書或中間證書:
@Test
void whenCertificateIsCA_thenItCanBeUsedToSignOtherCertificates() throws Exception {
X509Certificate certificate = (X509Certificate) keyStore.getCertificate("cloudflare");
assertTrue(certificate.getKeyUsage()[5]);
}
此外,我們可以執行的檢查之一是檢查基本約束擴展。
基本約束擴展是 X.509 證書中的一個字段,它提供有關證書的預期用途以及它代表證書頒發機構 (CA) 還是最終實體的信息。
如果基本約束擴展不存在,則getBasicConstraints()
方法返回 -1:
@Test
void whenCertificateIsCA_thenBasicConstrainsReturnsZeroOrGreaterThanZero() throws Exception {
X509Certificate certificate = (X509Certificate) keyStore.getCertificate("cloudflare");
assertNotEquals(-1, certificate.getBasicConstraints());
}
六,結論
在本文中,我們學習瞭如何檢查證書是自簽名的還是 CA 簽名的。
綜上所述,自簽名證書具有相同的主題和頒發者組件,此外,它們可以使用自己的公鑰進行驗證。
另一方面,CA 簽名的證書通常是證書鏈的一部分。為了驗證它們,我們需要創建一個包含受信任根證書和中間證書的信任存儲,並檢查最終實體證書的根是否與受信任證書之一匹配。
最後,如果我們使用根證書或中間證書,我們可以通過檢查它們是否用於簽署其他證書來識別它們。
與往常一樣,整個代碼示例可以在 GitHub 上找到。