目录
前言
公司产品中一个业务需要解析证书DN的各项属性,并提取某项属性的属性值。之前的实现是将DN作为字符串进行操作,以逗号split之后遍历取出各项属性,再以等号split取出某项属性值。在碰到某个DN中有一个逗号(,)的特殊格式后就会导致数组越界的问题。这种方式很不可取,遂查资料写了以下两种方式。
一、使用 javax.naming.ldap.LdapName 类
LdapName ln = null;
try {
ln = new LdapName(dn);
} catch (InvalidNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(Rdn rdn : ln.getRdns()) {
System.out.println("LdapName--Type-- "+rdn.getType());
System.out.println("LdapName--Value-- "+rdn.getValue());
}
这个方法是在网上查到,容易实现,不用引入其他jar包,是java自带的方法。符合RFC 2253规定的,类似于CN = Steve Kille,O = Isode Limited,C = CN这种格式的字符串都能解析。但在这个业务场景下,该方法的缺点是不能判断某个属性是否是证书DN中规定的属性。比如CNTest = Steve Kille,O = Isode Limited,C = CN,标准的证书DN项中没有CNTest 这一项,但是这种方法同样可以解析出来。
二、使用 org.bouncycastle.asn1.x500.X500Name 类(推荐)
基于以上原因,考虑到产品中的实际业务使用,翻阅bouncycastle的源码,写了如下方法。
maven引入
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.51</version>
</dependency>
代码如下
private static Map<String, String> parseDNToMap(String dn) {
if(StrUtil.isBlank(dn)) {
return null;
}
Map<String, String> resMap = new HashMap<String, String>();
X500Name x500Name = new X500Name(dn);
RDN[] rdNs = x500Name.getRDNs();
for (int i = 0; i < rdNs.length; i++) {
AttributeTypeAndValue first = rdNs[i].getFirst();
//获取属性名的oid
ASN1ObjectIdentifier type = first.getType();
//将属性名的oid转换为属性名
String typeString = BCStyle.INSTANCE.oidToDisplayName(type).toUpperCase();
//或使用RFC4519Style.INSTANCE.oidToDisplayName(type).toUpperCase();
//获取属性值
String valueString = first.getValue().toString();
resMap.put(typeString, valueString);
}
return resMap;
}
如果有非标准规定的属性名,会抛出异常如下
Exception in thread "main" java.lang.IllegalArgumentException: Unknown object id - CNTest - passed to distinguished name
at org.bouncycastle.asn1.x500.style.IETFUtils.decodeAttrName(Unknown Source)
at org.bouncycastle.asn1.x500.style.BCStyle.attrNameToOID(Unknown Source)
at org.bouncycastle.asn1.x500.style.IETFUtils.rDNsFromString(Unknown Source)
at org.bouncycastle.asn1.x500.style.BCStyle.fromString(Unknown Source)
at org.bouncycastle.asn1.x500.X500Name.<init>(Unknown Source)
at org.bouncycastle.asn1.x500.X500Name.<init>(Unknown Source)
at com.certTest.x509NameTest.main(x509NameTest.java:86)
总结
以上两种方法亲测有效,如果只是要解析DN各项,第一种方法更方便。如果同时需要校验DN是否符合标准可选用第二种。