[インデックス 10056] ファイルの概要
このコミットは、Go言語のcrypto/x509
パッケージにおける証明書生成時の名前(Subject/Issuer)の設定に関する重要なバグ修正を行ったものです。2011年10月にAdam Langleyによって実施されました。
コミット
- コミットハッシュ:
ec0b5533c9cb77bac948171c49e62ab8c7500f18
- 作成者: Adam Langley agl@golang.org
- 日付: 2011年10月19日 12:19:13 -0400
- コミットメッセージ: "crypto/x509: fix names in certificate generation."
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ec0b5533c9cb77bac948171c49e62ab8c7500f18
元コミット内容
crypto/x509: fix names in certificate generation.
I had a brain fart in af84b15fbae2 and messed up the names in
generated certificates.
R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/5315046
影響ファイル:
src/pkg/crypto/x509/x509.go
(4行の変更: 2行削除、2行追加)src/pkg/crypto/x509/x509_test.go
(11行の変更: 1行削除、12行追加)
変更の背景
このバグは、作成者が前のコミット(af84b15fbae2
)で証明書生成機能を実装する際に、単純な論理エラーによって発生しました。Adam Langley自身がコミットメッセージで「I had a brain fart(頭がボケてしまった)」と表現しているように、実装時のケアレスミスが原因でした。
具体的には、CreateCertificate
関数において、生成される証明書のSubject(証明書の主体)とIssuer(証明書の発行者)の名前を設定する際に、参照すべきオブジェクトを間違えていました。これにより、生成された証明書の名前フィールドが意図した値にならない問題が発生していました。
前提知識の解説
X.509証明書の構造
X.509証明書は公開鍵基盤(PKI)で使用される標準的なデジタル証明書形式です。証明書には以下の重要な名前フィールドがあります:
- Subject(証明書の主体): 証明書が発行される対象(エンティティ)の識別名
- Issuer(証明書の発行者): 証明書を発行した認証局(CA)の識別名
ASN.1とRDNSequence
X.509証明書はASN.1(Abstract Syntax Notation One)という形式記述言語で定義され、DER(Distinguished Encoding Rules)でエンコードされます。
証明書の名前フィールドはRDNSequence(Relative Distinguished Name Sequence)として表現されます:
Name ::= CHOICE {
rdnSequence RDNSequence
}
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
証明書の階層構造
証明書チェーンでは、以下の関係が成立します:
- 子証明書のIssuerフィールド = 親証明書のSubjectフィールド
- 自己署名証明書ではSubject = Issuer
技術的詳細
バグのあったコード
// バグのあったコード(修正前)
asn1Issuer, err := asn1.Marshal(parent.Issuer.ToRDNSequence())
if err != nil {
return
}
asn1Subject, err := asn1.Marshal(parent.Subject.ToRDNSequence())
if err != nil {
return
}
修正後のコード
// 修正後のコード
asn1Issuer, err := asn1.Marshal(parent.Subject.ToRDNSequence())
if err != nil {
return
}
asn1Subject, err := asn1.Marshal(template.Subject.ToRDNSequence())
if err != nil {
return
}
問題の本質
-
Issuer名の設定ミス:
- 誤:
parent.Issuer.ToRDNSequence()
- 正:
parent.Subject.ToRDNSequence()
- 誤:
-
Subject名の設定ミス:
- 誤:
parent.Subject.ToRDNSequence()
- 正:
template.Subject.ToRDNSequence()
- 誤:
この修正により、証明書チェーンの正しい階層関係が保たれるようになりました。
コアとなるコードの変更箇所
src/pkg/crypto/x509/x509.go:928-935
// 修正前
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err error) {
// ...
asn1Issuer, err := asn1.Marshal(parent.Issuer.ToRDNSequence())
if err != nil {
return
}
asn1Subject, err := asn1.Marshal(parent.Subject.ToRDNSequence())
if err != nil {
return
}
// ...
}
// 修正後
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err error) {
// ...
asn1Issuer, err := asn1.Marshal(parent.Subject.ToRDNSequence())
if err != nil {
return
}
asn1Subject, err := asn1.Marshal(template.Subject.ToRDNSequence())
if err != nil {
return
}
// ...
}
src/pkg/crypto/x509/x509_test.go:243-63
テストコードには、バグを検出するための新しいテストケースが追加されました:
commonName := "test.example.com"
template := Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{"Acme Co"},
},
// ...
}
// Subject名の検証
if cert.Subject.CommonName != commonName {
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
}
// Issuer名の検証(自己署名証明書の場合)
if cert.Issuer.CommonName != commonName {
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
}
コアとなるコードの解説
CreateCertificate関数の動作
CreateCertificate
関数は以下のパラメータを受け取ります:
template
: 生成する証明書のテンプレートparent
: 親証明書(発行者の証明書)pub
: 証明書に含める公開鍵priv
: 証明書署名に使用する秘密鍵
名前フィールドの正しい設定
修正後のコードでは:
-
Issuer名の設定:
parent.Subject.ToRDNSequence()
- 生成する証明書の発行者名は、親証明書の主体名と同じになる
- これにより証明書チェーンの階層関係が正しく保たれる
-
Subject名の設定:
template.Subject.ToRDNSequence()
- 生成する証明書の主体名は、テンプレートで指定された名前になる
- これにより意図した主体に対する証明書が生成される
ASN.1マーシャリング
asn1.Marshal
関数は、Go言語の構造体をASN.1 DER形式にエンコードします。ToRDNSequence()
メソッドは、pkix.Name
構造体をASN.1のRDNSequence
形式に変換します。
テストコードの意義
新しく追加されたテストコードは、以下をチェックします:
- Subject名の正確な設定: テンプレートで指定した名前が正しく証明書に設定されているか
- Issuer名の正確な設定: 自己署名証明書の場合、IssuerとSubjectが同じになっているか
- 証明書の署名検証: 生成された証明書が正しく署名されているか
関連リンク
- Go crypto/x509パッケージ公式ドキュメント
- Go crypto/x509/pkixパッケージ公式ドキュメント
- RFC 5280: X.509 Public Key Infrastructure Certificate and CRL Profile
- X.509証明書の基本構造に関する解説