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

[インデックス 16588] ファイルの概要

このコミットは、Go言語の標準ライブラリである crypto/x509 パッケージに、RFC 5280 で定義されている証明書失効リスト (CRL) 配布ポイントのサポートを追加するものです。具体的には、X.509 証明書から CRL 配布ポイント情報をパースし、また証明書を生成する際にこの情報を組み込む機能が実装されました。これにより、Goアプリケーションが証明書の失効状態を確認するための重要なメカニズムに対応できるようになります。

コミット

commit 4bd79e742a50281a2897eb84de7cf81a211c5dd4
Author: Paul van Brouwershaven <paul@vanbrouwershaven.com>
Date:   Mon Jun 17 14:56:45 2013 -0700

    crypto/x509: Added RFC 5280, section 4.2.1.14 to parseCertificate and buildExtensions
    Support for CRL Distribution Points
    
    R=golang-dev, agl, bradfitz
    CC=golang-dev
    https://golang.org/cl/10258043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4bd79e742a50281a2897eb84de7cf81a211c5dd4

元コミット内容

crypto/x509: Added RFC 5280, section 4.2.1.14 to parseCertificate and buildExtensions Support for CRL Distribution Points

変更の背景

デジタル証明書は、公開鍵の所有者を検証するために広く利用されています。しかし、証明書が発行された後でも、秘密鍵の漏洩、組織名の変更、証明書ポリシーの違反など、様々な理由でその有効性が失われることがあります。このような場合、証明書は「失効」される必要があります。

証明書の失効情報を配布する主要なメカニズムの一つが、証明書失効リスト (CRL) です。CRL は、認証局 (CA) によって発行され、失効した証明書のシリアル番号のリストを含んでいます。クライアントは、証明書を検証する際に、その証明書が失効していないことを確認するために、関連する CRL を取得してチェックする必要があります。

このコミット以前の crypto/x509 パッケージは、X.509 証明書内の CRL 配布ポイント拡張を適切にパースまたは生成する機能を持っていませんでした。これにより、Goアプリケーションが証明書の失効状態を効率的かつ標準的な方法で確認することが困難でした。この変更は、Goの crypto/x509 ライブラリが、より堅牢な証明書検証機能を提供し、RFC 5280 に準拠した形で CRL 配布ポイントを扱うことを可能にするために行われました。

前提知識の解説

X.509 証明書

X.509 は、公開鍵証明書の標準フォーマットを定義するITU-Tの標準です。デジタル証明書は、公開鍵と、その公開鍵の所有者に関する情報(名前、組織など)を関連付け、認証局 (CA) によってデジタル署名されます。これにより、証明書の信頼性が保証されます。

証明書失効リスト (CRL)

CRL は、認証局 (CA) によって発行される、失効したデジタル証明書のリストです。各エントリには、失効した証明書のシリアル番号と、失効日時、失効理由などが含まれます。クライアントは、証明書を検証する際に、このリストを参照して、対象の証明書が失効していないことを確認します。

CRL 配布ポイント (CRL Distribution Points)

CRL 配布ポイントは、X.509 証明書の拡張フィールドの一つで、関連する CRL がどこで取得できるかを示す情報(通常はURL)を含んでいます。RFC 5280 のセクション 4.2.1.14 で定義されており、クライアントが適切な CRL を効率的に見つけ出すためのメカニズムを提供します。この拡張は、複数の配布ポイントを持つことができ、それぞれが異なるプロトコル(HTTP, LDAPなど)や場所を指し示すことがあります。

ASN.1 (Abstract Syntax Notation One)

ASN.1 は、データ構造を記述するための標準的な記法です。X.509 証明書や CRL は、ASN.1 を用いて定義された構造に従ってエンコードされます。Goの encoding/asn1 パッケージは、ASN.1 構造のエンコードとデコードをサポートします。

OID (Object Identifier)

OID は、情報オブジェクトを一意に識別するための階層的な命名システムです。X.509 証明書の拡張フィールドは、それぞれ固有の OID を持ちます。CRL 配布ポイント拡張の OID は 2.5.29.31 です。

技術的詳細

このコミットの主要な技術的変更点は、crypto/x509 パッケージが CRL 配布ポイント拡張 (OID 2.5.29.31) を適切に処理できるようにしたことです。

  1. Certificate 構造体の拡張: Certificate 構造体に CRLDistributionPoints []string フィールドが追加されました。これにより、パースされた証明書から CRL 配布ポイントのURLリストを直接取得できるようになります。

  2. ASN.1 構造体の定義: RFC 5280, 4.2.1.14 に従って、distributionPoint および distributionPointName という内部的なASN.1構造体が定義されました。これらは、CRL 配布ポイント拡張のASN.1エンコーディングをGoの構造体にマッピングするために使用されます。

    • distributionPoint: 個々の配布ポイントを表し、配布ポイント名、失効理由、CRL発行者などの情報を含みます。
    • distributionPointName: 配布ポイントの名前を表し、完全な名前 (fullName) または相対名 (nameRelativeToCRLIssuer) のいずれかを取ります。このコミットでは、主に fullName (通常はURL) の処理に焦点を当てています。
  3. parseCertificate 関数でのパース処理: parseCertificate 関数は、証明書の拡張を処理する際に、OID 2.5.29.31 (CRL Distribution Points) を認識するようになりました。

    • 拡張の値 (ASN.1エンコードされたバイト列) を distributionPoint のスライスとしてアンマーシャルします。
    • distributionPoint から DistributionPoint.FullName を抽出し、その Bytes フィールドを再度ASN.1アンマーシャルして asn1.RawValue を取得します。
    • asn1.RawValueTag6 (IA5String、通常はURL) であることを確認し、その Bytes を文字列として Certificate.CRLDistributionPoints に追加します。これにより、URL形式の配布ポイントが抽出されます。
  4. buildExtensions 関数での生成処理: buildExtensions 関数は、Certificate テンプレートに基づいて証明書の拡張を構築する際に、template.CRLDistributionPoints が空でない場合に CRL 配布ポイント拡張を生成するようになりました。

    • template.CRLDistributionPoints の各URL文字列に対して、対応する distributionPoint 構造体を作成します。
    • distributionPointName.FullName には、URL文字列を asn1.RawValue (Tag: 6, Class: 2, Bytes: URLバイト列) としてエンコードしたものを設定します。
    • 構築された distributionPoint のスライスをASN.1エンコードし、その結果を拡張の値として設定します。
    • buildExtensions 関数内で、拡張を格納するスライスの初期サイズが 8 から 9 に増やされ、新しい拡張のためのスペースが確保されました。
  5. テストの追加: x509_test.go に、CRL 配布ポイントを含む自己署名証明書を生成し、それが正しくパースされることを検証するテストケースが追加されました。これにより、実装の正確性が保証されます。

