1.この記事の対象の人

    Golang で、属性※付きの証明書要求( CSR ) を作ってみたい人

※証明書の X.509 certificate extensions 用途のための属性を付けます。
※以下、証明書要求は CSR と呼称します。

2.概要

この記事の概要は以下の通りです。

1. Golang で SubjectAltName と KeyUsage の属性をもつ CSR を生成
2. OpenSSL でSubjectAltName と KeyUsage の属性をもつ CSR を作成
3. 作成したそれぞれの CSR の中身を比較
4. おまけ– CSR の属性の ASN.1 データ構造の調査(詳しく知りたい人向け)

3.Golang で SubjectAltName と KeyUsage の属性をもつ CSR を生成

Golang でCSRは、CreateCertificateRequest関数で作成できます。

属性を追加する場合、templateで値を指定します。

SubjectAltName 属性は template の

    • DNSNames

 

    • EmailAddresses

 

    • IPAddresses

 

    URIs

に値を指定します。

DNSNames:        []string{"www.example.com", "www.example.co.jp"},

KeyUsage 属性は template に直接指定することができません。
代わりにKeyUsage の pkix.Extension を作成し ExtraExtensions に Extension の1つとして指定します。
marshalKeyUsage関数は、x509 パッケージから移植しました。詳細はコードを参照ください。

    var ku x509.KeyUsage
    ku = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign
    kex, err := marshalKeyUsage(ku)

SubjectAltName と KeyUsage 属性を持った template は以下になります。

    template := &x509.CertificateRequest{
        PublicKeyAlgorithm: x509.RSA,
        PublicKey:          publicKey,
        SignatureAlgorithm: x509.SHA256WithRSA,

        Subject: pkix.Name{
            CommonName:         "www.example.org",
            OrganizationalUnit: []string{"Example Org Unit"},
            Organization:       []string{"Example Org"},
            Country:            []string{"JP"},
        },

        DNSNames:        []string{"www.example.com", "www.example.co.jp"},
        ExtraExtensions: []pkix.Extension{kex},
    }

CSR を作成します。

    //PKCS#10 Certification Request [RFC2986]
    csr, err := x509.CreateCertificateRequest(rand.Reader, template, privateKey)

4.OpenSSL で SubjectAltName と KeyUsage の属性をもつ CSR を作成

OpenSSL で属性をもつ CSR を作成する方法は幾つかあります。
ここでは SubjectAltName と KeyUsage の値を指定した cnf ファイルを作成し、そのファイルを引数に指定して OpenSSL コマンドを実行し CSR を作成します。

ここでは、 ext.cnf という名前の cnf ファイルを作成します。

[ req ]
req_extensions = req_ext
distinguished_name = req_distinguished_name
prompt = no

[ req_distinguished_name ]
countryName = JP
organizationName = Example Org
organizationalUnitName = Example Org Unit 
commonName = www.example.org 

[ req_ext ]
subjectAltName = @alt_names
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[alt_names]
DNS.1 = www.example.com
DNS.2 = www.example.co.jp

OpenSSL コマンドを実行し CSR を作成します。

$ openssl req -new -config ext.cnf -newkey rsa:2048 -nodes -keyout ext.key -out opensslext.csr

5.作成したそれぞれの CSR の中身を比較

OpenSSLで作成した CSR の中身を表示します。

$ openssl req -text -noout -in opensslext.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = JP, O = Example Org, OU = Example Org Unit, CN = www.example.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                   省略
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                DNS:www.example.com, DNS:www.example.co.jp
            X509v3 Key Usage: 
                Digital Signature, Non Repudiation, Key Encipherment
    Signature Algorithm: sha256WithRSAEncryption
         省略

次に Golang で作成した CSR の中身を表示します。

$ openssl req -text -noout -in goExtPem.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = JP, O = Example Org, OU = Example Org Unit, CN = www.example.org
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                   省略
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                DNS:www.example.com, DNS:www.example.co.jp
            X509v3 Key Usage: 
               Digital Signature, Non Repudiation, Key Encipherment
    Signature Algorithm: sha256WithRSAEncryption
         省略

同じですね。Golangで正しく属性付き CSR が作成できたようです。

6.おまけ — CSR の属性の ASN.1 データ構造の調査

以下 CSR の属性の ANS.1 データ構造について詳しく知りたい人向けとなります。興味のある人はどうぞ。

CSR は、RFC2986 では 以下のように定義されています。

   [PKCS#10] https://datatracker.ietf.org/doc/html/rfc2986#section-4

   CertificationRequestInfo ::= SEQUENCE {
        version       INTEGER { v1(0) } (v1,...),
        subject       Name,
        subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
        attributes    [0] Attributes{{ CRIAttributes }}
   }

   SubjectPublicKeyInfo { ALGORITHM : IOSet} ::= SEQUENCE {
        algorithm        AlgorithmIdentifier {{IOSet}},
        subjectPublicKey BIT STRING
   }

   PKInfoAlgorithms ALGORITHM ::= {
        ...  -- add any locally defined algorithms here -- }

   Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}

   CRIAttributes  ATTRIBUTE  ::= {
        ... -- add any locally defined attributes here -- }

   Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
        type   ATTRIBUTE.&id({IOSet}),
        values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
   }

属性だけに注目すると、属性は

attributes    [0] Attributes{{ CRIAttributes }}

Type が Context-specific[0] で Value が Attributes{{ CRIAttributes }} と定義されています。
Attributes{{ CRIAttributes }} は、Attributes{} のパラメタが CRIAttributes であることを意味します。
CRIAttributes は ATTRIBUTE クラスの Information Object Set です。
CRIAttributes 自体の定義は、

   CRIAttributes  ATTRIBUTE  ::= {
        ... -- add any locally defined attributes here -- }

