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

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

このコミットは、Go言語の crypto/x509 パッケージに、RFC 5280で定義されているX.509証明書の拡張機能である「Authority Information Access (AIA)」のサポートを追加するものです。これにより、証明書から発行元証明書やOCSPレスポンダーのURLなどの情報を抽出・利用できるようになります。

コミット

コミットハッシュ: f99158c8ad70a47ba51ce63f0e9a54d25e267292 Author: Paul van Brouwershaven paul@vanbrouwershaven.com Date: Thu Jun 27 17:16:25 2013 -0400

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

https://github.com/golang/go/commit/f99158c8ad70a47ba51ce63f0e9a54d25e267292

元コミット内容

crypto/x509: Added RFC 5280, section 4.2.2.1 Authority Information Access

R=agl, agl
CC=gobot, golang-dev
https://golang.org/cl/10245048

変更の背景

X.509証明書は、公開鍵基盤 (PKI) においてエンティティの身元を検証するために広く使用されています。これらの証明書には、証明書チェーンの構築や失効状態の確認など、様々な追加情報を含めるための「拡張機能」が定義されています。

RFC 5280は、X.509 Public Key Infrastructure (PKIX) の証明書と証明書失効リスト (CRL) のプロファイルに関する標準を定めています。そのセクション 4.2.2.1 で定義されている「Authority Information Access (AIA)」拡張は、証明書の発行元に関する情報や、証明書の失効状態を確認するためのOCSP (Online Certificate Status Protocol) サービスへのアクセス方法を提供します。

このコミット以前のGo言語の crypto/x509 パッケージは、AIA拡張の情報を適切にパースしたり、証明書に含めたりする機能を持っていませんでした。この機能がないと、証明書を利用するアプリケーションが、証明書チェーンを自動的に構築したり、OCSPを利用してリアルタイムで失効状態を確認したりする際に、追加の手動設定や外部ライブラリへの依存が必要になる可能性がありました。

この変更は、Go言語の crypto/x509 パッケージがX.509証明書の標準に完全に準拠し、より堅牢で自己完結型の証明書検証機能を提供するために不可欠でした。これにより、Goアプリケーションが証明書ベースの認証や通信を行う際に、より効率的かつ安全に証明書情報を利用できるようになります。

前提知識の解説

X.509 証明書と拡張機能

X.509証明書は、公開鍵とそれに関連するエンティティ(個人、サーバーなど)の身元を紐付けるデジタル文書です。これらは、認証局 (CA) によって署名され、信頼の連鎖を形成します。 X.509証明書には、標準的なフィールド(発行者、サブジェクト、有効期間など)に加えて、追加の情報を格納するための「拡張機能 (Extensions)」の仕組みがあります。これらの拡張機能は、証明書の用途を限定したり、追加のデータを提供したりするために使用されます。各拡張機能は、OID (Object Identifier) によって一意に識別され、クリティカル (Critical) または非クリティカル (Non-Critical) としてマークされます。クリティカルな拡張機能が理解できない場合、証明書は無効とみなされます。

ASN.1 (Abstract Syntax Notation One)

ASN.1は、データ構造を記述するための標準的な記法であり、通信プロトコルにおいてデータのエンコーディングとデコーディングを可能にします。X.509証明書は、ASN.1で定義された構造を持ち、DER (Distinguished Encoding Rules) などのエンコーディングルールに従ってバイナリ形式で表現されます。Go言語の encoding/asn1 パッケージは、このASN.1データのマーシャリングとアンマーシャリングを扱います。

OID (Object Identifier)

OIDは、情報オブジェクトを一意に識別するための国際標準です。ドットで区切られた一連の数字で構成されます。X.509証明書の拡張機能やアルゴリズム、ポリシーなどは、それぞれ固有のOIDを持っています。 このコミットで関連するOIDは以下の通りです。

  • oidExtensionAuthorityInfoAccess (1.3.6.1.5.5.7.1.1): Authority Information Access拡張機能のOID。
  • oidAuthorityInfoAccessOcsp (1.3.6.1.5.5.7.48.1): OCSPサービスへのアクセス方法を示すOID。
  • oidAuthorityInfoAccessIssuers (1.3.6.1.5.5.7.48.2): 発行元証明書へのアクセス方法を示すOID。

OCSP (Online Certificate Status Protocol)

OCSPは、X.509証明書の失効状態をリアルタイムで確認するためのプロトコルです。証明書失効リスト (CRL) とは異なり、OCSPは特定の証明書の現在の状態(有効、失効、不明)を即座に問い合わせることができます。AIA拡張には、OCSPレスポンダーのURLを含めることができ、これによりクライアントは証明書の失効状態を効率的に確認できます。

CA Issuers (発行元証明書)

