Saya menggunakan SslServerSocket
sertifikat dan klien dan ingin mengekstrak CN dari SubjectDN dari klien X509Certificate
.
Saat ini saya menelepon cert.getSubjectX500Principal().getName()
tetapi ini tentu saja memberi saya DN klien yang diformat total. Untuk beberapa alasan saya hanya tertarik pada CN=theclient
bagian DN. Adakah cara untuk mengekstrak bagian DN ini tanpa mengurai String sendiri?
java
ssl
x509certificate
x509
Martin C.
sumber
sumber
Jawaban:
Berikut beberapa kode untuk BouncyCastle API baru yang tidak digunakan lagi. Anda membutuhkan distribusi bcmail dan bcprov.
X509Certificate cert = ...; X500Name x500name = new JcaX509CertificateHolder(cert).getSubject(); RDN cn = x500name.getRDNs(BCStyle.CN)[0]; return IETFUtils.valueToString(cn.getFirst().getValue());
sumber
IETFUtils.valueToString
tidak muncul untuk menghasilkan hasil yang benar. Saya memiliki CN yang menyertakan beberapa tanda sama dengan karena pengkodean basis 64 (misalnyaAAECAwQFBgcICQoLDA0ODw==
). ThevalueToString
Metode menambahkan kembali garis miring ke hasilnya. Sebagai gantinya, penggunaantoString
tampaknya berhasil. Sulit untuk menentukan bahwa ini sebenarnya penggunaan api yang benar.ini cara lain. Idenya adalah bahwa DN yang Anda peroleh dalam format rfc2253, yang sama seperti yang digunakan untuk DN LDAP. Jadi mengapa tidak menggunakan kembali 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()); }
sumber
String commonName = new LdapName(certificate.getSubjectX500Principal().getName()).getRdns().stream() .filter(i -> i.getType().equalsIgnoreCase("CN")).findFirst().get().getValue().toString();
CN
(alias2.5.4.3
)Rdn#getValue()
berisi fileString
. Namun, untuk tipe kustom, hasilnya adalahbyte[]
(mungkin berdasarkan representasi internal yang dienkode dimulai dengan#
). Ofc,byte[]
->String
dimungkinkan, tetapi berisi karakter tambahan (tidak dapat diprediksi). Saya telah menyelesaikan ini dengan solusi @laz berdasarkan BC, karena menangani dan menerjemahkannya dengan benarString
.Jika menambahkan dependensi tidak menjadi masalah, Anda dapat melakukannya dengan API Bouncy Castle untuk bekerja dengan sertifikat 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);
Memperbarui
Pada saat posting ini, ini adalah cara untuk melakukan ini. Namun, seperti yang disebutkan gtrak di komentar, pendekatan ini sekarang sudah tidak digunakan lagi. Lihat kode terupdate gtrak yang menggunakan Bouncy Castle API baru.
sumber
Sebagai alternatif dari kode gtrak yang tidak memerlukan '' 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: Saya telah menggunakan solusi Anda sampai SW saya harus dijalankan di Android. Dan Android tidak mengimplementasikan javax.naming.ldap :-(
sumber
X500Name x500Name = new X500Name(cert.getSubjectX500Principal().getName()); String cn = x500Name.getCommonName();
(menggunakan java 8)IETFUtils.valueToString
mengembalikan nilai dalam melarikan diri bentuk. Saya menemukan bahwa hanya memohon.toString()
bekerja untuk saya.Satu baris dengan http://www.cryptacular.org
JavaDoc: http://www.cryptacular.org/javadocs/org/cryptacular/util/CertUtil.html#subjectCN(java.security.cert.X509Certificate)
Ketergantungan Maven:
<dependency> <groupId>org.cryptacular</groupId> <artifactId>cryptacular</artifactId> <version>1.1.0</version> </dependency>
sumber
Semua jawaban yang diposting sejauh ini memiliki beberapa masalah: Sebagian besar menggunakan
X500Name
dependensi Bounty Castle internal atau eksternal. Kalimat berikut ini dibangun di atas jawaban @ Jakub dan hanya menggunakan JDK API publik, tetapi juga mengekstrak CN seperti yang diminta oleh OP. Ini juga menggunakan Java 8, yang berdiri di pertengahan 2017, Anda benar-benar harus.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(", "))
sumber
Berikut cara melakukannya menggunakan regex over
cert.getSubjectX500Principal().getName()
, jika Anda tidak ingin bergantung pada BouncyCastle.Regex ini akan mengurai nama yang dibedakan, memberi
name
danval
menangkap grup untuk setiap pertandingan.Jika string DN berisi koma, itu dimaksudkan untuk dikutip - ekspresi reguler ini menangani string yang dikutip dan tidak dikutip dengan benar, dan juga menangani kutipan yang lolos dalam string yang dikutip:
(?:^|,\s?)(?:(?<name>[A-Z]+)=(?<val>"(?:[^"]|"")+"|[^,]+))+
Ini diformat dengan baik:
(?:^|,\s?) (?: (?<name>[A-Z]+)= (?<val>"(?:[^"]|"")+"|[^,]+) )+
Berikut tautannya sehingga Anda dapat melihatnya beraksi: https://regex101.com/r/zfZX3f/2
Jika Anda ingin regex hanya mendapatkan CN, versi adaptasi ini akan melakukannya:
(?:^|,\s?)(?:CN=(?<val>"(?:[^"]|"")+"|[^,]+))
sumber
Saya memiliki BouncyCastle 1,49, dan kelas yang dimilikinya sekarang adalah org.bouncycastle.asn1.x509.Certificate. Saya melihat ke dalam kode
IETFUtils.valueToString()
- itu melakukan beberapa melarikan diri mewah dengan garis miring terbalik. Untuk nama domain itu tidak akan melakukan sesuatu yang buruk, tapi saya rasa kami bisa melakukan yang lebih baik. Dalam kasus-kasus, saya telah melihatcn.getFirst().getValue()
mengembalikan berbagai jenis string yang semuanya mengimplementasikan antarmuka ASN1String, yang ada untuk menyediakan metode getString (). Jadi, apa yang tampaknya berhasil bagi saya adalahCertificate c = ...; RDN cn = c.getSubject().getRDNs(BCStyle.CN)[0]; return ((ASN1String)cn.getFirst().getValue()).getString();
sumber
UPDATE: Kelas ini ada dalam paket "sun" dan Anda harus menggunakannya dengan hati-hati. Terima kasih Emil atas komentarnya :)
Hanya ingin berbagi, untuk mendapatkan CN, saya lakukan:
Mengenai komentar Emil Lundberg lihat: Mengapa Pengembang Tidak Harus Menulis Program Yang Menyebut Paket 'sun'
sumber
X500Name
menjadi API kepemilikan internal yang dapat dihapus dalam rilis mendatang.Memang, berkat
gtrak
tampaknya untuk mendapatkan sertifikat klien dan mengekstrak CN, kemungkinan besar ini berfungsi.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;
sumber
Bisa menggunakan cryptacular yang merupakan perpustakaan kriptografi Java yang dibangun di atas bouncycastle agar mudah digunakan.
RDNSequence dn = new NameReader(cert).readSubject(); return dn.getValue(StandardAttributeType.CommonName);
sumber
Anda dapat mencoba menggunakan getName (X500Principal.RFC2253, oidMap) atau
getName(X500Principal.CANONICAL, oidMap)
untuk melihat format mana yang terbaik untuk string DN. Mungkin salah satu nilaioidMap
peta akan menjadi string yang Anda inginkan.sumber
Mengambil CN dari sertifikat tidak sesederhana itu. Kode di bawah ini pasti akan membantu Anda.
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();
sumber
Satu cara lagi untuk dilakukan dengan Java biasa:
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); }
sumber
Ekspresi ekspresi reguler, agak mahal untuk digunakan. Untuk tugas sesederhana itu mungkin akan menjadi over kill. Sebagai gantinya Anda bisa menggunakan pemisahan String sederhana:
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 ""; }
sumber
\,
atau nilai yang dikutip.X500Name adalah implementasi internal JDK, namun Anda dapat menggunakan refleksi.
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); }
sumber
BC membuat ekstraksi jauh lebih mudah:
X500Principal principal = x509Certificate.getSubjectX500Principal(); X500Name x500name = new X500Name(principal.getName()); String cn = x500name.getCommonName();
sumber
.getCommonName()
metode apa pun di X500Name .sun.security.x509.X500Name
- yang karena jawaban lain yang dicatat beberapa tahun sebelumnya tidak berdokumen dan tidak dapat diandalkan?org.bouncycastle.asn1.x500.X500Name
kelas, yang tidak menunjukkan metode itu…Untuk atribut multi-nilai - menggunakan 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; } } } } }
sumber