Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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
}

問題の本質

  1. Issuer名の設定ミス:

    • 誤:parent.Issuer.ToRDNSequence()
    • 正:parent.Subject.ToRDNSequence()
  2. 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: 証明書署名に使用する秘密鍵

名前フィールドの正しい設定

修正後のコードでは:

  1. Issuer名の設定: parent.Subject.ToRDNSequence()

    • 生成する証明書の発行者名は、親証明書の主体名と同じになる
    • これにより証明書チェーンの階層関係が正しく保たれる
  2. Subject名の設定: template.Subject.ToRDNSequence()

    • 生成する証明書の主体名は、テンプレートで指定された名前になる
    • これにより意図した主体に対する証明書が生成される

ASN.1マーシャリング

asn1.Marshal関数は、Go言語の構造体をASN.1 DER形式にエンコードします。ToRDNSequence()メソッドは、pkix.Name構造体をASN.1のRDNSequence形式に変換します。

テストコードの意義

新しく追加されたテストコードは、以下をチェックします:

  1. Subject名の正確な設定: テンプレートで指定した名前が正しく証明書に設定されているか
  2. Issuer名の正確な設定: 自己署名証明書の場合、IssuerとSubjectが同じになっているか
  3. 証明書の署名検証: 生成された証明書が正しく署名されているか

関連リンク

参考にした情報源リンク