AIA拡張は、現在の証明書を発行したCAの証明書を取得するためのURLも提供できます。これにより、クライアントは証明書チェーンを構築する際に、必要な中間CA証明書を自動的に発見し、ダウンロードすることが可能になります。これは、特にブラウザやアプリケーションが信頼できるルート証明書ストアに直接含まれていない中間証明書を必要とする場合に役立ちます。

技術的詳細

RFC 5280, Section 4.2.2.1 Authority Information Access (AIA) 拡張は、証明書に以下の情報を含めることを可能にします。

  1. OCSPレスポンダーの場所: 証明書の失効状態を問い合わせるためのOCSPサービスのURI (Uniform Resource Identifier)。
  2. 発行元証明書の場所: この証明書を発行したCAの証明書を取得するためのURI。

AIA拡張は、AccessDescription のシーケンスとして構造化されます。各 AccessDescription は、accessMethod (OID) と accessLocation (GeneralName) のペアで構成されます。

  • accessMethod: どのような情報にアクセスできるかを示すOID。
    • id-ad-ocsp (OID 1.3.6.1.5.5.7.48.1): OCSPサービスへのアクセス。
    • id-ad-caIssuers (OID 1.3.6.1.5.5.7.48.2): 発行元CA証明書へのアクセス。
  • accessLocation: 情報がどこにあるかを示す GeneralName。通常は uniformResourceIdentifier (URI) として表現されます。

このコミットでは、Go言語の crypto/x509 パッケージがこの構造をパースし、Goの Certificate 構造体にマッピングする機能を追加しています。具体的には、Certificate 構造体に OCSPServerIssuingCertificateURL という新しいフィールドが追加され、証明書をパースする際にAIA拡張からこれらの情報が抽出されて格納されます。また、証明書を構築する際にも、これらのフィールドに設定された値に基づいてAIA拡張が生成されるようになります。

AIA拡張は通常、非クリティカルとしてマークされます。これは、AIA拡張を理解できないアプリケーションでも証明書自体は有効とみなされるべきであることを意味します。

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

このコミットでは、主に以下の2つのファイルが変更されています。

  1. src/pkg/crypto/x509/x509.go: X.509証明書の構造体定義、パースロジック、および構築ロジックが含まれる主要なファイル。
    • Certificate 構造体への新しいフィールドの追加。
    • authorityInfoAccess 構造体の定義。
    • parseCertificate 関数におけるAIA拡張のパース処理の追加。
    • buildExtensions 関数におけるAIA拡張の構築処理の追加。
    • 関連するOIDの定義。
  2. src/pkg/crypto/x509/x509_test.go: crypto/x509 パッケージのテストファイル。
    • AIA拡張を含む証明書の生成とパースを検証するための新しいテストケースの追加。

コアとなるコードの解説

src/pkg/crypto/x509/x509.go

Certificate 構造体の変更

type Certificate struct {
	SubjectKeyId   []byte
	AuthorityKeyId []byte

	// RFC 5280, 4.2.2.1 (Authority Information Access)
	OCSPServer            []string
	IssuingCertificateURL []string

	// Subject Alternate Name values
	DNSNames       []string
	EmailAddresses []string

Certificate 構造体に、AIA拡張から抽出される情報を格納するための2つの新しいスライスフィールドが追加されました。

  • OCSPServer []string: OCSPレスポンダーのURLのリスト。
  • IssuingCertificateURL []string: 発行元証明書のURLのリスト。

authorityInfoAccess 構造体の定義

// RFC 5280, 4.2.2.1
type authorityInfoAccess struct {
	Method   asn1.ObjectIdentifier
	Location asn1.RawValue
}

これは、AIA拡張の内部構造である AccessDescription をGoの構造体として表現したものです。

  • Method: アクセス方法を示すOID(例: OCSPまたはCA Issuers)。
  • Location: アクセス先のURIを含む asn1.RawValueRawValue は、ASN.1のタグ、クラス、バイト列を直接保持します。

OIDの定義

var (
	oidExtensionCertificatePolicies   = []int{2, 5, 29, 32}
	oidExtensionNameConstraints       = []int{2, 5, 29, 30}
	oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31}
	oidExtensionAuthorityInfoAccess   = []int{1, 3, 6, 1, 5, 5, 7, 1, 1}
)

var (
	oidAuthorityInfoAccessOcsp    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1}
	oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2}
)

AIA拡張自体と、その内部で使用されるOCSPおよびCA IssuersのOIDが定義されています。これらは、ASN.1のパースおよび構築時に、特定の拡張機能やアクセス方法を識別するために使用されます。

