GolangでPKI入門 - 5

#1.この記事の対象の人
- Golang で、属性※付きの証明書要求( [CSR](https://datatracker.ietf.org/doc/html/rfc2986) ) を作ってみたい人

※証明書の[ X.509 certificate extensions ](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)用途のための属性を付けます。
※以下、証明書要求は 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関数](https://pkg.go.dev/crypto/x509#CreateCertificateRequest)で作成できます。


属性を追加する場合、[template](https://pkg.go.dev/crypto/x509#CertificateRequest)で値を指定します。

**SubjectAltName** 属性は template の

- DNSNames
- EmailAddresses
- IPAddresses
- URIs

に値を指定します。

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


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

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

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

```GO
	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 を作成します。

```GO
	//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 ファイルを作成します。


```Bash
[ 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 を作成します。

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

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

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

```Bash
$ 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 の中身を表示します。

```Bash
$ 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](https://datatracker.ietf.org/doc/html/rfc2986) は、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](https://datatracker.ietf.org/doc/html/rfc6025#section-2.2.2)
[OSS Nokalva, Inc. 社の ASN.1 解説](https://www.oss.com/asn1/resources/asn1-made-simple/advanced-topics.html)


ここで、[RFC2986 の attributes の説明](https://datatracker.ietf.org/doc/html/rfc2986#page-6) には

```
        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 ](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2) 用に付加情報を属性に指定したいので、[RFC2985 (PKCS#9)](https://datatracker.ietf.org/doc/html/rfc2985#page-17) の **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節 ](https://datatracker.ietf.org/doc/html/rfc2985#page-16)に従えば、Information Object Set としての CRIAttributes  はこう定義できるはず...
   CRIAttributes  ATTRIBUTE  ::= { challengePassword | extensionRequest | extendedCertificateAttributes , ... }


ここで、**Extensions** の定義は、[ISO/IEC 9594-8:1997](https://www.itu.int/ITU-T/recommendations/rec.aspx?rec=X.509) を参照せよと書いてあるので
[ISO/IEC 9594-8:1997](https://www.itu.int/ITU-T/recommendations/rec.aspx?rec=X.509) を参照すると **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 ](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)用途の属性をつける場合、属性の 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 してみました。

```Bash
$ 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 として[定義](https://datatracker.ietf.org/doc/html/rfc2986#appendix-A)されています。



#7. コード
[コード](https://github.com/tardevnull/gopkicookbook5)はこちら