從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 上找到代碼示例。