parseCertificate 関数におけるパース処理

		} else if e.Id.Equal(oidExtensionAuthorityInfoAccess) {
			// RFC 5280 4.2.2.1: Authority Information Access
			var aia []authorityInfoAccess
			if _, err = asn1.Unmarshal(e.Value, &aia); err != nil {
				return nil, err
			}

			for _, v := range aia {
				// GeneralName: uniformResourceIdentifier [6] IA5String
				if v.Location.Tag != 6 {
					continue
				}
				if v.Method.Equal(oidAuthorityInfoAccessOcsp) {
					out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes))
				} else if v.Method.Equal(oidAuthorityInfoAccessIssuers) {
					out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes))
				}
			}
		}

parseCertificate 関数は、証明書の拡張機能をループ処理し、各拡張機能のOIDに基づいて適切な処理を行います。 oidExtensionAuthorityInfoAccess に一致する拡張機能が見つかった場合、その値 (e.Value) を authorityInfoAccess 構造体のスライスにASN.1アンマーシャルします。 その後、このスライスをイテレートし、各 authorityInfoAccess エントリの Method (OID) をチェックします。

  • oidAuthorityInfoAccessOcsp であれば、Location.Bytes を文字列として out.OCSPServer に追加します。
  • oidAuthorityInfoAccessIssuers であれば、Location.Bytes を文字列として out.IssuingCertificateURL に追加します。 v.Location.Tag != 6 のチェックは、GeneralNameuniformResourceIdentifier (URI) がタグ 6 を持つことを確認しています。これにより、URI以外の形式のロケーションが誤ってパースされるのを防ぎます。

buildExtensions 関数における構築処理

	ret = make([]pkix.Extension, 10 /* maximum number of elements. */) // 9 -> 10 に変更
	// ... (既存の拡張機能の構築ロジック) ...

	if len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0 {
		ret[n].Id = oidExtensionAuthorityInfoAccess
		var aiaValues []authorityInfoAccess
		for _, name := range template.OCSPServer {
			aiaValues = append(aiaValues, authorityInfoAccess{
				Method:   oidAuthorityInfoAccessOcsp,
				Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)},
			})
		}
		for _, name := range template.IssuingCertificateURL {
			aiaValues = append(aiaValues, authorityInfoAccess{
				Method:   oidAuthorityInfoAccessIssuers,
				Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)},
			})
		}
		ret[n].Value, err = asn1.Marshal(aiaValues)
		if err != nil {
			return
		}
		n++
	}

buildExtensions 関数は、Certificate テンプレートに基づいて証明書の拡張機能を構築します。 ret スライスの初期サイズが 9 から 10 に増やされ、AIA拡張のためのスペースが確保されています。 template.OCSPServer または template.IssuingCertificateURL のいずれかに値が設定されている場合、AIA拡張を構築します。

  • oidExtensionAuthorityInfoAccess を拡張機能のOIDとして設定します。
  • template.OCSPServer の各URLに対して、oidAuthorityInfoAccessOcsp メソッドとURIを含む authorityInfoAccess 構造体を作成し、aiaValues スライスに追加します。
  • template.IssuingCertificateURL の各URLに対しても同様に、oidAuthorityInfoAccessIssuers メソッドとURIを含む authorityInfoAccess 構造体を作成し、aiaValues スライスに追加します。
  • 構築された aiaValues スライスをASN.1マーシャルし、拡張機能の値 (ret[n].Value) として設定します。

src/pkg/crypto/x509/x509_test.go

テストケースの追加

func TestCreateSelfSignedCertificate(t *testing.T) {
	template := Certificate{
		// ... 既存のフィールド ...
		BasicConstraintsValid: true,
		IsCA: true,

		OCSPServer:            []string{"http://ocsp.example.com"},
		IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"},

		DNSNames:       []string{"test.example.com"},
		EmailAddresses: []string{"gopher@golang.org"},
		IPAddresses:    []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},
		// ...
	}

	// ... (証明書の生成とパース) ...

	if !reflect.DeepEqual(cert.OCSPServer, template.OCSPServer) {
		t.Errorf("%s: OCSP servers differ from template. Got %v, want %v", test.name, cert.OCSPServer, template.OCSPServer)
	}

	if !reflect.DeepEqual(cert.IssuingCertificateURL, template.IssuingCertificateURL) {
		t.Errorf("%s: Issuing certificate URLs differ from template. Got %v, want %v", test.name, cert.IssuingCertificateURL, template.IssuingCertificateURL)
	}
	// ...
}

TestCreateSelfSignedCertificate テスト関数に、OCSPServerIssuingCertificateURL フィールドを含む Certificate テンプレートが追加されました。 テストでは、このテンプレートから自己署名証明書を生成し、それをパースし直した結果の cert オブジェクトの OCSPServerIssuingCertificateURL が、元の template の値と一致するかどうかを reflect.DeepEqual を使って検証しています。これにより、AIA拡張が正しくエンコードおよびデコードされることが保証されます。

関連リンク

参考にした情報源リンク