从证书DN中解析CN

首先让我说这是一个美学问题。 我已经解决了自己的问题,我只是对更好的方法感到好奇。

所以,我有一个DN证书,如下所示:

CN = Jimmy Blooptoop,OU = Someplace,OU = Employees,DC = Bloopsoft-Inc

现在,我想从中获取CN。 在java中,除了使用Xoun9证书中的完整DN而没有使用像bouncy castle这样的第三方库时,没有原生支持可以抓取任何东西 – 我无法使用它。 所以我必须解析它,这不是什么大问题。 唯一让它有点棘手的事实是CN并不总是被格式化为 。 通常情况下,它实际上将是, 。 因此,在上面的例子中,CN可以是Jimmy Blooptoop或Blooptoop,Jimmy J(当然是Joop的缩写)。

在阅读了关于正则表达式的内容之后,我写了以下内容,它运作良好:

 Matcher m = Pattern.compile("CN=[A-Za-z]*[, ]*[ A-Za-z]*").matcher(dn); if (m.find()) cn = m.group(); 

我只是好奇是否有看起来不像垃圾的表情。 我非常有信心,因为我在阅读了正则表达式的介绍之后就已经解决了这个问题。

javax.naming.ldap.LdapName怎么样?

 String dn = "CN=Jimmy Blooptoop,OU=Someplace,OU=Employees,DC=Bloopsoft-Inc"; LdapName ln = new LdapName(dn); for(Rdn rdn : ln.getRdns()) { if(rdn.getType().equalsIgnoreCase("CN")) { System.err.println("CN is: " + rdn.getValue()); break; } } 

它不是最漂亮的界面,因为像LdapName#getByType(String)这样的东西会丢失,但它LdapName#getByType(String)你必须考虑DN可能具有的奇怪function的麻烦。

你可以避免使用’糟糕’的表达方式。 如果您已经拥有DN,则可以拆分String,然后找到CN。 例如:

 String dn = "CN=Jimmy Blooptoop,OU=Someplace,OU=Employees,DC=Bloopsoft-Inc"; String[] split = dn.split(","); for (String x : split) { if (x.contains("CN=")) { System.out.println(x.trim()); } } 

如果您使用的是Spring Framework,则可以使用DistinguishedName ,如下所示:

 String path = "CN=Jimmy Blooptoop,OU=Someplace,OU=Employees,DC=Bloopsoft-Inc"; DistinguishedName dn = new DistinguishedName(path); String cn = dn.getValue("cn"); // Jimmy Blooptoop 

你可以使用Spring Frameworks LdapUtils以一种简洁的方式提取CN ,如下所示:

 String cn = LdapUtils.getStringValue(new LdapName(group),"cn"); 

要么

不使用如下的Spring Framework:

 String cn = (String)new LdapName(group).getRdns().stream().filter(rdn -> rdn.getType().equalsIgnoreCase("CN")).findFirst().get().getValue(); 

你不应该使用上面提供的正则表达式。 这些正则表达式的问题在于您不会遵循RFC 4514并且不会忽略先前转义的符号,并且可能会松散或误解专有名称。 使用LdapName类而不是musiKk建议正确解析可分辨名称。

正则表达式使用起来相当昂贵。 对于这样一个简单的任务,它可能是一个过度杀戮。 相反,您可以使用简单的String拆分:

 String dn = ((X509Certificate) certificate).getIssuerDN().getName(); String CN = getValByAttributeTypeFromIssuerDN(dn,"CN="); private String getValByAttributeTypeFromIssuerDN(String dn, String attributeType) { String[] dnSplits = dn.split(","); for (String dnSplit : dnSplits) { if (dnSplit.contains(attributeType)) { String[] cnSplits = dnSplit.trim().split("="); if(cnSplits[1]!= null) { return cnSplits[1].trim(); } } } return ""; } 

看起来这样可行

 CertificateFactory fact = CertificateFactory.getInstance("X.509"); FileInputStream is = new FileInputStream ("cert.pem"); X509Certificate cert = (X509Certificate) fact.generateCertificate(is); String cn = ((X500Name)cert.getSubjectDN()).getCommonName() 

如果我理解正确,这应该产生相同的结果,只是看起来更具可读性。 \w匹配任何单词字符。

 "CN=[\\w]*[., ]+[\\w ]*" 

如果您想要更灵活的东西,您可以这样做:

 Matcher m = Pattern.compile("(CN=.*?),[AZ]{2}=").matcher(dn); if (m.find()) cn = m.group(1); 

这匹配“CN =”和“,XX =”之间的任何内容,其中XX是两个大写字母。 group(1)返回第一组(括号内的匹配)。

Regexp并不像他们看起来那么困难!

我解析这样的DN。 您将所有属性作为属性:

 Principal principal = cert.getSubjectDN(); Properties prop = new Properties(); prop.load(new StringReader(principal.getName().replaceAll(",", "\n"))); prop.list(System.out); String CN= props.get("CN"); 

但它不适用于像ou或dc这样的重复元素。