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. コード
コードはこちら