と書いてあるので任意のようです。

次に Attributes の定義をみると

   Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}

Attributes は、 SET OF Attribute{{ IOSet }} と定義されています。
ここで、IOSet = CRIAttributes です。

次に SET OF の Value である Attribute は、

   Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
        type   ATTRIBUTE.&id({IOSet}),
        values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
   }

と定義されています。これは、
SEQUENCE が type と valuesを持つ。
この時、type は [ ATTRIBUTE クラス の &id ] を持つ。
(ただし、&id は、CRIAttributes で定義された Information Object の集合の &id の値の範囲に拘束される)
この時、values は SET SIZE(1..MAX) OF [ ATTRIBUTE クラスの &Type ] を持つ。
(ただし、&Type の値は typeで指定した値(= &id ) に対応する Information Object の &Type の値に拘束される)

※ Information object class や 拘束(Constrain) の文法の詳細は以下を参照してください。
RFC6025
OSS Nokalva, Inc. 社の ASN.1 解説

ここで、RFC2986 の attributes の説明 には

        attributes is a collection of attributes providing additional
          information about the subject of the certificate.  Some
          attribute types that might be useful here are defined in PKCS
          #9.  An example is the challenge-password attribute, which
          specifies a password by which the entity may request
          certificate revocation.  Another example is information to
          appear in X.509 certificate extensions (e.g. the
          extensionRequest attribute from PKCS #9).

と書いてあります。
今回は、 X.509 certificate extensions 用に付加情報を属性に指定したいので、RFC2985 (PKCS#9) の Extension request を参照します。
Extension request は

  5.4.2 Extension request

   The extensionRequest attribute type may be used to carry information
   about certificate extensions the requester wishes to be included in a
   certificate.

   extensionRequest ATTRIBUTE ::= {
           WITH SYNTAX ExtensionRequest
           SINGLE VALUE TRUE
           ID pkcs-9-at-extensionRequest
   }

   ExtensionRequest ::= Extensions

   The Extensions type is imported from [10].

[10] ISO/IEC 9594-8:1997: Information technology - Open Systems
Interconnection - The Directory: Authentication framework. 1997.

と定義されています。 ということで今回、 CRIAttributes で使用する Information Object は、extensionRequest になります。
※余談ですがRFC2985 5.4節 に従えば、Information Object Set としての CRIAttributes はこう定義できるはず…
CRIAttributes ATTRIBUTE ::= { challengePassword | extensionRequest | extendedCertificateAttributes , … }

ここで、Extensions の定義は、ISO/IEC 9594-8:1997 を参照せよと書いてあるので
ISO/IEC 9594-8:1997 を参照すると Extensions は

Extensions ::= SEQUENCE OF Extension
Extension ::= SEQUENCE {
extnId EXTENSION.&id ({ExtensionSet}),
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
-- contains a DER encoding of a value of type &ExtnType
-- for the extension object identified by extnId -- }

と定義されています。

以上から CSR に証明書の X.509 certificate extensions 用途の属性をつける場合、属性の ASN.1 データ構造の模式図は以下になるはずです。

Context-Specific[0]
    SET OF
        SEQUENCE
            ObjectIdentifier(Extension Request : 1.2.840.113549.1.9.14)
            SET OF
                SEQUENCE
                    SEQUENCE
                        ObjectIdentifier(X509v3 Subject Alternative Name : 2.5.29.15)
                        BOOLEAN(FALSEの場合省略される)
                        OCTET STRING
                    SEQUENCE
                        ObjectIdentifier(X509v3 Key Usage : 2.5.29.17)
                        BOOLEAN(FALSEの場合省略される)
                        OCTET STRING

確認のため、OpenSSL で属性付き CSR を asn1parse してみました。

$ openssl asn1parse -in goExtPem.csr
中略    
  395:d=2  hl=2 l=  77 cons: cont [ 0 ]        
  397:d=3  hl=2 l=  75 cons: SEQUENCE          
  399:d=4  hl=2 l=   9 prim: OBJECT            :Extension Request
  410:d=4  hl=2 l=  62 cons: SET               
  412:d=5  hl=2 l=  60 cons: SEQUENCE          
  414:d=6  hl=2 l=  45 cons: SEQUENCE          
  416:d=7  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Alternative Name
  421:d=7  hl=2 l=  38 prim: OCTET STRING      [HEX DUMP]:3024820F7777772E6578616D706C652E636F6D82117777772E6578616D706C652E636F2E6A70
  461:d=6  hl=2 l=  11 cons: SEQUENCE          
  463:d=7  hl=2 l=   3 prim: OBJECT            :X509v3 Key Usage
  468:d=7  hl=2 l=   4 prim: OCTET STRING      [HEX DUMP]:030205E0
以下略

上記結果を同じように ASN.1 のデータ構造図の模式図にしてみると

Context-Specific[0]
    SEQUENCE
        ObjectIdentifier(Extension Request : 1.2.840.113549.1.9.14)
        SET OF
            SEQUENCE
                SEQUENCE
                    ObjectIdentifier(X509v3 Subject Alternative Name : 2.5.29.15)
                    BOOLEAN(FALSEの場合省略される)
                    OCTET STRING
                SEQUENCE
                    ObjectIdentifier(X509v3 Key Usage : 2.5.29.17)
                    BOOLEAN(FALSEの場合省略される)
                    OCTET STRING

あれ… Context-Specific[0] の Value の SET OF がない…。
Context-Specific[0] の Value である SET OF は IMPLICIT になって SET OF の Type と Length が省略されてエンコードされています。改めてRFC 2986を確認すると IMPLICIT として定義されています。

7. コード

コードはこちら

广告
将在 10 秒后关闭
bannerAds