LoginSignup
2
1

More than 1 year has passed since last update.

GolangでPKI入門 - 5

Last updated at Posted at 2021-10-01

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)

SubjectAltNameKeyUsage 属性を持った 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 であることを意味します。
CRIAttributesATTRIBUTE クラスの 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})
   }

と定義されています。これは、
SEQUENCEtypevaluesを持つ。
この時、type は [ ATTRIBUTE クラス の &id ] を持つ。
(ただし、&id は、CRIAttributes で定義された Information Object の集合の &id の値の範囲に拘束される)
この時、valuesSET 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. コード

コードはこちら

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1