[インデックス 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) 拡張は、証明書に以下の情報を含めることを可能にします。
- OCSPレスポンダーの場所: 証明書の失効状態を問い合わせるためのOCSPサービスのURI (Uniform Resource Identifier)。
- 発行元証明書の場所: この証明書を発行したCAの証明書を取得するためのURI。
AIA拡張は、AccessDescription
のシーケンスとして構造化されます。各 AccessDescription
は、accessMethod
(OID) と accessLocation
(GeneralName) のペアで構成されます。
accessMethod
: どのような情報にアクセスできるかを示すOID。id-ad-ocsp
(OID1.3.6.1.5.5.7.48.1
): OCSPサービスへのアクセス。id-ad-caIssuers
(OID1.3.6.1.5.5.7.48.2
): 発行元CA証明書へのアクセス。
accessLocation
: 情報がどこにあるかを示すGeneralName
。通常はuniformResourceIdentifier
(URI) として表現されます。
このコミットでは、Go言語の crypto/x509
パッケージがこの構造をパースし、Goの Certificate
構造体にマッピングする機能を追加しています。具体的には、Certificate
構造体に OCSPServer
と IssuingCertificateURL
という新しいフィールドが追加され、証明書をパースする際にAIA拡張からこれらの情報が抽出されて格納されます。また、証明書を構築する際にも、これらのフィールドに設定された値に基づいてAIA拡張が生成されるようになります。
AIA拡張は通常、非クリティカルとしてマークされます。これは、AIA拡張を理解できないアプリケーションでも証明書自体は有効とみなされるべきであることを意味します。
コアとなるコードの変更箇所
このコミットでは、主に以下の2つのファイルが変更されています。
src/pkg/crypto/x509/x509.go
: X.509証明書の構造体定義、パースロジック、および構築ロジックが含まれる主要なファイル。Certificate
構造体への新しいフィールドの追加。authorityInfoAccess
構造体の定義。parseCertificate
関数におけるAIA拡張のパース処理の追加。buildExtensions
関数におけるAIA拡張の構築処理の追加。- 関連するOIDの定義。
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.RawValue
。RawValue
は、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
のチェックは、GeneralName
のuniformResourceIdentifier
(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
テスト関数に、OCSPServer
と IssuingCertificateURL
フィールドを含む Certificate
テンプレートが追加されました。
テストでは、このテンプレートから自己署名証明書を生成し、それをパースし直した結果の cert
オブジェクトの OCSPServer
と IssuingCertificateURL
が、元の template
の値と一致するかどうかを reflect.DeepEqual
を使って検証しています。これにより、AIA拡張が正しくエンコードおよびデコードされることが保証されます。
関連リンク
- Go CL: https://golang.org/cl/10245048
- GitHub Commit: https://github.com/golang/go/commit/f99158c8ad70a47ba51ce63f0e9a54d25e267292
参考にした情報源リンク
- RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile: https://datatracker.ietf.org/doc/html/rfc5280
- 特に Section 4.2.2.1. Authority Information Access
- Online Certificate Status Protocol (OCSP): https://datatracker.ietf.org/doc/html/rfc6960
- ASN.1 (Abstract Syntax Notation One) の概要: https://www.itu.int/en/ITU-T/asn1/Pages/default.aspx
- Go言語
crypto/x509
パッケージドキュメント: https://pkg.go.dev/crypto/x509