[インデックス 12523] ファイルの概要
このコミットは、Go言語の crypto/x509
パッケージにおけるWindowsシステムでの証明書検証ロジックの改善に関するものです。具体的には、WindowsのCryptoAPIを利用した証明書チェーンの検証において、Server Gated Crypto (SGC) 証明書を許可するように変更が加えられています。また、既存の systemVerify
関数内のコードが複数の補助関数に分割され、可読性と保守性が向上しています。
コミット
commit 3133b14b307103b79117a033ddf1ac9d0f7a24d0
Author: Mikkel Krautz <mikkel@krautz.dk>
Date: Thu Mar 8 11:28:04 2012 -0500
crypto/x509: allow server gated crypto in windows systemVerify
Also factors out some code into functions to make
systemVerify easier to read.
R=rsc, agl
CC=golang-dev
https://golang.org/cl/5781054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3133b14b307103b79117a033ddf1ac9d0f7a24d0
元コミット内容
元のコミットメッセージは以下の通りです。
「crypto/x509: allow server gated crypto in windows systemVerify」 「Also factors out some code into functions to make systemVerify easier to read.」
これは、Windows環境における crypto/x509
パッケージの証明書検証機能において、Server Gated Crypto (SGC) 証明書を許可するように変更を加えたことを示しています。さらに、systemVerify
関数内のコードを複数の関数に分割し、コードの可読性を向上させたことも言及されています。
変更の背景
この変更の背景には、以下の点が考えられます。
-
Server Gated Crypto (SGC) の互換性問題: Server Gated Crypto (SGC) は、かつて輸出規制の対象となっていた暗号化技術であり、一部の古いブラウザやシステムでは、SGC証明書が必須となる場合がありました。Go言語の
crypto/x509
パッケージがWindowsのCryptoAPIを利用して証明書を検証する際、SGC証明書が正しく扱われない場合、互換性の問題が発生し、正当な証明書であっても検証に失敗する可能性がありました。このコミットは、このような互換性の問題を解消し、より広範な証明書に対応することを目的としています。コミットメッセージにも「Both IE and Chrome allow certificates with Server Gated Crypto as well. Some certificates in the wild require them.」とあり、実際の環境でSGC証明書が使用されていることが示唆されています。 -
コードの可読性と保守性の向上:
systemVerify
関数は、WindowsのCryptoAPIを呼び出して証明書チェーンを構築・検証する複雑なロジックを含んでいました。この関数が肥大化すると、コードの理解やデバッグが困難になります。コードを複数の小さな関数に分割することで、各関数の役割が明確になり、コード全体の可読性と保守性が向上します。これは、ソフトウェア開発における一般的なベストプラクティスであり、将来的な機能追加やバグ修正を容易にします。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
X.509 証明書: X.509は、公開鍵証明書の標準フォーマットです。WebサイトのSSL/TLS通信などで広く利用されており、公開鍵とそれに対応するエンティティ(Webサイト、個人など)の身元情報を紐付けます。証明書は、信頼された認証局(CA)によって署名され、その信頼の連鎖(証明書チェーン)を通じて検証されます。
-
証明書チェーン (Certificate Chain): 証明書チェーンは、エンドエンティティ証明書(例: Webサイトの証明書)から始まり、中間認証局(Intermediate CA)の証明書を介して、最終的に信頼されたルート認証局(Root CA)の証明書に至る一連の証明書の階層構造です。証明書の検証は、このチェーンを辿り、各証明書が正しく署名され、有効期限内であるかなどを確認することで行われます。
-
Windows CryptoAPI: Windows CryptoAPI (Cryptographic Application Programming Interface) は、Windowsオペレーティングシステムが提供する暗号化サービスへのインターフェースです。これには、証明書の管理、暗号化操作、ハッシュ計算など、様々な機能が含まれます。Go言語の
crypto/x509
パッケージは、Windows環境で証明書を検証する際に、このCryptoAPIを利用してシステムレベルの証明書ストアや検証ロジックと連携します。 -
Server Gated Crypto (SGC): Server Gated Crypto (SGC) は、かつて米国政府の輸出規制により、暗号化の強度に制限があった時代に登場した技術です。規制緩和前は、海外への輸出が許可される暗号化の強度が制限されており、SGCは、規制対象外の弱い暗号化しかサポートしないクライアント(古いブラウザなど)に対しても、より強力な暗号化(通常は128ビット)を提供するための仕組みでした。SGC証明書は、特定の拡張キー使用法 (Extended Key Usage, EKU) OID(Object Identifier)を持つことで識別されます。このコミットで追加された
OID_SERVER_GATED_CRYPTO
(1.3.6.1.4.1.311.10.3.3) とOID_SGC_NETSCAPE
(2.16.840.1.113730.4.1) がそれにあたります。 -
OID (Object Identifier): OIDは、情報オブジェクトを一意に識別するための国際標準の命名メカニズムです。X.509証明書では、証明書の拡張機能やアルゴリズムなどを識別するために使用されます。
技術的詳細
このコミットでは、主に以下の技術的な変更が行われています。
-
Server Gated Crypto (SGC) OID の追加:
src/pkg/syscall/ztypes_windows.go
ファイルに、SGCに関連する2つの新しいOIDが追加されました。OID_SERVER_GATED_CRYPTO = []byte("1.3.6.1.4.1.311.10.3.3\\x00")
OID_SGC_NETSCAPE = []byte("2.16.840.1.113730.4.1\\x00")
- これらのOIDは、
crypto/x509/root_windows.go
のsystemVerify
関数内で、サーバー認証 (OID_PKIX_KP_SERVER_AUTH
) と共にpara.RequestedUsage.Usage.UsageIdentifiers
に追加されます。 para.RequestedUsage.Type
がsyscall.USAGE_MATCH_TYPE_AND
からsyscall.USAGE_MATCH_TYPE_OR
に変更されました。これにより、証明書がOID_PKIX_KP_SERVER_AUTH
、OID_SERVER_GATED_CRYPTO
、またはOID_SGC_NETSCAPE
のいずれかの拡張キー使用法を持つ場合に、検証が成功するようになります。これは、SGC証明書がサーバー認証目的で利用されることを許可するための重要な変更です。
-
systemVerify
関数のリファクタリング:systemVerify
関数内のロジックが、以下の3つの新しい補助関数に分割されました。extractSimpleChain
:syscall.CertSimpleChain
から最終的な証明書チェーンを抽出し、[]*Certificate
型に変換します。これは、Windows CryptoAPIが返す低レベルの構造体から、Go言語のx509.Certificate
オブジェクトのリストを構築する役割を担います。checkChainTrustStatus
: 証明書チェーンの信頼ステータスをチェックし、syscall.CERT_TRUST_NO_ERROR
以外のエラーをGoのエラー型に変換します。これにより、有効期限切れ (CERT_TRUST_IS_NOT_TIME_VALID
) や不明な認証局 (UnknownAuthorityError
) などの一般的な信頼エラーを適切に処理できます。checkChainSSLServerPolicy
:CertVerifyCertificateChainPolicy
を呼び出して、証明書チェーンがSSL/TLSサーバーのポリシーに適合しているかを検証します。これには、DNS名の一致 (CERT_E_CN_NO_MATCH
) や、有効期限切れ (CERT_E_EXPIRED
)、信頼されていないルート (CERT_E_UNTRUSTEDROOT
) などのSSL固有のエラーチェックが含まれます。
- これらの関数への分割により、
systemVerify
関数はより簡潔になり、各検証ステップの責任が明確になりました。
-
エラーハンドリングの改善:
- 新しい補助関数内で、より具体的なエラー型 (
CertificateInvalidError
,HostnameError
,UnknownAuthorityError
) を返すようにエラーハンドリングが改善されました。これにより、検証失敗の原因をより詳細に特定できるようになります。
- 新しい補助関数内で、より具体的なエラー型 (
これらの変更により、Go言語の crypto/x509
パッケージは、Windows環境においてSGC証明書を適切に処理できるようになり、同時にコードベースの品質も向上しました。
コアとなるコードの変更箇所
主要な変更は src/pkg/crypto/x509/root_windows.go
と src/pkg/syscall/ztypes_windows.go
の2つのファイルにあります。
src/pkg/crypto/x509/root_windows.go
- 新しい関数の追加:
extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error)
checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error
checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error
systemVerify
関数の変更:para.RequestedUsage.Type
の設定ロジックが変更され、SGC OIDが追加されました。- 以前
systemVerify
関数内に直接記述されていた証明書チェーンの信頼ステータスチェックとSSLサーバーポリシーチェックのロジックが、新しく作成されたcheckChainTrustStatus
とcheckChainSSLServerPolicy
関数への呼び出しに置き換えられました。 - 証明書チェーンの抽出ロジックも
extractSimpleChain
関数に移動されました。
src/pkg/syscall/ztypes_windows.go
- 新しいOIDの定義:
OID_SERVER_GATED_CRYPTO = []byte("1.3.6.1.4.1.311.10.3.3\\x00")
OID_SGC_NETSCAPE = []byte("2.16.840.1.113730.4.1\\x00")
コアとなるコードの解説
src/pkg/crypto/x509/root_windows.go
の変更点
systemVerify
関数の変更
// systemVerify is like Verify, except that it uses CryptoAPI calls
// to build certificate chains and verify them.
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
// ... (省略) ...
para := new(syscall.CertChainPara)
para.Size = uint32(unsafe.Sizeof(*para))
// If there's a DNSName set in opts, assume we're verifying
// a certificate from a TLS server.
if hasDNSName {
oids := []*byte{
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
// Both IE and Chrome allow certificates with
// Server Gated Crypto as well. Some certificates
// in the wild require them.
&syscall.OID_SERVER_GATED_CRYPTO[0],
&syscall.OID_SGC_NETSCAPE[0],
}
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR // ここが変更点
para.RequestedUsage.Usage.Length = uint32(len(oids))
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
} else {
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
para.RequestedUsage.Usage.Length = 0
para.RequestedUsage.Usage.UsageIdentifiers = nil
}
// ... (省略) ...
err = checkChainTrustStatus(c, chainCtx) // 新しい関数呼び出し
if err != nil {
return nil, err
}
if hasDNSName {
err = checkChainSSLServerPolicy(c, chainCtx, opts) // 新しい関数呼び出し
if err != nil {
return nil, err
}
}
chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount)) // 新しい関数呼び出し
if err != nil {
return nil, err
}
chains = append(chains, chain)
return chains, nil
}
この変更により、systemVerify
関数は、DNS名が指定されている場合に、サーバー認証 (OID_PKIX_KP_SERVER_AUTH
) に加えて、Server Gated Crypto (OID_SERVER_GATED_CRYPTO
および OID_SGC_NETSCAPE
) のOIDも証明書の要求された使用法として考慮するようになりました。USAGE_MATCH_TYPE_OR
を使用することで、これらのOIDのいずれかが存在すれば検証が続行されます。また、複雑な検証ロジックが checkChainTrustStatus
, checkChainSSLServerPolicy
, extractSimpleChain
の各関数に委譲され、systemVerify
の役割がより明確になりました。
新しい補助関数
-
extractSimpleChain
: この関数は、Windows CryptoAPIが返すsyscall.CertSimpleChain
構造体から、Go言語のx509.Certificate
オブジェクトのリストを抽出します。unsafe.Pointer
を使用してCの構造体からGoのスライスに変換し、各証明書のバイトデータをコピーしてParseCertificate
で解析しています。 -
checkChainTrustStatus
: この関数は、chainCtx.TrustStatus.ErrorStatus
をチェックし、証明書チェーンの基本的な信頼ステータス(例:有効期限切れ)を検証します。エラーがある場合は、対応するGoのエラー型(CertificateInvalidError
やUnknownAuthorityError
)を返します。 -
checkChainSSLServerPolicy
: この関数は、syscall.CertVerifyCertificateChainPolicy
を呼び出して、SSL/TLSサーバー証明書としてのポリシー検証を行います。これには、サーバー名の一致(DNSNameの検証)や、SSL固有のエラー(例:CERT_E_CN_NO_MATCH
)の処理が含まれます。
src/pkg/syscall/ztypes_windows.go
の変更点
var (
OID_PKIX_KP_SERVER_AUTH = []byte("1.3.6.1.5.5.7.3.1\\x00")
OID_SERVER_GATED_CRYPTO = []byte("1.3.6.1.4.1.311.10.3.3\\x00") // 追加
OID_SGC_NETSCAPE = []byte("2.16.840.1.113730.4.1\\x00") // 追加
)
このファイルでは、Server Gated Cryptoに関連する2つの新しいOIDがバイトスライスとして定義されています。これらのOIDは、Windows CryptoAPIがSGC証明書を識別するために使用するものです。
関連リンク
- Go言語の
crypto/x509
パッケージのドキュメント: https://pkg.go.dev/crypto/x509 - Windows CryptoAPI のドキュメント (Microsoft Learn): https://learn.microsoft.com/en-us/windows/win32/seccrypto/cryptography-api-portal
- X.509 証明書に関する情報: https://ja.wikipedia.org/wiki/X.509
参考にした情報源リンク
- Server Gated Cryptography (SGC) の説明: https://www.globalsign.com/ja/ssl-information-center/what-is-server-gated-cryptography-sgc
- Go言語のコードレビューシステム (Gerrit) の変更セット: https://golang.org/cl/5781054 (コミットメッセージに記載されているリンク)
- MicrosoftのOIDに関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/object-identifiers--oids-
- PKIX Extended Key Usage OID: https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12 (RFC 5280, X.509証明書の拡張キー使用法に関する標準)
- Netscape SGC OID: https://www.oid-info.com/get/2.16.840.1.113730.4.1 (OID Repository)
- Microsoft SGC OID: https://www.oid-info.com/get/1.3.6.1.4.1.311.10.3.3 (OID Repository)
syscall.CertVerifyCertificateChainPolicy
関数に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certverifycertificatechainpolicysyscall.CertChainPolicyPara
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_policy_parasyscall.SSLExtraCertChainPolicyPara
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-ssl_extra_cert_chain_policy_parasyscall.CertChainPolicyStatus
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_policy_statussyscall.CertSimpleChain
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_simple_chainsyscall.CertChainElement
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_elementsyscall.CertContext
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_contextsyscall.CertChainContext
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_contextsyscall.CertChainPara
構造体に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_parasyscall.CERT_TRUST_NO_ERROR
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/cert-trust-statussyscall.CERT_TRUST_IS_NOT_TIME_VALID
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/cert-trust-statussyscall.CERT_E_EXPIRED
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/cert-chain-policy-statussyscall.CERT_E_CN_NO_MATCH
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/cert-chain-policy-statussyscall.CERT_E_UNTRUSTEDROOT
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/seccrypto/cert-chain-policy-statussyscall.USAGE_MATCH_TYPE_AND
およびsyscall.USAGE_MATCH_TYPE_OR
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_usage_matchsyscall.AUTHTYPE_SERVER
定数に関する情報: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-ssl_extra_cert_chain_policy_parasyscall.StringToUTF16Ptr
関数に関する情報: https://pkg.go.dev/syscall#StringToUTF16Ptrunsafe.Pointer
に関する情報: https://pkg.go.dev/unsafe#PointerParseCertificate
関数に関する情報: https://pkg.go.dev/crypto/x509#ParseCertificateCertificateInvalidError
,HostnameError
,UnknownAuthorityError
エラー型に関する情報: https://pkg.go.dev/crypto/x509#CertificateInvalidError