Mar*_* C. 82 java ssl x509certificate x509
我正在使用SslServerSocket和客户端证书,并希望从客户端的SubjectDN中提取CN X509Certificate.
我打电话的那一刻,cert.getSubjectX500Principal().getName()但这当然给了我客户端的格式化DN.出于某种原因,我只对CN=theclientDN 的部分感兴趣.有没有办法提取DN的这一部分而不自己解析String?
Jak*_*kub 91
这是另一种方式.您的想法是您获得的DN是rfc2253格式,与LDAP DN使用的格式相同.那么为什么不重用LDAP API呢?
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
String dn = x509cert.getSubjectX500Principal().getName();
LdapName ldapDN = new LdapName(dn);
for(Rdn rdn: ldapDN.getRdns()) {
System.out.println(rdn.getType() + " -> " + rdn.getValue());
}
Run Code Online (Sandbox Code Playgroud)
gtr*_*rak 79
以下是新推荐的不推荐使用的BouncyCastle API的代码.你需要bcmail和bcprov发行版.
X509Certificate cert = ...;
X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
RDN cn = x500name.getRDNs(BCStyle.CN)[0];
return IETFUtils.valueToString(cn.getFirst().getValue());
Run Code Online (Sandbox Code Playgroud)
laz*_*laz 12
如果添加依赖项不是问题,您可以使用Bouncy Castle的 API来处理X.509证书:
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
...
final X509Principal principal = PrincipalUtil.getSubjectX509Principal(cert);
final Vector<?> values = principal.getValues(X509Name.CN);
final String cn = (String) values.get(0);
Run Code Online (Sandbox Code Playgroud)
更新
在发布时,这是实现此目的的方法.然而,正如gtrak在评论中提到的那样,这种方法现在已被弃用.请参阅gtrak 使用新Bouncy Castle API 的更新代码.
作为gtrak代码的替代品,不需要''bcmail'':
X509Certificate cert = ...;
X500Principal principal = cert.getSubjectX500Principal();
X500Name x500name = new X500Name( principal.getName() );
RDN cn = x500name.getRDNs(BCStyle.CN)[0]);
return IETFUtils.valueToString(cn.getFirst().getValue());
Run Code Online (Sandbox Code Playgroud)
@Jakub:我已经使用了你的解决方案,直到我的SW必须在Android上运行.Android没有实现javax.naming.ldap :-(
cert.getSubjectX500Principal().getName()如果您不想依赖 BouncyCastle,这里介绍了如何使用正则表达式 over 来完成此操作。
该正则表达式将解析一个专有名称,为每个匹配提供name一个val捕获组。
当 DN 字符串包含逗号时,它们应该被引用 - 此正则表达式可以正确处理带引号和不带引号的字符串,并且还处理带引号的字符串中的转义引号:
(?:^|,\s?)(?:(?<name>[A-Z]+)=(?<val>"(?:[^"]|"")+"|[^,]+))+
这是格式很好的:
(?:^|,\s?)
(?:
(?<name>[A-Z]+)=
(?<val>"(?:[^"]|"")+"|[^,]+)
)+
Run Code Online (Sandbox Code Playgroud)
这是一个链接,您可以看到它的实际效果: https ://regex101.com/r/zfZX3f/2
如果您希望正则表达式仅获取CN,那么这个改编版本可以做到这一点:
(?:^|,\s?)(?:CN=(?<val>"(?:[^"]|"")+"|[^,]+))
小智 6
CertUtil.subjectCN(certificate);
Run Code Online (Sandbox Code Playgroud)
JavaDoc:http://www.cryptacular.org/javadocs/org/cryptacular/util/CertUtil.html#subjectCN(java.security.cert.X509Certificate )
Maven依赖:
<dependency>
<groupId>org.cryptacular</groupId>
<artifactId>cryptacular</artifactId>
<version>1.1.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
小智 6
无需使用任何库即可获取证书的通用名称。使用正则表达式
为了得到名字
String name = x509Certificate.getSubjectDN().getName();
Run Code Online (Sandbox Code Playgroud)
从全名中提取常用名
String name = "CN=Go Daddy Root Certificate Authority - G2, O=\"GoDaddy.com, Inc.\", L=Scottsdale, ST=Arizona, C=US";
Pattern pattern = Pattern.compile("CN=(.*?)(?:,|\$)");
Matcher matcher = pattern.matcher(name);
if (matcher.find()) {
System.out.println(matcher.group(1));
}
Run Code Online (Sandbox Code Playgroud)
希望这对任何人都有帮助。(-_-)
到目前为止发布的所有答案都有一些问题:大多数使用内部X500Name或外部Bounty Castle依赖项.以下内容基于@ Jakub的答案并仅使用公共JDK API,但也提取OP所要求的CN.它也使用Java 8,它在2017年中期,你真的应该.
Stream.of(certificate)
.map(cert -> cert.getSubjectX500Principal().getName())
.flatMap(name -> {
try {
return new LdapName(name).getRdns().stream()
.filter(rdn -> rdn.getType().equalsIgnoreCase("cn"))
.map(rdn -> rdn.getValue().toString());
} catch (InvalidNameException e) {
log.warn("Failed to get certificate CN.", e);
return Stream.empty();
}
})
.collect(joining(", "))
Run Code Online (Sandbox Code Playgroud)