我正在使用SslServerSocket
和客户端证书,想要从客户端的X509Certificate
中提取SubjectDN中的CN。
目前我调用cert.getSubjectX500Principal().getName()
,但这当然给了我客户端的总格式化DN。由于某种原因,我只对DN中的CN=theclient
部分感兴趣。是否有方法可以在不自己解析字符串的情况下提取DN的此部分?
我正在使用SslServerSocket
和客户端证书,想要从客户端的X509Certificate
中提取SubjectDN中的CN。
目前我调用cert.getSubjectX500Principal().getName()
,但这当然给了我客户端的总格式化DN。由于某种原因,我只对DN中的CN=theclient
部分感兴趣。是否有方法可以在不自己解析字符串的情况下提取DN的此部分?
实际上,多亏了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;
还有一种使用纯Java的方法:
public static String getCommonName(X509Certificate certificate) {
String name = certificate.getSubjectX500Principal().getName();
int start = name.indexOf("CN=");
int end = name.indexOf(",", start);
if (end == -1) {
end = name.length();
}
return name.substring(start + 3, end);
}
BC使提取变得更加容易:
X500Principal principal = x509Certificate.getSubjectX500Principal();
X500Name x500name = new X500Name(principal.getName());
String cn = x500name.getCommonName();
sun.security.x509.X500Name
吗?正如其他答案多年前指出的那样,它是未经记录的,不能依靠的。 - dave_thompson_085org.bouncycastle.asn1.x500.X500Name
类的JavaDoc,但是它并没有显示那个方法... - lapo可以使用Cryptacular,它是一个基于Bouncycastle的Java加密库,方便易用。
RDNSequence dn = new NameReader(cert).readSubject();
return dn.getValue(StandardAttributeType.CommonName);
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();
X500Name
是JDK内部类。 - peterh使用Spring Security,可以使用SubjectDnX509PrincipalExtractor
:
X509Certificate certificate = ...;
new SubjectDnX509PrincipalExtractor().extractPrincipal(certificate).toString();
如果您想要使用100%专用于此的API(尤其是如果您拥有更多“高级”证书),您可以这样使用eu.europa.esig.dss:
val x509Certificate = X509CertUtils.parse(certPem)
val certToken = CertificateToken(x509Certificate)
val commonName = DSSASN1Utils.extractAttributeFromX500Principal(
ASN1ObjectIdentifier(X520Attributes.COMMONNAME.oid),
X500PrincipalHelper(
x509Certificate.subjectX500Principal
)
)
commonName
,而且几乎了解规范允许的每个可能的属性,如organizationIdentifier
、encryptedBusinessCategory
等(目前有239个)。CertificateExtensionsUtils.getQcStatements(certToken).psd2QcType.rolesOfPSP
getSubjectAlternativeNames
和getSubjectX500Principal
来获取主体名称。System.out.println(cer.getSubjectX500Principal().getName())
System.out.println(cer.getSubjectAlternativeNames().stream().filter(l->l.get(0).equals(2)).map(l -> String.valueOf(l.get(1))).collect(Collectors.joining(",")))
对于多值属性 - 使用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;
}
}
}
}
}
正则表达式在使用时相对较耗费资源。对于这样一个简单的任务,使用它可能会过度杀伤。相反,您可以使用简单的字符串分割:
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 "";
}