從Java中的X509證書中提取CN
1. 概述
公用名 (CN) 是 X.509 證書中可分辨名稱 (DN) 字段中的一個屬性。 CN 通常是證書所屬組織的域名。有時,我們需要從應用程序中的證書文件中訪問 CN 值。
在本教程中,我們將學習在 Java 中提取 CN 值的不同方法。
2. 通用名稱
證書包含有關證書所有者的信息:有效期、證書用途、DN 等。
可分辨名稱或 DN 本質上由一組名稱/值對組成,其中包括國家/地區 (C)、組織 (O)、組織單位 (OU)、CN 等名稱。
DN 看起來類似於“ CN=Baeldung, L=Casablanca, ST=Morocco, C=MA ”。如本示例所示,CN 通常是站點的域名。
要使用 Java 從 X.509 證書中提取 CN,我們可以執行以下操作:
- 解析證書
- 獲取其 DN
- 解析DN以提取CN
在以下部分中,我們將使用不同的庫提取 CN。
3. 使用 BouncyCastle
BouncyCastle 是用於加密操作的 API 集合,它補充了默認的 Java 加密擴展 (JCE)。此外,它還提供了一種獲取有關證書信息的簡單方法。
3.1. Maven依賴
讓我們首先在pom.xml中聲明bouncycastle依賴項:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
3.2.提取 CN
首先,讓我們從證書文件中獲取X509Certificate對象:
Security.addProvider(new BouncyCastleProvider());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(new FileInputStream("src/main/resources/Baeldung.cer"));
在上面的代碼中,我們使用addProvider()方法將BouncyCastleProvider註冊為安全提供程序。之後,我們使用getInstance()方法創建一個CertificateFactory對象。 getInstance()方法採用兩個參數 - 證書類型“ X.509 ”和安全提供程序“ BC ”。隨後, certificateFactory實例通過generateCertificate()方法生成X509Certificate對象。
接下來,讓我們從X509Certificate對象獲取 CN:
@Test
void whenUsingBouncyCastle_thenExtractCommonName() {
X500Principal principal = certificate.getSubjectX500Principal();
X500Name x500Name = new X500Name(principal.getName());
RDN[] rdns = x500Name.getRDNs(BCStyle.CN);
List<String> names = new ArrayList<>();
for (RDN rdn : rdns) {
String name = IETFUtils.valueToString(rdn.getFirst().getValue());
names.add(name);
}
for (String commonName : names) {
assertEquals("Baeldung", commonName);
}
}
在此代碼中,我們使用getSubjectX500Principal()方法檢索X500Principal格式的主題 DN。然後,我們將該 DN 轉換為 BouncyCastle 的X500Name表示形式。之後,我們通過getRDNs()方法從X500Name對像中提取 CN。
RDN是一個 BouncyCastle 類,表示X.500Name對象的單個部分。一個X.500Name對象由多個 RDN 組成,每個 RDN 都由一個屬性類型和一個屬性值組成。最後,我們使用BCStyle.CN,它是 CN 屬性類型的 BouncyCastle 常量。
4. 使用正則表達式
正則表達式(regex)是 Java 中字符串操作的強大工具。我們可以用它從證書中提取 CN 。
讓我們創建一個測試用例來提取 CN:
@Test
void whenUsingRegex_thenExtractCommonName() {
X500Principal principal = certificate.getSubjectX500Principal();
List<String> names = new ArrayList<>();
Pattern pattern = Pattern.compile("CN=([^,]+)");
Matcher matcher = pattern.matcher(principal.getName());
while (matcher.find()) {
names.add(matcher.group(1));
}
for (String commonName : names) {
assertEquals("Baeldung", commonName);
}
}
在上面的代碼中,我們使用了Pattern和Matcher類。我們首先通過調用其靜態compile()方法並傳遞“CN=([^,]+)”模式來創建一個Pattern對象。然後,我們通過調用Pattern對象的matcher()方法並向其傳遞 DN 值來創建一個Matcher對象。最後,我們調用Matcher對像中的find()方法。
5. 使用密碼庫
從證書獲取 CN 值的另一種方法是使用 Cryptaulous 庫。
5.1. Maven依賴
讓我們在pom.xml中聲明cryptacular依賴:
<dependency>
<groupId>org.cryptacular</groupId>
<artifactId>cryptacular</artifactId>
<version>1.2.6</version>
</dependency>
5.2.提取 CN
讓我們創建一個測試用例,在其中使用CertUtil類提取 CN:
@Test
void whenUsingCryptacular_thenExtractCommonName() {
String commonName = CertUtil.subjectCN(certificate);
assertEquals("Baeldung", commonName);
}
我們使用subjectCN()方法從X509Certificate對像中提取 CN。
另外,我們應該注意,當證書有多個 CN 時,該庫僅返回一個 CN 。
6. 使用 LDAP API
我們還可以使用 JDK 中的標準 LDAP API 來實現相同的目標:
@Test
void whenUsingLDAPAPI_thenExtractCommonName() throws Exception {
X500Principal principal = certificate.getSubjectX500Principal();
LdapName ldapDN = new LdapName(principal.getName());
List<String> names = new ArrayList<>();
for (Rdn rdn : ldapDN.getRdns()) {
if (rdn.getType().equalsIgnoreCase("cn")) {
String name = rdn.getValue().toString();
names.add(name);
}
}
for (String commonName : names) {
assertEquals("Baeldung", commonName);
}
}
上面的代碼處理在 LDAP 上下文中從 X.509 證書解析 DN。我們從 DN 的字符串表示形式構造LdapName對象。這是一種將 DN 從 X.509 證書上下文轉換為 LDAP 上下文的方法。
一旦我們有了LdapName的實例,我們就可以使用getRdns()方法輕鬆地將 DN 分解為其各個組件(如 CN、OU、O 等) 。
七、結論
CN是證書非常重要的一部分。在 SSL/TLS 證書的上下文中,CN 用於指示與證書關聯的域名。
在本文中,我們學習瞭如何使用多種方法提取證書文件的 CN 值。
與往常一樣,可以在 GitHub 上找到代碼示例。