[インデックス 12478] ファイルの概要
このコミットは、Go言語の標準ライブラリにおけるTLS (Transport Layer Security) およびX.509証明書処理に関する重要な変更を導入しています。具体的には、証明書ルートフェッチャー(信頼されたルート証明書を取得するメカニズム)の crypto/tls
パッケージから crypto/x509
パッケージへの移動、およびWindows環境における証明書検証にWindows CryptoAPIを利用する機能の追加が主な内容です。
影響を受けるファイルは以下の通りです。
src/pkg/crypto/tls/common.go
: TLS設定の共通部分。ルートCAの取得ロジックが削除されています。src/pkg/crypto/tls/handshake_client.go
: TLSクライアントハンドシェイク処理。RootCAs
の参照方法が変更されています。src/pkg/crypto/tls/root_test.go
: TLSルート証明書関連のテスト。Windows固有のホスト名検証テストが追加されています。src/pkg/crypto/tls/root_windows.go
: 削除されたファイル。Windows固有のルート証明書取得ロジックがcrypto/x509
に移動しました。src/pkg/crypto/tls/tls.go
: TLS接続の確立ロジック。ServerName
の推論ロジックが変更されています。src/pkg/crypto/x509/root.go
: 新規追加されたファイル。システムルート証明書プールを管理する共通ロジックが定義されています。src/pkg/crypto/{tls => x509}/root_darwin.go
: ファイル名が変更され、crypto/tls
からcrypto/x509
へ移動。macOSにおけるルート証明書取得ロジック。src/pkg/crypto/{tls => x509}/root_stub.go
: ファイル名が変更され、crypto/tls
からcrypto/x509
へ移動。スタブ実装(特定のOSでルート証明書取得がサポートされない場合)。src/pkg/crypto/{tls => x509}/root_unix.go
: ファイル名が変更され、crypto/tls
からcrypto/x509
へ移動。Unix系OSにおけるルート証明書取得ロジック。src/pkg/crypto/x509/root_windows.go
: 新規追加されたファイル。Windows CryptoAPIを利用した証明書検証およびチェーン構築の主要ロジック。src/pkg/crypto/x509/verify.go
: X.509証明書の検証ロジック。VerifyOptions
の変更と、Windowsでのシステム検証への分岐が追加されています。src/pkg/crypto/x509/verify_test.go
: X.509証明書検証のテスト。Windows固有の検証パスをスキップするテストケースが追加されています。src/pkg/syscall/syscall_windows.go
: Windowsシステムコール定義。CryptoAPI関連の新しい関数が追加されています。src/pkg/syscall/zsyscall_windows_386.go
: Windows 386アーキテクチャ向けシステムコールプロシージャ定義。CryptoAPI関連のプロシージャが追加されています。src/pkg/syscall/zsyscall_windows_amd64.go
: Windows AMD64アーキテクチャ向けシステムコールプロシージャ定義。CryptoAPI関連のプロシージャが追加されています。src/pkg/syscall/ztypes_windows.go
: Windowsシステムコールで使用されるデータ型定義。CryptoAPI関連の定数や構造体が追加されています。
コミット
commit a324a5ac2081f3760aefaf27ab47efbd59fecb17
Author: Mikkel Krautz <mikkel@krautz.dk>
Date: Wed Mar 7 13:12:35 2012 -0500
crypto/x509: new home for root fetchers; build chains using Windows API
This moves the various CA root fetchers from crypto/tls into crypto/x509.
The move was brought about by issue 2997. Windows doesn't ship with all
its root certificates, but will instead download them as-needed when using
CryptoAPI for certificate verification.
This CL changes crypto/x509 to verify a certificate using the system root
CAs when VerifyOptions.RootCAs == nil. On Windows, this verification is
now implemented using Windows's CryptoAPI. All other root fetchers are
unchanged, and still use Go's own verification code.
The CL also fixes the hostname matching logic in crypto/tls/tls.go, in
order to be able to test whether hostname mismatches are honored by the
Windows verification code.
The move to crypto/x509 also allows other packages to use the OS-provided
root certificates, instead of hiding them inside the crypto/tls package.
Fixes #2997.
R=agl, golang-dev, alex.brainman, rsc, mikkel
CC=golang-dev
https://golang.org/cl/5700087
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a324a5ac2081f3760aefaf27ab47efbd59fecb17
元コミット内容
crypto/x509: new home for root fetchers; build chains using Windows API
This moves the various CA root fetchers from crypto/tls into crypto/x509.
The move was brought about by issue 2997. Windows doesn't ship with all
its root certificates, but will instead download them as-needed when using
CryptoAPI for certificate verification.
This CL changes crypto/x509 to verify a certificate using the system root
CAs when VerifyOptions.RootCAs == nil. On Windows, this verification is
now implemented using Windows's CryptoAPI. All other root fetchers are
unchanged, and still use Go's own verification code.
The CL also fixes the hostname matching logic in crypto/tls/tls.go, in
order to be able to test whether hostname mismatches are honored by the
Windows verification code.
The move to crypto/x509 also allows other packages to use the OS-provided
root certificates, instead of hiding them inside the crypto/tls package.
Fixes #2997.
変更の背景
この変更の主な背景は、Go言語のTLS実装がWindows環境で直面していた証明書検証の問題、特にGo issue 2997に起因します。
従来のGoのTLS実装では、信頼されたルート証明書(CA証明書)はGo自身の crypto/tls
パッケージ内で管理され、各OSのシステム証明書ストアからルート証明書を読み込むロジックもこのパッケージ内に存在していました。しかし、Windowsの証明書管理システム(CryptoAPI)は、他のOSとは異なる振る舞いをします。
Windowsは、すべてのルート証明書をローカルにバンドルしているわけではありません。代わりに、必要に応じてインターネットからルート証明書をダウンロードして検証を行う「自動ルート証明書更新メカニズム」を持っています。GoのTLS実装がこのWindowsの特性を考慮せずに独自の検証ロジックを使用していたため、Windows環境で特定の証明書チェーンの検証に失敗する問題が発生していました。特に、中間証明書がローカルに存在しない場合、Windows CryptoAPIは自動的にダウンロードして検証を試みますが、Goの独自実装ではそれができませんでした。
この問題を解決するため、以下の変更が必要とされました。
- ルート証明書フェッチャーの汎用化: ルート証明書を取得するロジックを
crypto/tls
からcrypto/x509
へ移動することで、TLS以外の他のパッケージもOSが提供するルート証明書を利用できるようになります。これにより、証明書検証の基盤がより汎用的になります。 - Windows CryptoAPIの活用: Windows環境では、Go独自の証明書検証ロジックではなく、WindowsのCryptoAPIを利用して証明書チェーンの構築と検証を行うように変更することで、Windowsの自動ルート証明書更新メカニズムの恩恵を受けられるようになります。これにより、WindowsユーザーはGoアプリケーションがシステムレベルで信頼されている証明書を適切に検証できるようになります。
- ホスト名マッチングのテスト: Windows CryptoAPIによる検証がホスト名不一致を適切に処理するかどうかをテストするために、
crypto/tls/tls.go
のホスト名マッチングロジックも修正されました。
これらの変更により、GoアプリケーションがWindows環境でより堅牢かつ互換性のある証明書検証を行えるようになることが目指されました。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
1. X.509 証明書と公開鍵基盤 (PKI)
- X.509 証明書: デジタル証明書の標準フォーマットの一つで、公開鍵と、その公開鍵の所有者に関する情報(氏名、組織、ドメイン名など)を紐付け、認証局 (CA) によって署名されたものです。これにより、公開鍵が正当なエンティティに属することを保証します。
- 公開鍵基盤 (PKI): 公開鍵暗号方式を用いて、デジタル署名や暗号化などのセキュリティサービスを提供するシステム全体を指します。PKIは、X.509証明書、認証局 (CA)、登録局 (RA)、証明書失効リスト (CRL) などの要素で構成されます。
2. 認証局 (CA) とルート証明書、中間証明書
- 認証局 (CA): デジタル証明書を発行し、その正当性を保証する信頼された第三者機関です。CAは自身の秘密鍵で証明書に署名します。
- ルート証明書: CAが自身に発行する自己署名証明書で、信頼の起点となります。オペレーティングシステムやブラウザには、信頼されたルート証明書のリスト(ルートストア)が事前に組み込まれています。
- 中間証明書: ルートCAが直接エンドエンティティ証明書(ウェブサイトの証明書など)を発行するのではなく、セキュリティ上の理由から、中間CAに証明書を発行させることが一般的です。この中間CAの証明書が中間証明書です。中間証明書はルートCAによって署名され、エンドエンティティ証明書は中間CAによって署名されます。
3. 証明書チェーン (Certificate Chain)
証明書チェーンは、エンドエンティティ証明書から始まり、中間証明書を介して最終的に信頼されたルート証明書に至るまでの証明書の連なりです。検証プロセスでは、このチェーンを辿り、各証明書が上位の証明書によって正しく署名されているか、有効期限内か、失効していないかなどを確認します。
4. TLS (Transport Layer Security) / SSL (Secure Sockets Layer)
- TLS/SSL: インターネット上で安全な通信を行うための暗号化プロトコルです。ウェブサイトのHTTPS通信などで使用され、クライアントとサーバー間のデータが盗聴や改ざんから保護されることを保証します。TLSハンドシェイクの過程で、サーバーは自身の証明書をクライアントに提示し、クライアントはその証明書を検証します。
5. Windows CryptoAPI
- Windows CryptoAPI (Cryptographic Application Programming Interface): Microsoft Windowsオペレーティングシステムが提供する暗号化サービス群です。これには、証明書の管理、検証、暗号化、ハッシュ計算など、様々なセキュリティ機能が含まれます。
- 証明書ストア: Windows CryptoAPIは、証明書を保存・管理するための「証明書ストア」という概念を持っています。システムストア(例:
ROOT
、CA
、MY
)やユーザーごとのストアなどがあります。 - 自動ルート証明書更新: Windowsは、信頼されたルート証明書が不足している場合、必要に応じてMicrosoftのサーバーから自動的にダウンロードして更新するメカニズムを持っています。これにより、ユーザーが手動でルート証明書を管理する必要がなくなります。
6. Go言語の crypto/tls
と crypto/x509
パッケージ
crypto/tls
: Go言語でTLS/SSL通信を実装するためのパッケージです。クライアントとサーバー間のセキュアな接続を確立するために使用されます。crypto/x509
: Go言語でX.509証明書を解析、生成、検証するためのパッケージです。証明書の構造を扱い、署名の検証や証明書チェーンの構築などを行います。
このコミットは、Goの crypto/tls
が行っていた証明書検証の一部を crypto/x509
に移管し、特にWindows環境ではGo独自の検証ロジックではなく、Windows CryptoAPIの機能を活用することで、Windowsの自動ルート証明書更新などの恩恵を受けられるようにするものです。
技術的詳細
このコミットの技術的詳細は、主に以下の3つの側面に集約されます。
-
ルート証明書フェッチャーの
crypto/x509
への移管:- これまで
crypto/tls
パッケージ内に存在していた、OS固有のルート証明書を取得するロジック(root_darwin.go
,root_stub.go
,root_unix.go
,root_windows.go
)が、crypto/x509
パッケージに移動されました。 - これにより、
crypto/x509
パッケージがシステムルート証明書を管理する中心的な役割を担うようになります。crypto/tls
は、必要に応じてcrypto/x509
からシステムルート証明書を取得する形に変更されます。 - この変更により、TLS以外のGoアプリケーションも、OSが提供する信頼されたルート証明書を容易に利用できるようになり、Goの証明書検証基盤の汎用性と再利用性が向上します。
- これまで
-
Windows CryptoAPI を利用した証明書検証の導入:
- 最も重要な変更点の一つは、Windows環境において
crypto/x509.VerifyOptions.RootCAs
がnil
の場合(つまり、Goアプリケーションが独自のルート証明書プールを指定しない場合)に、Go独自の検証ロジックではなく、Windows CryptoAPI を利用して証明書検証を行うようになった点です。 - これは、Windowsが持つ「自動ルート証明書更新」機能に対応するためです。Windows CryptoAPIは、証明書チェーンの構築時に不足している中間証明書やルート証明書を必要に応じてインターネットからダウンロードして検証を試みます。Go独自の検証ロジックではこの機能を利用できませんでした。
- 新しい
src/pkg/crypto/x509/root_windows.go
ファイルには、Windows CryptoAPIの関数(CertOpenStore
,CertAddCertificateContextToStore
,CertGetCertificateChain
,CertVerifyCertificateChainPolicy
など)を呼び出すためのGoのラッパーが実装されています。 CertGetCertificateChain
は、与えられた証明書から信頼されたルート証明書までのチェーンを構築し、その信頼性を評価します。CertVerifyCertificateChainPolicy
は、SSLポリシー(ホスト名検証など)に基づいて証明書チェーンをさらに検証します。これにより、GoのVerifyHostname
ロジックとWindows CryptoAPIのホスト名検証が連携します。- この変更により、GoアプリケーションはWindows環境で、システムレベルで信頼されている証明書をより正確かつ堅牢に検証できるようになります。
- 最も重要な変更点の一つは、Windows環境において
-
VerifyOptions
の変更とテストの調整:crypto/x509.VerifyOptions
構造体のRoots
フィールドのコメントが更新され、「if nil, the system roots are used
」(nilの場合、システムルートが使用される)という説明が追加されました。これは、WindowsでのCryptoAPI利用のトリガー条件を明確に示しています。crypto/x509/verify.go
のVerify
メソッドに、opts.Roots == nil
かつruntime.GOOS == "windows"
の場合にc.systemVerify(&opts)
を呼び出す条件分岐が追加されました。これがWindows CryptoAPIを利用するエントリポイントとなります。- テストコード (
crypto/tls/root_test.go
,crypto/x509/verify_test.go
) も、この変更に合わせて調整されています。特に、Windows CryptoAPIによる検証が特定のテストケース(例: 中間証明書が不足しているケース)で異なる振る舞いをする可能性があるため、systemSkip
フラグが導入され、Windowsでのシステム検証時に一部のテストがスキップされるようになっています。これは、Go独自の検証とWindows CryptoAPIの検証の振る舞いの違いを許容するためです。
これらの変更は、Goの証明書検証機能のクロスプラットフォーム互換性を高めつつ、各OSのネイティブな証明書管理機能を最大限に活用するための重要なステップと言えます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は多岐にわたりますが、特に重要なのは以下のファイル群です。
-
src/pkg/crypto/tls/common.go
:rootCAs()
メソッドが削除されました。これは、ルートCAの取得ロジックがcrypto/x509
に移管されたためです。defaultRoots()
関数およびinitDefaultRoots()
関数が削除され、varDefaultRoots
変数も削除されました。これにより、crypto/tls
が独自のデフォルトルート証明書プールを管理する責任がなくなりました。
-
src/pkg/crypto/tls/handshake_client.go
:- クライアントハンドシェイクにおいて、証明書検証オプションの
Roots
フィールドがc.config.rootCAs()
からc.config.RootCAs
に直接変更されました。これにより、Config.RootCAs
がnil
の場合にcrypto/x509
がシステムルートを使用するロジックが適用されるようになります。
- クライアントハンドシェイクにおいて、証明書検証オプションの
-
src/pkg/crypto/{tls => x509}/root_darwin.go
,root_stub.go
,root_unix.go
:- これらのファイルは
crypto/tls
からcrypto/x509
ディレクトリに移動し、パッケージ名もtls
からx509
に変更されました。 initDefaultRoots()
関数はinitSystemRoots()
にリネームされ、varDefaultRoots
変数はsystemRoots
にリネームされました。これにより、OS固有のルート証明書取得ロジックがcrypto/x509
の管理下に置かれました。*Certificate
型にsystemVerify
メソッドが追加されましたが、これらのOSではまだ実装されておらず、nil, nil
を返します。
- これらのファイルは
-
src/pkg/crypto/x509/root.go
(新規):systemRootsPool()
関数が追加されました。これは、initSystemRoots()
を一度だけ実行し、OS固有のルート証明書プール (systemRoots
) を返すための共通エントリポイントです。
-
src/pkg/crypto/x509/root_windows.go
(新規):- このファイルは、Windows CryptoAPI を利用した証明書検証の核心部分です。
createStoreContext
関数: Goの*Certificate
オブジェクトと中間証明書 (opts.Intermediates
) から、Windows CryptoAPIが扱う*syscall.CertContext
とインメモリ証明書ストアを作成します。(*Certificate).systemVerify
メソッド:createStoreContext
を呼び出して検証対象の証明書コンテキストを作成します。syscall.CertGetCertificateChain
を呼び出し、Windows CryptoAPIに証明書チェーンの構築と基本的な信頼性検証を依頼します。- チェーンの信頼ステータス (
TrustStatus.ErrorStatus
) をチェックし、エラーがあればGoのCertificateInvalidError
やUnknownAuthorityError
に変換します。 - 構築されたチェーンの各要素をGoの
*Certificate
オブジェクトに変換し、結果として[][]*Certificate
を返します。 opts.DNSName
が設定されている場合、syscall.CertVerifyCertificateChainPolicy
をCERT_CHAIN_POLICY_SSL
ポリシーで呼び出し、SSLポリシー(ホスト名検証など)に基づいて追加の検証を行います。ここでホスト名不一致 (CERT_E_CN_NO_MATCH
) や有効期限切れ (CERT_E_EXPIRED
) などのエラーを検出します。
initSystemRoots()
関数: Windowsでは、この関数はsystemRoots
を初期化するだけで、実際のルート証明書の読み込みはsystemVerify
がCryptoAPIを通じて行います。
-
src/pkg/crypto/x509/verify.go
:VerifyOptions
構造体のRoots
フィールドのコメントが「// if nil, the system roots are used
」に変更されました。(*Certificate).Verify
メソッドに以下の重要な条件分岐が追加されました。
これにより、Windows環境でif opts.Roots == nil && runtime.GOOS == "windows" { return c.systemVerify(&opts) } if opts.Roots == nil { opts.Roots = systemRootsPool() }
RootCAs
が明示的に指定されていない場合にsystemVerify
が呼び出され、Windows CryptoAPIによる検証が実行されます。それ以外のOSや、RootCAs
が指定されている場合は、Go独自の検証ロジックが継続して使用されます。
-
src/pkg/syscall/syscall_windows.go
,zsyscall_windows_386.go
,zsyscall_windows_amd64.go
,ztypes_windows.go
:- Windows CryptoAPIの新しい関数(
CertOpenStore
,CertAddCertificateContextToStore
,CertGetCertificateChain
,CertFreeCertificateChain
,CertCreateCertificateContext
,CertFreeCertificateContext
,CertVerifyCertificateChainPolicy
)のGoのシステムコールラッパーが追加されました。 - これらの関数で使用される新しい定数(
X509_ASN_ENCODING
,PKCS_7_ASN_ENCODING
,CERT_STORE_PROV_MEMORY
,CERT_TRUST_NO_ERROR
など)や構造体(CertChainContext
,CertSimpleChain
,CertChainElement
,CertTrustStatus
,CertChainPara
,CertChainPolicyPara
,SSLExtraCertChainPolicyPara
,CertChainPolicyStatus
など)がztypes_windows.go
に定義されました。
- Windows CryptoAPIの新しい関数(
これらの変更により、Goの証明書検証スタックは、Windowsのネイティブな証明書管理機能と深く統合され、より堅牢で互換性の高い動作を実現しています。
コアとなるコードの解説
このコミットの核心は、Goの証明書検証ロジックがWindows環境でどのようにシステムネイティブな機能と連携するようになったか、そしてそのためにどのようなコードが追加・変更されたかにあります。
crypto/x509/verify.go
の Verify
メソッド
このメソッドは、GoにおけるX.509証明書検証の主要なエントリポイントです。変更後のコードは以下のようになっています。
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
// Use Windows's own verification and chain building.
if opts.Roots == nil && runtime.GOOS == "windows" {
return c.systemVerify(&opts)
}
if opts.Roots == nil {
opts.Roots = systemRootsPool()
}
err = c.isValid(leafCertificate, nil, &opts)
if err != nil {
return
}
if len(opts.DNSName) > 0 {
err = c.VerifyHostname(opts.DNSName)
if err != nil {
return
}
}
return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
}
- 条件分岐の追加: 最も重要な変更は、最初の
if
文です。opts.Roots == nil
: これは、GoアプリケーションがVerifyOptions
で明示的に信頼されたルート証明書プールを指定していないことを意味します。この場合、GoはOSが提供するシステムルート証明書を使用しようとします。runtime.GOOS == "windows"
: 現在の実行環境がWindowsであるかどうかをチェックします。- この両方の条件が真である場合、Goは
c.systemVerify(&opts)
を呼び出します。これは、Windows CryptoAPIを利用した検証ロジックへの委譲です。
systemRootsPool()
の利用: 上記の条件に合致しない場合(つまり、Windows以外のOS、またはWindowsでもopts.Roots
が指定されている場合)、opts.Roots
がnil
であればsystemRootsPool()
を呼び出してOS固有のシステムルート証明書プールを取得します。これは、crypto/tls
からcrypto/x509
に移動したOS固有のルート証明書取得ロジック(root_darwin.go
,root_unix.go
など)が提供するものです。- 既存ロジックの維持: その後の
c.isValid
やc.VerifyHostname
、c.buildChains
の呼び出しは、Go独自の証明書検証ロジックであり、Windows CryptoAPIを使用しない場合のフォールバックまたは追加検証として機能します。
crypto/x509/root_windows.go
の (*Certificate).systemVerify
メソッド
このメソッドは、Windows環境で opts.Roots == nil
の場合に呼び出される、Windows CryptoAPIを利用した証明書検証の具体的な実装です。
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
hasDNSName := opts != nil && len(opts.DNSName) > 0
// 1. 証明書コンテキストの作成
storeCtx, err := createStoreContext(c, opts)
if err != nil {
return nil, err
}
defer syscall.CertFreeCertificateContext(storeCtx)
// 2. 証明書チェーン構築パラメータの設定
para := new(syscall.CertChainPara)
para.Size = uint32(unsafe.Sizeof(*para))
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
if hasDNSName {
// サーバー認証用のOIDを設定
oids := []*byte{&syscall.OID_PKIX_KP_SERVER_AUTH[0]}
para.RequestedUsage.Usage.Length = uint32(len(oids))
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
}
var verifyTime *syscall.Filetime
if opts != nil && !opts.CurrentTime.IsZero() {
ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
verifyTime = &ft
}
// 3. CertGetCertificateChain によるチェーン構築と基本検証
var chainCtx *syscall.CertChainContext
err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
if err != nil {
return nil, err
}
defer syscall.CertFreeCertificateChain(chainCtx)
// 4. 基本的な信頼ステータスのチェック
if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
status := chainCtx.TrustStatus.ErrorStatus
switch status {
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return nil, CertificateInvalidError{c, Expired}
default:
return nil, UnknownAuthorityError{c}
}
}
// 5. 構築されたチェーンのGo形式への変換
simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(chainCtx.Chains))[:]
if chainCtx.ChainCount == 0 {
return nil, UnknownAuthorityError{c}
}
verifiedChain := simpleChains[int(chainCtx.ChainCount)-1] // 通常、最も信頼性の高いチェーンが返される
var chain []*Certificate
for i := 0; i < int(verifiedChain.NumElements); i++ {
cert := elements[i].CertContext
// ... (GoのCertificateオブジェクトへの変換ロジック) ...
chain = append(chain, parsedCert)
}
// 6. SSLポリシー(ホスト名検証など)の適用
if hasDNSName {
sslPara := &syscall.SSLExtraCertChainPolicyPara{
AuthType: syscall.AUTHTYPE_SERVER,
ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
}
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
para := &syscall.CertChainPolicyPara{
ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
}
para.Size = uint32(unsafe.Sizeof(*para))
status := syscall.CertChainPolicyStatus{}
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
if err != nil {
return nil, err
}
// 7. SSLポリシー検証結果のチェック
if status.Error != 0 {
switch status.Error {
case syscall.CERT_E_EXPIRED:
return nil, CertificateInvalidError{c, Expired}
case syscall.CERT_E_CN_NO_MATCH:
return nil, HostnameError{c, opts.DNSName}
case syscall.CERT_E_UNTRUSTEDROOT:
return nil, UnknownAuthorityError{c}
default:
return nil, UnknownAuthorityError{c}
}
}
}
chains = make([][]*Certificate, 1)
chains[0] = chain
return chains, nil
}
createStoreContext
: 検証対象のリーフ証明書と中間証明書を、Windows CryptoAPIが処理できる形式(CertContext
とインメモリ証明書ストア)に変換します。CertGetCertificateChain
: Windows CryptoAPIの主要な関数で、与えられた証明書から信頼されたルート証明書までのチェーンを構築します。この関数は、Windowsのシステム証明書ストアや、必要に応じて自動ダウンロードされるルート証明書を利用してチェーンを完成させます。- 信頼ステータスのチェック:
chainCtx.TrustStatus.ErrorStatus
を確認し、証明書が有効期限切れ (CERT_TRUST_IS_NOT_TIME_VALID
) や不明な認証局 (UnknownAuthorityError
) である場合にGoのエラーに変換します。 - チェーンの変換: 構築されたWindowsの証明書チェーン (
CertSimpleChain
,CertChainElement
) を、Goの[]*Certificate
スライスに変換します。 CertVerifyCertificateChainPolicy
:opts.DNSName
が指定されている場合、SSLポリシー (CERT_CHAIN_POLICY_SSL
) を適用して、ホスト名検証などの追加のセキュリティチェックを行います。これにより、GoのVerifyHostname
と同等の機能がWindowsネイティブで実行されます。ホスト名不一致 (CERT_E_CN_NO_MATCH
) やその他のSSLポリシー違反が検出された場合、適切なGoのエラーに変換されます。
この一連の処理により、GoアプリケーションはWindowsの強力な証明書管理機能とシームレスに連携し、より正確で信頼性の高い証明書検証を実現しています。特に、Windowsの自動ルート証明書更新機能の恩恵を受けられるようになった点が大きな改善です。
関連リンク
- Go Issue 2997: https://github.com/golang/go/issues/2997
- Go CL 5700087: https://golang.org/cl/5700087
参考にした情報源リンク
- Windows CryptoAPI Certificate Verification: https://learn.microsoft.com/en-us/windows/win32/seccrypto/certificate-verification-functions
- CertGetCertificateChain function: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetcertificatechain
- CertVerifyCertificateChainPolicy function: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certverifycertificatechainpolicy
- X.509 Certificates: https://en.wikipedia.org/wiki/X.509
- Transport Layer Security (TLS): https://en.wikipedia.org/wiki/Transport_Layer_Security
- Certificate Stores: https://learn.microsoft.com/en-us/windows/win32/seccrypto/certificate-stores
- Automatic Root Certificates Update: https://learn.microsoft.com/en-us/windows/win32/seccrypto/automatic-root-certificates-update