102

およびクライアント証明書を使用してSslServerSocketおり、クライアントのSubjectDNからCNを抽出したいと考えていますX509Certificate

現時点では電話をかけていますcert.getSubjectX500Principal().getName()が、もちろんこれにより、クライアントのフォーマットされたDNの合計がわかります。どういうわけか、私はCN=theclientDNの部分に興味があります。文字列を自分で解析せずにDNのこの部分を抽出する方法はありますか?

4

20 に答える 20

99

非推奨ではない新しいBouncyCastleAPIのコードを次に示します。bcmailとbcprovの両方のディストリビューションが必要です。

X509Certificate cert = ...;

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

return IETFUtils.valueToString(cn.getFirst().getValue());
于 2011-04-03T02:23:06.787 に答える
98

ここに別の方法があります。取得するDNはrfc2253形式であるという考え方です。これは、LDAPDNに使用されるものと同じです。では、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());
}
于 2011-10-03T12:07:01.397 に答える
14

依存関係の追加が問題にならない場合は、X.509証明書を操作するためのBouncyCastleの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がコメントで言及しているように、このアプローチは現在非推奨です。新しいBouncyCastleAPIを使用するgtrakの更新されたコードを参照してください。

于 2010-05-26T20:46:00.907 に答える
9

'' 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を実装していません:-(

于 2011-11-30T08:20:35.327 に答える
7

http://www.cryptacular.orgの1行

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>
于 2015-08-12T16:31:37.300 に答える
7

これまでに投稿されたすべての回答にはいくつかの問題があります。ほとんどの場合、内部X500Nameまたは外部のバウンティキャッスルの依存関係を使用しています。以下は@Jakubの回答に基づいており、パブリックJDK APIのみを使用しますが、OPからの要求に応じてCNも抽出します。また、2017年半ばに登場するJava8も使用しています。

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(", "))
于 2017-07-13T22:35:59.993 に答える
6

cert.getSubjectX500Principal().getName()BouncyCastleに依存したくない場合に備えて、正規表現を使用してこれを行う方法は次のとおりです。

この正規表現は、識別された名前を解析し、一致ごとにグループを指定してキャプチャしますnameval

DN文字列にコンマが含まれている場合、それらは引用符で囲まれていることを意味します。この正規表現は、引用符で囲まれた文字列と引用符で囲まれていない文字列の両方を正しく処理し、引用符で囲まれた文字列のエスケープされた引用符も処理します。

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

これはうまくフォーマットされています:

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

動作を確認できるリンクは次のとおりです: https ://regex101.com/r/zfZX3f/2

正規表現でCNのみを取得する場合は、この適合バージョンで次のようになります。

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

于 2018-04-03T10:26:51.950 に答える
3

私はBouncyCastle1.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();
于 2013-09-17T20:39:58.317 に答える
3

更新:このクラスは「sun」パッケージに含まれているため、注意して使用する必要があります。コメントをありがとうEmil:)

CNを取得するために、共有したかっただけです。

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

Emil Lundbergのコメントについては、「開発者が「sun」パッケージを呼び出すプログラムを作成すべきでない理由」を参照してください。

于 2015-09-14T06:42:45.967 に答える
2

実際、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;
于 2014-11-10T09:56:33.377 に答える
2

プレーンJavaを使用するもう1つの方法:

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);
}
于 2020-05-28T18:30:00.873 に答える
2

ライブラリを使用せずに証明書の一般名を取得します。正規表現を使用して

名前を取得するには

String name = x509Certificate.getSubjectDN().getName();

フルネームから一般名を抽出する

    String name = "CN=Go Daddy Root Certificate Authority - G2, O=\"GoDaddy.com, Inc.\", L=Scottsdale, ST=Arizona, C=US";
    Pattern pattern = Pattern.compile("CN=(.*?)(?:,|\$)");
    Matcher matcher = pattern.matcher(name);
    if (matcher.find()) {
        System.out.println(matcher.group(1));
    }

これが誰かに役立つことを願っています。(-_-)

于 2021-10-22T11:40:36.500 に答える
1

使いやすいように、弾む城の上に構築されたJava暗号ライブラリであるcryptacularを使用できます。

RDNSequence dn = new NameReader(cert).readSubject();
return dn.getValue(StandardAttributeType.CommonName);
于 2015-05-28T09:31:40.853 に答える
1

証明書から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();
于 2017-01-17T21:33:55.460 に答える
1

BCにより、抽出がはるかに簡単になりました。

X500Principal principal = x509Certificate.getSubjectX500Principal();
X500Name x500name = new X500Name(principal.getName());
String cn = x500name.getCommonName();
于 2017-11-09T12:31:55.940 に答える
0

getName(X500Principal.RFC2253、oidMap)を使用するか、DN文字列を最適にフォーマットするものを確認することができますgetName(X500Principal.CANONICAL, oidMap)。たぶん、oidMapマップ値の1つは必要な文字列になります。

于 2010-05-26T16:02:38.870 に答える
0

正規表現は、使用するのにかなり費用がかかります。そのような単純なタスクの場合、それはおそらくやり過ぎでしょう。代わりに、単純な文字列分割を使用できます。

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 "";
}
于 2014-07-24T12:41:40.690 に答える
0

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);
}
于 2016-12-23T07:36:22.453 に答える
0

複数値属性の場合-LDAPAPIを使用して..。

        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;
                        }
                    }
                }
            }
        }
于 2019-06-11T03:14:12.980 に答える
0

Spring Securityを使用すると、次を使用できますSubjectDnX509PrincipalExtractor

X509Certificate certificate = ...;
new SubjectDnX509PrincipalExtractor().extractPrincipal(certificate).toString();
于 2020-11-25T10:53:27.617 に答える