[インデックス 13649] ファイルの概要
このコミットは、Go言語の crypto/tls
パッケージにおいて、クライアント証明書の検証ロジックを修正するものです。具体的には、クライアント証明書が正しく「クライアント認証」の目的で利用されることを検証器に明示的に伝えるために、x509.ExtKeyUsageClientAuth
を KeyUsages
として追加しています。これにより、クライアント証明書が誤ってサーバー証明書として扱われ、不適切な ExtKeyUsageServerAuth
の検証を要求される問題を解決します。
コミット
commit 67924c1b602c170239eec821c3aea67b6ab682c7
Author: Mikkel Krautz <mikkel@krautz.dk>
Date: Sat Aug 18 15:50:33 2012 -0700
crypto/tls: explicitly require ExtKeyUsageClientAuth for client certs
If we aren't explicit about the KeyUsages, the verifier
will treat the certificate as a server certificate and require
it to have a ExtKeyUsageServerAuth key usage.
R=golang-dev
CC=golang-dev
https://golang.org/cl/6453148
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/67924c1b602c170239eec821c3aea67b6ab682c7
元コミット内容
Go言語の crypto/tls
パッケージにおいて、クライアント証明書の検証時に ExtKeyUsageClientAuth
を明示的に要求するように変更しました。これにより、KeyUsages
が明示されていない場合に、検証器が証明書をサーバー証明書として扱い、ExtKeyUsageServerAuth
の鍵用途を要求してしまう問題を修正します。
変更の背景
TLS (Transport Layer Security) プロトコルでは、クライアントがサーバーに対して自身の身元を証明するためにクライアント証明書を提示する場合があります。このクライアント証明書には、その証明書がどのような目的で使用されるべきかを示す「鍵用途 (Key Usage)」や「拡張鍵用途 (Extended Key Usage)」という情報が含まれています。
Go言語の crypto/tls
パッケージにおけるサーバー側のハンドシェイク処理では、クライアントから提示された証明書を検証する際に、x509.VerifyOptions
構造体を用いて検証オプションを設定します。この VerifyOptions
には、検証時に考慮すべき鍵用途を指定する KeyUsages
フィールドがあります。
このコミット以前のコードでは、クライアント証明書の検証時に KeyUsages
フィールドが明示的に設定されていませんでした。その結果、x509
パッケージの証明書検証器は、KeyUsages
が指定されていない場合に、デフォルトの動作としてその証明書をサーバー証明書として解釈してしまう可能性がありました。サーバー証明書として解釈された場合、検証器は通常、その証明書が「サーバー認証 (ExtKeyUsageServerAuth)」の拡張鍵用途を持っていることを要求します。
しかし、クライアント証明書は通常、「クライアント認証 (ExtKeyUsageClientAuth)」の拡張鍵用途を持つべきであり、サーバー認証の鍵用途を持つ必要はありません。このミスマッチにより、正当なクライアント証明書であっても、検証器が不適切な鍵用途を要求するために検証に失敗するというバグが発生していました。
このコミットは、この問題を解決するために、クライアント証明書の検証時に ExtKeyUsageClientAuth
を明示的に KeyUsages
として設定することで、検証器が証明書を正しくクライアント証明書として扱い、適切な鍵用途の検証を行うように修正しています。
前提知識の解説
TLS (Transport Layer Security)
TLSは、インターネット上で安全な通信を行うための暗号化プロトコルです。ウェブブラウザとウェブサーバー間のHTTPS通信などで広く利用されています。TLSハンドシェイクの過程で、クライアントとサーバーは互いの身元を確認し、暗号化された通信チャネルを確立します。
クライアント証明書認証
通常のTLS通信では、クライアントがサーバーの証明書を検証してサーバーの身元を確認しますが、クライアント証明書認証では、サーバーもクライアントの証明書を検証してクライアントの身元を確認します。これは、より高いセキュリティが求められるシステム(例:企業内システム、VPNなど)で利用されます。
X.509 証明書
X.509は、公開鍵証明書の標準フォーマットです。TLS通信で使用される証明書は、このX.509形式に従っています。X.509証明書には、公開鍵、所有者の情報、発行者の情報、有効期間、そして証明書の用途を示す拡張情報などが含まれます。
鍵用途 (Key Usage) と拡張鍵用途 (Extended Key Usage)
X.509証明書には、その証明書に含まれる公開鍵がどのような目的で使用されるべきかを示すフィールドがあります。
-
鍵用途 (Key Usage): 証明書の公開鍵がデジタル署名、鍵の暗号化、鍵の合意、証明書署名、CRL署名などの基本的な用途に利用できるかを指定します。
-
拡張鍵用途 (Extended Key Usage - EKU): 鍵用途をさらに細分化し、特定のアプリケーションやプロトコルにおける鍵の用途を指定します。例えば、ウェブサーバー認証 (
ExtKeyUsageServerAuth
)、クライアント認証 (ExtKeyUsageClientAuth
)、コード署名 (ExtKeyUsageCodeSigning
) などがあります。それぞれの用途はOID (Object Identifier) で識別されます。ExtKeyUsageClientAuth
: この証明書がクライアント認証のために使用されることを示します。ExtKeyUsageServerAuth
: この証明書がサーバー認証のために使用されることを示します。
Go言語の crypto/x509
パッケージ
Go言語の標準ライブラリには、X.509証明書を扱うための crypto/x509
パッケージが含まれています。このパッケージは、証明書のパース、検証、生成などの機能を提供します。
x509.Certificate
: X.509証明書を表す構造体です。x509.VerifyOptions
: 証明書検証時のオプションを指定するための構造体です。この構造体には、検証対象の証明書チェーン、信頼されたルート証明書、現在の時刻、そして検証時に期待される鍵用途などを指定するフィールドが含まれます。x509.ExtKeyUsage
: 拡張鍵用途の型です。
技術的詳細
Go言語の crypto/tls
パッケージにおけるサーバー側のハンドシェイク処理 (handshake_server.go
) では、クライアントから提示された証明書チェーンを検証する際に、x509.VerifyOptions
を構築して x509.Certificate.Verify()
メソッドを呼び出します。
コミット前のコードでは、VerifyOptions
の KeyUsages
フィールドが空のままでした。x509.Certificate.Verify()
メソッドは、VerifyOptions
の KeyUsages
フィールドが空の場合、証明書の ExtKeyUsage
フィールドを検査し、もし ExtKeyUsage
が存在しないか、または特定の用途が指定されていない場合に、デフォルトの動作としてサーバー認証の用途を推測しようとすることがあります。
具体的には、x509
パッケージの内部ロジックにおいて、KeyUsages
が明示的に指定されていない場合、証明書がサーバー証明書として使用されることを意図していると見なされ、ExtKeyUsageServerAuth
が存在するかどうかを検証するロジックが働いていました。これは、ウェブサーバーの証明書検証では一般的な動作ですが、クライアント証明書には適用されるべきではありません。
このコミットでは、VerifyOptions
を初期化する際に、KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
を明示的に追加しています。これにより、x509.Certificate.Verify()
メソッドは、クライアント証明書が ExtKeyUsageClientAuth
を持っていることを厳密に要求するようになります。この明示的な指定により、検証器がクライアント証明書を誤ってサーバー証明書として解釈し、不適切な ExtKeyUsageServerAuth
の検証を要求する問題を回避できます。
この変更は、TLSハンドシェイクの FindCipherSuite
ラベルの直下にあるクライアント証明書検証ロジックに適用されています。
コアとなるコードの変更箇所
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -211,6 +211,7 @@ FindCipherSuite:
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
for i, cert := range certs {
コアとなるコードの解説
変更は src/pkg/crypto/tls/handshake_server.go
ファイルの FindCipherSuite
ラベル内の、クライアント証明書検証のための x509.VerifyOptions
構造体の初期化部分にあります。
追加された行は以下の通りです。
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
この行は、x509.VerifyOptions
構造体の KeyUsages
フィールドに、x509.ExtKeyUsageClientAuth
という単一の要素を持つ ExtKeyUsage
型のスライスを割り当てています。
KeyUsages
:x509.VerifyOptions
のフィールドで、証明書が持つべき拡張鍵用途のリストを指定します。[]x509.ExtKeyUsage
:ExtKeyUsage
型のスライス(配列)を意味します。x509.ExtKeyUsageClientAuth
:crypto/x509
パッケージで定義されている定数で、証明書がクライアント認証のために使用されることを示す拡張鍵用途のOIDに対応します。
この変更により、クライアントから提示された証明書を検証する際、x509.Certificate.Verify()
メソッドは、その証明書が ExtKeyUsageClientAuth
を持っていることを必須条件として検証するようになります。これにより、検証器がクライアント証明書を誤ってサーバー証明書として扱い、不適切な ExtKeyUsageServerAuth
を要求する問題を根本的に解決し、クライアント証明書認証の堅牢性を向上させています。
関連リンク
- Go言語
crypto/tls
パッケージドキュメント: https://pkg.go.dev/crypto/tls - Go言語
crypto/x509
パッケージドキュメント: https://pkg.go.dev/crypto/x509 - X.509 Extended Key Usage (RFC 5280): https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12
参考にした情報源リンク
- Go言語の公式ドキュメント
- RFC 5280 (Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile)
- Stack Overflow や技術ブログ記事 (一般的なX.509証明書と鍵用途に関する情報)