Разбор CN из сертификата DN

Позвольте мне начать с того, что это вопрос эстетики. Я решил свою проблему, мне просто интересно, как это сделать.

Итак, у меня есть DN сертификата, что-то вроде этого:

CN = Jimmy Blooptoop, OU = Someplace, OU = Сотрудники, DC = Bloopsoft-Inc

Теперь я хочу захватить CN из этого. В Java нет встроенной поддержки для захвата всего, кроме полного DN из сертификата X509, без использования какой-либо сторонней библиотеки, такой как bouncy castle - которую я не могу использовать. Поэтому я должен разобрать его, что не так много проблем. Единственное, что немного смущает, - это то, что CN не всегда будет отформатирован как <first name> <last name>. Чаще всего это будет <last name>, <first name> <middle initial>. Итак, в приведенном выше примере CN может быть Джимми Блуптоопом или Блуптотопом, Джимми Дж (конечно, для Joop).

После просмотра и регулярного выражения, я написал следующее, которое работает достаточно хорошо:

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

Мне просто интересно, есть ли выражения, которые выглядели бы меньше, чем дерьмо. Я довольно уверен, что есть, так как я работал над этим после прочтения только введения в регулярное выражение.

Ответ 1

Как насчет 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 могут быть странные функции.

Ответ 2

Вы можете избежать использования выражения 'crappy'. Если у вас уже есть 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());
    }
}

Ответ 3

Если вы используете 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

Вы не должны использовать регулярные выражения, предложенные выше. Проблема с этими регулярными выражениями заключается в том, что вы не будете следовать RFC 4514 и не будут игнорировать ранее экранированные символы и могут потенциально потерять или неправильно интерпретировать выдающееся имя. Используйте класс LdapName, как рекомендуется musiKk, чтобы правильно проанализировать отличительное имя.

Ответ 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();

Ответ 6

Похоже, что это работает ОК

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

Ответ 7

Если я правильно понял, это должно привести к тому же результату, просто выглядит немного более читаемым. \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 не так сложно, как они выглядят!

Ответ 8

Я разбираю 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.

Ответ 9

Выражения регулярных выражений довольно дороги в использовании. Для такой простой задачи это, вероятно, будет больше, чем убийство. Вместо этого вы можете использовать простое разделение строк:

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