Java의 X509Certificate에서 CN을 추출하는 방법은 무엇입니까?
SslServerSocket
및 클라이언트 인증서를 사용하고 인식 클라이언트의 .NET Framework에서 SubjectDN을 추출합니다 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(", "))
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();
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>"(?:[^"]|"")+"|[^,]+))
실제로 gtrak
클라이언트 인증서를 CN을 추출하는 데 도움이 될 가능성이 가장 많은 클라이언트입니다.
X509Certificate[] certs = (X509Certificate[]) httpServletRequest
.getAttribute("javax.servlet.request.X509Certificate");
X509Certificate cert = certs[0];
X509CertificateHolder x509CertificateHolder = new X509CertificateHolder(cert.getEncoded());
X500Name x500Name = x509CertificateHolder.getSubject();
RDN[] rdns = x500Name.getRDNs(BCStyle.CN);
RDN rdn = rdns[0];
String name = IETFUtils.valueToString(rdn.getFirst().getValue());
return name;
쉬운 사용을 위해 bouncycastle 위에 빌드 된 Java 암호화 라이브러리 인 cryptacular를 사용할 수 있습니다.
RDNSequence dn = new NameReader(cert).readSubject();
return dn.getValue(StandardAttributeType.CommonName);
업데이트 :이 클래스는 "sun"패키지에 있으므로주의해서 사용해야합니다. 댓글에 대한 Emil 감사합니다 :)
CN을 얻기 위해 공유하고 싶었습니다.
X500Name.asX500Name(cert.getSubjectX500Principal()).getCommonName();
Emil Lundberg의 의견에 대해서는 개발자가 'sun'패키지를 호출하는 프로그램을 작성하지 않아야하는 이유를 참조하십시오.
인증서에서 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();
정규 표현식은 사용하는 데 다소 비쌉니다. 이러한 간단한 작업의 경우 아마도 오버 킬일 것입니다. 대신 간단한 문자열 분할을 사용할 수 있습니다.
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 "";
}
X500Name은 JDK의 내부 구현이지만 리플렉션을 사용할 수 있습니다.
public String getCN(String formatedDN) throws Exception{
Class<?> x500NameClzz = Class.forName("sun.security.x509.X500Name");
Constructor<?> constructor = x500NameClzz.getConstructor(String.class);
Object x500NameInst = constructor.newInstance(formatedDN);
Method method = x500NameClzz.getMethod("getCommonName", null);
return (String)method.invoke(x500NameInst, null);
}
다음을 사용하여 시도 할 수있는 getName (X500Principal.RFC2253, oidMap)을 또는 어느 getName(X500Principal.CANONICAL, oidMap)
형식의 DN을 최고의 볼 수 있습니다. oidMap
맵 값 중 하나가 원하는 원하는 일 수 있습니다.
BC는 추출을 훨씬 쉽게 만들었습니다.
X500Principal principal = x509Certificate.getSubjectX500Principal();
X500Name x500name = new X500Name(principal.getName());
String cn = x500name.getCommonName();
다중 값 속성의 경우 -LDAP API 사용 ...
X509Certificate testCertificate = ....
X500Principal principal = testCertificate.getSubjectX500Principal(); // return subject DN
String dn = null;
if (principal != null)
{
String value = principal.getName(); // return String representation of DN in RFC 2253
if (value != null && value.length() > 0)
{
dn = value;
}
}
if (dn != null)
{
LdapName ldapDN = new LdapName(dn);
for (Rdn rdn : ldapDN.getRdns())
{
Attributes attributes = rdn != null
? rdn.toAttributes()
: null;
Attribute attribute = attributes != null
? attributes.get("CN")
: null;
if (attribute != null)
{
NamingEnumeration<?> values = attribute.getAll();
while (values != null && values.hasMoreElements())
{
Object o = values.next();
if (o != null && o instanceof String)
{
String cnValue = (String) o;
}
}
}
}
}
참고 URL : https://stackoverflow.com/questions/2914521/how-to-extract-cn-from-x509certificate-in-java
'ProgramingTip' 카테고리의 다른 글
C ++로 URL 인코딩 / (0) | 2020.10.04 |
---|---|
해시 맵 키를 반복하는 Freemarker (0) | 2020.10.04 |
방법 : 사용자 지정 위젯에 대한 테마 (스타일) 항목 정의 (0) | 2020.10.04 |
System.Data.SQLite Close ()가 데이터베이스 파일을 해제하지 않습니다. (0) | 2020.10.04 |
오류 구문 분석 /page.xhtml : 오류 추적 [line : 42] "nbsp"가 참조되지 않습니다. (0) | 2020.10.04 |