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

Дозвольте мені почати з того, що це питання естетики. Я вирішив власну проблему, мені просто цікаво, як краще це зробити.

Отже, я отримав DN сертифіката, щось на зразок цього:

CN = Jimmy Blooptoop, OU = Someplace, OU = Співробітники, DC = Bloopsoft-Inc

Now, I want to grab the CN out of that. In java, there isn't native support to grab anything but the full DN from the X509 certificate without using some 3rd party library like bouncy castle - which I can't use. So I've got to parse it out, which isn't much of problem. The only thing that makes it slightly tricky is the fact that the CN isn't always going to be formatted as . More often than not, it's actually going to be , . So, in the example above, the CN could be Jimmy Blooptoop or Blooptoop, Jimmy J (short for the Joop of course).

Пройшовши і прочитавши про регулярні вирази, я написав наступне, що досить добре працює:

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

Мені просто цікаво, якщо є вирази, які виглядають менш схоже на лайно. Я досить впевнений, що є, так як я опрацював це після прочитання вступу до регулярного виразу.

21

9 Відповіді

Як щодо 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 можуть мати.

51
додано
@gregschlom: Ви так правильно. Я поняття не маю, чому я пішов зі старою шкільною петлею.
додано Автор musiKk, джерело
Цей підхід має обмеження з DN, що містять багатозначні RDN, наприклад: OU = Sales + CN = J. Smith, O = Widget Inc., C = US . У цьому випадку ви можете використовувати Rdn.toAttributes (). GetAll() і переглядати повернуті перерахування.
додано Автор Jaime Hablutzel, джерело
Як витягати cn: @musiKk від x509сертифіковано в java без використання надувного замка "> stackoverflow.com/questions/18669041/…
додано Автор likejiujitsu, джерело
Трохи краще рішення: для (Rdn rdn: ln.getRdns ()) {...}
додано Автор gregschlom, джерело
Остаточно більш тверде рішення, ніж регулярний вираз один, використовуючи BouncyCastle API є іншим вибором, див. як витягти cn з x509сертифікату в java
додано Автор jmd, джерело

Ви можете уникнути використання 'crappy' виразу. Якщо ви вже маєте DN, ви можете розділити рядок, а потім знайти 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());
    }
}
5
додано
@JesseWebb: A String # split() також створює Regex.
додано Автор musiKk, джерело
Наприклад, CN = Go Daddy Root Certificate Authority - G2, O = "GoDaddy.com, Inc.", L = Скоттсдейл, ST = Арізона, C = US
додано Автор Andrew Alcock, джерело
Хоча Regex працює, вони зазвичай дорогі у використанні. Для такого простого формату рядків, розділених комами, розбиття рядків було б краще. На жаль, ВП зазначає, що значення можуть також іноді містити кому, що робить це рішення непрацездатним.
додано Автор Jesse Webb, джерело
@musiKk - Дякуємо вам за те, що я це не знав. Я вважаю, що будь-яке рішення буде еквівалентно дорогим.
додано Автор Jesse Webb, джерело
Також не вдасться на CN = розробку Cyanogen Application, OU = Release Management, O = Cyanogen Inc., L = Сіетл, ST = Вашингтон, C = US
додано Автор tophyr, джерело

Якщо ви використовуєте 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
додано
DistinguishedName тепер застаріло на користь використання javax.naming.ldap.LdapName . Але ви можете скористатися Spring, щоб витягти ім'я з нього за допомогою LdapUtils.getStringValue (ldapName, "cn")
додано Автор jbx, джерело

Ви можете використовувати 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();
2
додано

Якщо я зрозумів це правильно, це повинно призвести до того самого результату, він виглядає трохи читабельнішим. w відповідає будь-якому символу слова.

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

Якщо ви хочете щось більш гнучке, ви можете зробити це:

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

Це збігається з "CN =" і ", XX =", де XX - дві великі літери. Група (1) повертає першу групу (відповідність у дужках).

Regexp не так складно, як вони виглядають!

1
додано
Але невдасться на комах у цитованих розділах, наприклад, CN = Перейти до кореневого сертифіката адміністратора Daddy - G2, O = "GoDaddy.com, Inc.", L = Скоттсдейл, ST = Арізона, C = US
додано Автор Andrew Alcock, джерело

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

1
додано

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

1
додано
Подібно до інших "звичайних" рішень/розділів "рішення", це буде невдачею у комах у котируваних розділах, таких як CN = Go Daddy Root Certificate Authority - G2, O = "GoDaddy.com, Inc.", L = Scottsdale, ST = Арізона, C = США
додано Автор Emil Lundberg, джерело

Схоже, це працює нормально

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

Вирази Regex досить дорогі у використанні. Для такої простої задачі, ймовірно, буде перевищення. Замість цього ви можете використовувати простий розділ рядків:

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 "";
}
0
додано
Подібно до інших "звичайних" рішень/розділів "рішення", це буде невдачею у комах у котируваних розділах, таких як CN = Go Daddy Root Certificate Authority - G2, O = "GoDaddy.com, Inc.", L = Scottsdale, ST = Арізона, C = США
додано Автор Emil Lundberg, джерело
ІТ КПІ - Java
ІТ КПІ - Java
436 учасників