crypto/tls: provide a way to access local certificate used to set up a connection
要約
概要
crypto/tls パッケージの ConnectionState 構造体に LocalCertificate フィールドを追加し、TLSハンドシェイクで使用されたローカル証明書(自身が相手に提示した証明書)を取得できるようにするproposalです。
ステータス変更
likely_accept → accepted
2026年3月11日に「likely accept」と判定された後、1週間の意見募集期間を経て反対意見がなく、2026年3月18日に正式に承認されました。既に実装PR(#75699)も提出されており、crypto/tls/common.go 等の関連ファイルへの変更も完成しています。
技術的背景
現状の問題点
crypto/tls の Conn.ConnectionState() は接続のセキュリティ情報を返しますが、PeerCertificates(相手方の証明書)は含まれているものの、自分自身が提示したローカル証明書の情報が含まれていませんでした。
証明書の選択ロジックは GetCertificate や GetClientCertificate のコールバックだけでなく、NameToCertificate による自動選択も含まれるため、アプリケーションコードから「実際にどの証明書が使われたか」を把握することが困難でした。
// Before: ローカル証明書を知る手段がない
conn, _ := tls.Dial("tcp", "example.com:443", config)
state := conn.ConnectionState()
fmt.Println(state.PeerCertificates) // 相手の証明書は取得可能
// state.LocalCertificate は存在しない
// 実際にどの証明書を提示したかが不明
提案された解決策
ConnectionState 構造体に LocalCertificate *tls.Certificate フィールドを追加します。
type ConnectionState struct {
// ... 既存フィールド ...
// LocalCertificate is the certificate presented to the peer, if any, during
// the handshake. This field is only populated for connections which are not
// resumed (DidResume is false).
LocalCertificate *Certificate
}
重要な設計判断として、セッション再開(TLSセッションチケットによる resumption)の場合は nil となります。これはセッションチケットのサイズを増大させないための判断です。
これによって何ができるようになるか
接続に実際に使用されたローカル証明書をプログラムから直接参照できるようになります。
ユースケース1: 証明書の有効期限監視
state := tlsConn.ConnectionState()
if state.LocalCertificate != nil {
cert := state.LocalCertificate.Leaf
if time.Until(cert.NotAfter) < 30*24*time.Hour {
log.Printf("警告: 証明書が30日以内に期限切れ: %v", cert.NotAfter)
}
}
ユースケース2: gRPC Channelzによる接続状態の可視化
gRPC の channelz 機能では接続の詳細情報をユーザーに提示しており、ローカル証明書の情報(ID、有効期限、SAN等)を含める必要がありました。これまではワークアラウンドが必要でしたが、このフィールドにより標準的に取得可能になります。
ユースケース3: 外部認可サービスへの証明書情報の転送
gRPC の外部認可(ext_authz)機能では、クライアントとのハンドシェイクで使用したサーバ証明書の URI SAN や DNS SAN を認可サーバへ送信する必要があります。LocalCertificate によりこれが実装可能になります。
議論のハイライト
- 2018年の初期議論:
@FiloSottileがTLS 1.3との整合性を懸念し、長期間保留状態となった。TLS 1.3ではクライアント証明書が複数回要求される可能性があるという懸念が提起されたが、後に解決された。 - gRPCチームの継続的な要望: gRPC-Go チームが2018年から複数回にわたり優先度を上げるよう要請し、proxyless service mesh や外部認可機能の実装に必要な機能として具体的なユースケースを示したことが承認の後押しとなった。
- セッション再開時の扱い:
@FiloSottileの指摘により、TLSセッション再開(resumption)時の動作が議論された。セッションチケットのサイズ増大を避けるため、DidResume == trueの場合はnilを返す設計が採用された。 - 将来の拡張性: 再開セッションでも証明書情報が必要になった場合は、
Configにセッションチケットへ情報を含めるオプションを追加する方向で対応することが言及されている。 - 設計のシンプルさ: 当初
LocalCertificates(複数形)も検討されたが、最終的にシンプルな単数形のLocalCertificate *Certificateとして確定した。