SslServerSocket
및 클라이언트 인증서를 사용하고 있으며 클라이언트의 .NET Framework에서 SubjectDN에서 CN을 추출하려고합니다 X509Certificate
.
지금 전화를 걸 cert.getSubjectX500Principal().getName()
었지만 이것은 물론 클라이언트의 전체 형식 DN을 제공합니다. 어떤 이유로 나는 CN=theclient
DN 의 일부 에만 관심이 있습니다. 문자열을 직접 구문 분석하지 않고 DN의이 부분을 추출하는 방법이 있습니까?
답변
다음은 더 이상 사용되지 않는 새로운 BouncyCastle API에 대한 코드입니다. bcmail과 bcprov 배포판이 모두 필요합니다.
X509Certificate cert = ...;
X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
RDN cn = x500name.getRDNs(BCStyle.CN)[0];
return IETFUtils.valueToString(cn.getFirst().getValue());
답변
여기에 또 다른 방법이 있습니다. 아이디어는 얻은 DN이 LDAP DN에 사용되는 것과 동일한 rfc2253 형식이라는 것입니다. 그렇다면 LDAP API를 재사용하지 않는 이유는 무엇입니까?
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());
}
답변
종속성 추가가 문제가되지 않는 경우 X.509 인증서 작업을 위해 Bouncy Castle의 API를 사용하여 이를 수행 할 수 있습니다 .
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이 주석에서 언급했듯이이 접근 방식은 이제 더 이상 사용되지 않습니다. 새로운 Bouncy Castle API를 사용하는 gtrak의 업데이트 된 코드 를 참조하십시오 .
답변
“bcmail ”이 필요하지 않은 gtrak 코드의 대안 :
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 : 내 SW를 Android에서 실행해야 할 때까지 솔루션을 사용했습니다. 그리고 Android는 javax.naming.ldap을 구현하지 않습니다 🙁
답변
http://www.cryptacular.org 와 한 줄
CertUtil.subjectCN(certificate);
Maven 종속성 :
<dependency>
<groupId>org.cryptacular</groupId>
<artifactId>cryptacular</artifactId>
<version>1.1.0</version>
</dependency>
답변
지금까지 게시 된 모든 답변에는 몇 가지 문제가 있습니다. 대부분은 내부 X500Name
또는 외부 Bounty Castle 종속성을 사용합니다. 다음은 @Jakub의 답변을 기반으로하며 공용 JDK API 만 사용하지만 OP에서 요청한대로 CN을 추출합니다. 또한 2017 년 중반에 출시 된 Java 8을 사용합니다.
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(", "))
답변
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>"(?:[^"]|"")+"|[^,]+))