Как извлечь CN из сертификата X509 в Java? - PullRequest
78 голосов
/ 26 мая 2010

Я использую SslServerSocket и клиентские сертификаты и хочу извлечь CN из SubjectDN клиента из X509Certificate.

В данный момент я звоню cert.getSubjectX500Principal().getName(), но это, конечно, дает мне полное отформатированное DN клиента. Почему-то меня просто интересует часть CN=theclient DN. Есть ли способ извлечь эту часть DN, не разбирая саму строку?

Ответы [ 17 ]

88 голосов
/ 03 октября 2011

вот другой способ. идея заключается в том, что полученное вами DN имеет формат rfc2253, который совпадает с используемым для LDAP DN. Так почему бы не использовать API-интерфейс LDAP?

import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;

String dn = x509cert.getSubjectX500Principal().getName();
LdapName ldapDN = new LdapName(dn);
for(Rdn rdn: ldapDN.getRdns()) {
    System.out.println(rdn.getType() + " -> " + rdn.getValue());
}
75 голосов
/ 03 апреля 2011

Вот некоторый код для нового не устаревшего API BouncyCastle.Вам понадобятся дистрибутивы bcmail и bcprov.

X509Certificate cert = ...;

X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
RDN cn = x500name.getRDNs(BCStyle.CN)[0];

return IETFUtils.valueToString(cn.getFirst().getValue());
12 голосов
/ 27 мая 2010

Если добавление зависимостей не является проблемой, вы можете сделать это с API Bouncy Castle для работы с сертификатами X.509:

import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;

...

final X509Principal principal = PrincipalUtil.getSubjectX509Principal(cert);
final Vector<?> values = principal.getValues(X509Name.CN);
final String cn = (String) values.get(0);

Обновление

Во время этой публикации это был способ сделать это. Однако, как упоминает gtrak в комментариях, этот подход сейчас не рекомендуется. См. обновленный код gtrak , который использует новый API Bouncy Castle.

9 голосов
/ 30 ноября 2011

В качестве альтернативы коду gtrak, который не нуждается в '' bcmail '':

    X509Certificate cert = ...;
    X500Principal principal = cert.getSubjectX500Principal();

    X500Name x500name = new X500Name( principal.getName() );
    RDN cn = x500name.getRDNs(BCStyle.CN)[0]);

    return IETFUtils.valueToString(cn.getFirst().getValue());

@ Jakub: Я использовал ваше решение до тех пор, пока мое ПО не было запущено на Android. И Android не реализует javax.naming.ldap: - (

6 голосов
/ 12 августа 2015

Одна строка с http://www.cryptacular.org

CertUtil.subjectCN(certificate);

JavaDoc: http://www.cryptacular.org/javadocs/org/cryptacular/util/CertUtil.html#subjectCN(java.security.cert.X509Certificate)

Зависимость Maven:

<dependency>
    <groupId>org.cryptacular</groupId>
    <artifactId>cryptacular</artifactId>
    <version>1.1.0</version>
</dependency>
5 голосов
/ 14 июля 2017

Все ответы, опубликованные до сих пор, имеют некоторую проблему: большинство используют внутреннюю X500Name или внешнюю зависимость Bounty Castle. Следующее основано на ответе @ Jakub и использует только общедоступный JDK API, но также извлекает CN в соответствии с запросом OP. Он также использует Java 8, который стоит в середине 2017 года, вам действительно стоит.

Stream.of(certificate)
    .map(cert -> cert.getSubjectX500Principal().getName())
    .flatMap(name -> {
        try {
            return new LdapName(name).getRdns().stream()
                    .filter(rdn -> rdn.getType().equalsIgnoreCase("cn"))
                    .map(rdn -> rdn.getValue().toString());
        } catch (InvalidNameException e) {
            log.warn("Failed to get certificate CN.", e);
            return Stream.empty();
        }
    })
    .collect(joining(", "))
3 голосов
/ 18 сентября 2013

У меня BouncyCastle 1.49, и теперь у него есть класс org.bouncycastle.asn1.x509.Certificate. Я заглянул в код IETFUtils.valueToString() - он делает какие-то чудеса с обратными слешами. Для доменного имени это не сделало бы ничего плохого, но я чувствую, что мы можем сделать лучше. В случаях, которые я рассмотрел, cn.getFirst().getValue() возвращает различные типы строк, которые все реализуют интерфейс ASN1String, который предназначен для предоставления метода getString (). Итак, что, кажется, работает для меня это

Certificate c = ...;
RDN cn = c.getSubject().getRDNs(BCStyle.CN)[0];
return ((ASN1String)cn.getFirst().getValue()).getString();
2 голосов
/ 03 апреля 2018

Вот как это сделать, используя регулярное выражение над cert.getSubjectX500Principal().getName(), если вы не хотите получать зависимость от BouncyCastle.

Это регулярное выражение будет анализировать различающееся имя, давая name и val группы захвата для каждого совпадения.

Когда строки DN содержат запятые, они должны быть заключены в кавычки - это регулярное выражение правильно обрабатывает как строки в кавычках, так и строки в кавычках, а также обрабатывает экранированные кавычки в строках в кавычках:

(?:^|,\s?)(?:(?<name>[A-Z]+)=(?<val>"(?:[^"]|"")+"|[^,]+))+

Вот красиво отформатировано:

(?:^|,\s?)
(?:
    (?<name>[A-Z]+)=
    (?<val>"(?:[^"]|"")+"|[^,]+)
)+

Вот ссылка, чтобы вы могли увидеть ее в действии: https://regex101.com/r/zfZX3f/2

Если вы хотите, чтобы регулярное выражение получало только CN, то эта адаптированная версия сделает это:

(?:^|,\s?)(?:CN=(?<val>"(?:[^"]|"")+"|[^,]+))

1 голос
/ 18 января 2017

Получение CN из сертификата не так просто. Код ниже, безусловно, поможет вам.

String certificateURL = "C://XYZ.cer";      //just pass location

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate testCertificate = (X509Certificate)cf.generateCertificate(new FileInputStream(certificateURL));
String certificateName = X500Name.asX500Name((new X509CertImpl(testCertificate.getEncoded()).getSubjectX500Principal())).getCommonName();
1 голос
/ 14 сентября 2015

ОБНОВЛЕНИЕ: Этот класс находится в пакете "sun", и вы должны использовать его с осторожностью. Спасибо Эмилю за комментарий:)

Просто хотел поделиться, чтобы получить CN, я делаю:

X500Name.asX500Name(cert.getSubjectX500Principal()).getCommonName();

Относительно комментария Эмиля Лундберга см .: Почему разработчики не должны писать программы, которые называют «солнечными» пакетами

...