从证书DN中解析CN

时间:2011-10-28 18:45:12

标签: java regex

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

所以,我有一个证书DN,类似这样:

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

现在,我想从中获取CN。在java中,除了使用Xoun9证书中的完整DN而没有使用像bouncy castle这样的第三方库时,没有原生支持可以抓取任何东西 - 我无法使用它。所以我必须解析它,这不是什么大问题。唯一让它有点棘手的事实是CN并不总是被格式化为<first name> <last name>。通常情况下,它实际上将是<last name>, <first name> <middle initial>。因此,在上面的例子中,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();

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

9 个答案:

答案 0 :(得分:55)

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)之类的东西缺失,但它可以省去你不得不考虑DN可能有什么奇怪功能的麻烦。

答案 1 :(得分:5)

您可以避免使用“糟糕”的表达方式。如果您已经拥有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());
    }
}

答案 2 :(得分:5)

您可以使用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();

答案 3 :(得分:4)

如果您使用的是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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

我解析这样的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这样的重复元素不起作用。

答案 6 :(得分:0)

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

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 "";
}

答案 7 :(得分:0)

看起来这样可行

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

答案 8 :(得分:-1)

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

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

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

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

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

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