これらの変更により、Goの crypto/x509 パッケージは、X.509 証明書における CRL 配布ポイントの標準的な利用パターンを完全にサポートするようになりました。

コアとなるコードの変更箇所

src/pkg/crypto/x509/x509.go

  • Certificate 構造体に CRLDistributionPoints []string フィールドを追加。
  • distributionPoint および distributionPointName 構造体を定義。
  • parseCertificate 関数内で、OID 2.5.29.31 (CRL Distribution Points) の拡張をパースするロジックを追加。
    • ASN.1の SEQUENCE OF DistributionPoint をアンマーシャルし、各 DistributionPoint から FullName (URL) を抽出。
  • oidExtensionCRLDistributionPoints (OID 2.5.29.31) を定義。
  • buildExtensions 関数内で、template.CRLDistributionPoints に基づいて CRL 配布ポイント拡張を構築するロジックを追加。
    • ret スライスの初期サイズを 8 から 9 に変更。

src/pkg/crypto/x509/x509_test.go

  • TestCreateSelfSignedCertificate 関数内で、テスト用の証明書テンプレートに CRLDistributionPoints を追加。
  • 生成された証明書の CRLDistributionPoints がテンプレートと一致するかを検証する reflect.DeepEqual チェックを追加。

コアとなるコードの解説

src/pkg/crypto/x509/x509.go の変更

// Certificate struct: Added CRLDistributionPoints field
type Certificate struct {
	// ... existing fields ...
	// CRL Distribution Points
	CRLDistributionPoints []string
	// ... existing fields ...
}

// New ASN.1 structs for CRL Distribution Points
// RFC 5280, 4.2.1.14
type distributionPoint struct {
	DistributionPoint distributionPointName `asn1:"optional,tag:0"`
	Reason            asn1.BitString        `asn1:"optional,tag:1"`
	CRLIssuer         asn1.RawValue         `asn1:"optional,tag:2"`
}

type distributionPointName struct {
	FullName     asn1.RawValue    `asn1:"optional,tag:0"`
	RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
}

// parseCertificate function: Handling of OID 2.5.29.31
func parseCertificate(in *certificate) (*Certificate, error) {
	out := new(Certificate)
	// ... existing parsing logic ...

	for _, e := range in.Extensions {
		switch {
		// ... existing extension parsing cases ...
		case e.Id.Equal(oidExtensionCRLDistributionPoints): // OID 2.5.29.31
			var cdp []distributionPoint
			_, err := asn1.Unmarshal(e.Value, &cdp)
			if err != nil {
				return nil, err
			}

			for _, dp := range cdp {
				var n asn1.RawValue
				// Unmarshal the FullName bytes to get the actual value
				_, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n)
				if err != nil {
					return nil, err
				}

				// Check if the tag is IA5String (tag 6), which is typically a URL
				if n.Tag == 6 {
					out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
				}
			}
			continue
		// ... other extension parsing cases ...
		}
	}
	return out, nil
}

// OID definition for CRL Distribution Points
var (
	// ... existing OID definitions ...
	oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31}
)

// buildExtensions function: Building CRL Distribution Points extension
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
	// Increased slice size to accommodate the new extension
	ret = make([]pkix.Extension, 9 /* maximum number of elements. */)
	n := 0

	// ... existing extension building logic ...

	if len(template.CRLDistributionPoints) > 0 {
		ret[n].Id = oidExtensionCRLDistributionPoints

		var crlDp []distributionPoint
		for _, name := range template.CRLDistributionPoints {
			// Marshal the URL string as an IA5String (tag 6, class 2)
			rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)})

			dp := distributionPoint{
				DistributionPoint: distributionPointName{
					FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName}, // Tag 0 for FullName choice
				},
			}
			crlDp = append(crlDp, dp)
		}

		ret[n].Value, err = asn1.Marshal(crlDp) // Marshal the slice of distributionPoint
		if err != nil {
			return
		}
		n++
	}

	// ... final return ...
	return ret[:n], nil // Return only the used part of the slice
}

src/pkg/crypto/x509/x509_test.go の変更

func TestCreateSelfSignedCertificate(t *testing.T) {
	// ... existing test setup ...

	template := Certificate{
		// ... existing template fields ...
		PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
		PermittedDNSDomains: []string{".example.com", "example.com"},
		// Added CRL Distribution Points to the template
		CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
	}

	// ... certificate creation ...

	// Added check for CRL Distribution Points
	if !reflect.DeepEqual(cert.CRLDistributionPoints, template.CRLDistributionPoints) {
		t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints)
	}

	// ... existing checks ...
}

関連リンク

  • RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile:

参考にした情報源リンク