[インデックス 10870] ファイルの概要
このコミットでは、src/pkg/crypto/crypto.go
、src/pkg/crypto/tls/common.go
、src/pkg/crypto/tls/handshake_client.go
、src/pkg/crypto/tls/key_agreement.go
の4つのファイルが変更されています。
コミット
- コミットハッシュ:
2ca4a61658b5561cc807fc1cebe177169ff28034
- Author: Adam Langley agl@golang.org
- Date: Mon Dec 19 10:39:30 2011 -0500
- コミットメッセージ:
crypto/tls: don't assume an RSA private key in the API. We still very much assume it in the code, but with this change in place we can implement other things later without changing and users of the package. Fixes #2319. R=golang-dev, bradfitz, r CC=golang-dev https://golang.org/cl/5489073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2ca4a61658b5561cc807fc1cebe177169ff28034
元コミット内容
crypto/tls: don't assume an RSA private key in the API.
We still very much assume it in the code, but with this change in
place we can implement other things later without changing and users
of the package.
Fixes #2319.
R=golang-dev, bradfitz, r
CC=golang-dev
https://golang.org/cl/5489073
変更の背景
このコミットの背景には、Go言語の crypto/tls
パッケージが、TLS (Transport Layer Security) プロトコルにおける秘密鍵の型として、RSA秘密鍵のみを前提としていたという問題があります。これは、Issue #2319「TLS interface only supports RSA keys」として報告されていました。
TLSプロトコルでは、サーバー認証や鍵交換のために秘密鍵が使用されますが、RSA鍵以外にもECDSA (Elliptic Curve Digital Signature Algorithm) やEd25519などの異なるアルゴリズムに基づく秘密鍵が存在します。しかし、当時の crypto/tls
パッケージのAPI設計では、Certificate
構造体の PrivateKey
フィールドが *rsa.PrivateKey
型に固定されており、これにより他の種類の秘密鍵を扱うことができませんでした。
この制限は、将来的にRSA以外の鍵アルゴリズムをサポートしようとした際に、APIの破壊的変更を伴う可能性がありました。そのため、APIレベルでRSA秘密鍵への依存を解消し、より汎用的な秘密鍵の型を許容するように変更することが求められました。これにより、コード内部では引き続きRSA秘密鍵を前提とする部分が残るものの、APIの利用者にとっては将来的な拡張性を持たせることが可能になります。
前提知識の解説
crypto/tls
パッケージ
crypto/tls
はGo言語の標準ライブラリの一部で、TLS (Transport Layer Security) プロトコルを実装するためのパッケージです。TLSは、インターネット上での安全な通信を確立するために広く使用されており、ウェブブラウザとサーバー間のHTTPS通信などで利用されています。このパッケージは、TLSクライアントとサーバーの実装、証明書の管理、鍵交換、暗号化などの機能を提供します。
RSA秘密鍵
RSA (Rivest–Shamir–Adleman) は、公開鍵暗号方式の一つで、デジタル署名や鍵交換に広く用いられています。RSA秘密鍵は、RSA公開鍵とペアになる鍵で、暗号化されたデータの復号やデジタル署名の生成に使用されます。Go言語の crypto/rsa
パッケージで扱われます。
Go言語の interface
Go言語の interface
は、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースで定義されたすべてのメソッドを実装していれば、その型はそのインターフェースを満たすとみなされます。これにより、具体的な型に依存しない汎用的なコードを書くことが可能になります。例えば、io.Reader
インターフェースは Read
メソッドを持つ任意の型を受け入れることができます。
型アサーション (Type Assertion
)
Go言語の型アサーションは、インターフェース型の値が、特定の具象型であるかどうかをチェックし、もしそうであればその具象型の値として取り出すための構文です。
value.(Type)
の形式で記述され、value
が Type
型であれば、その値が返されます。もし value
が Type
型でなければ、パニックが発生します。安全な形式として value, ok := value.(Type)
があり、この場合 ok
は型アサーションが成功したかどうかを示すブール値になります。
このコミットでは、crypto.PrivateKey
インターフェース型として受け取った秘密鍵を、内部処理で *rsa.PrivateKey
型として扱うために型アサーションが使用されています。
技術的詳細
このコミットの主要な技術的変更は、crypto/tls
パッケージのAPIがRSA秘密鍵に強く依存している状態を緩和することです。具体的には、以下の点が変更されています。
-
crypto.PrivateKey
インターフェースの導入:src/pkg/crypto/crypto.go
に、PrivateKey interface{}
という空のインターフェースが導入されました。これは、任意の型を秘密鍵として表現できる汎用的な型となります。 -
Certificate
構造体のPrivateKey
フィールドの型変更:src/pkg/crypto/tls/common.go
内のCertificate
構造体において、PrivateKey
フィールドの型が*rsa.PrivateKey
から新しく定義されたcrypto.PrivateKey
インターフェース型に変更されました。これにより、Certificate
構造体はRSA秘密鍵だけでなく、将来的に他のアルゴリズムの秘密鍵も保持できるようになります。コメントには// supported types: *rsa.PrivateKey
と追記され、現時点ではRSA秘密鍵のみがサポートされていることが明示されています。 -
内部コードでの型アサーションの利用:
src/pkg/crypto/tls/handshake_client.go
とsrc/pkg/crypto/tls/key_agreement.go
の内部処理では、Certificate.PrivateKey
がcrypto.PrivateKey
型になったため、RSA秘密鍵に特化した関数 (rsa.SignPKCS1v15
,rsa.DecryptPKCS1v15SessionKey
) に渡す際に、.(*rsa.PrivateKey)
という型アサーションを使用して、crypto.PrivateKey
型の値を*rsa.PrivateKey
型に変換しています。これは、APIは汎用化されたものの、実際の暗号処理のコードはまだRSA秘密鍵に依存しているためです。
この変更により、crypto/tls
パッケージの利用者は、将来的にRSA以外の秘密鍵を扱う際に、Certificate
構造体の定義自体が変わることを心配する必要がなくなりました。パッケージの内部実装はまだRSAに特化していますが、APIの柔軟性が向上し、将来の拡張が容易になっています。
コアとなるコードの変更箇所
src/pkg/crypto/crypto.go
--- a/src/pkg/crypto/crypto.go
+++ b/src/pkg/crypto/crypto.go
@@ -71,3 +71,6 @@ func RegisterHash(h Hash, f func() hash.Hash) {
}\n \thashes[h] = f\n }\n+\n+// PrivateKey represents a private key using an unspecified algorithm.\n+type PrivateKey interface{}\n```
### `src/pkg/crypto/tls/common.go`
```diff
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -5,8 +5,8 @@
package tls
import (
+\t"crypto"
\t"crypto/rand"
-\t"crypto/rsa"
\t"crypto/x509"
\t"io"
\t"strings"
@@ -255,7 +255,7 @@ func (c *Config) BuildNameToCertificate() {
// A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct {
Certificate [][]byte
-\tPrivateKey *rsa.PrivateKey
+\tPrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
OCSPStaple []byte
src/pkg/crypto/tls/handshake_client.go
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -234,7 +234,7 @@ func (c *Conn) clientHandshake() error {
digest := make([]byte, 0, 36)
digest = finishedHash.serverMD5.Sum(digest)
digest = finishedHash.serverSHA1.Sum(digest)
-\t\tsigned, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest)
+\t\tsigned, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
if err != nil {
return c.sendAlert(alertInternalError)
}
src/pkg/crypto/tls/key_agreement.go
--- a/src/pkg/crypto/tls/key_agreement.go
+++ b/src/pkg/crypto/tls/key_agreement.go
@@ -44,7 +44,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
ciphertext = ckx.ciphertext[2:]
}
-\terr = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
+\terr = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
if err != nil {
return nil, err
}
@@ -147,7 +147,7 @@ Curve:
copy(serverECDHParams[4:], ecdhePublic)
md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
-\tsig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1)
+\tsig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
}
コアとなるコードの解説
src/pkg/crypto/crypto.go
の変更
type PrivateKey interface{}
が追加されました。これは、Go言語のcrypto
パッケージ全体で利用可能な、秘密鍵を表すための汎用的なインターフェースです。このインターフェースはメソッドを持たないため、任意の型がこのインターフェースを満たすことができます。これにより、秘密鍵の具体的な型に依存しないAPI設計が可能になります。
src/pkg/crypto/tls/common.go
の変更
import "crypto/rsa"
が削除され、代わりにimport "crypto"
が追加されました。これは、Certificate
構造体のPrivateKey
フィールドの型が*rsa.PrivateKey
からcrypto.PrivateKey
インターフェースに変更されたためです。Certificate
構造体のPrivateKey
フィールドの型が*rsa.PrivateKey
からcrypto.PrivateKey
に変更されました。これにより、Certificate
構造体はRSA秘密鍵だけでなく、将来的に他のアルゴリズムの秘密鍵も保持できるようになります。// supported types: *rsa.PrivateKey
というコメントが追加され、現時点ではRSA秘密鍵のみがサポートされていることが明示されています。これは、APIは汎用化されたものの、内部実装はまだRSAに依存していることを示唆しています。
src/pkg/crypto/tls/handshake_client.go
および src/pkg/crypto/tls/key_agreement.go
の変更
- これらのファイルでは、
Certificate
構造体のPrivateKey
フィールドがcrypto.PrivateKey
インターフェース型になったことに伴い、既存のRSA秘密鍵を扱う関数 (rsa.SignPKCS1v15
やrsa.DecryptPKCS1v15SessionKey
) に渡す際に、型アサーション.(*rsa.PrivateKey)
が追加されました。 - 例えば、
c.config.Certificates[0].PrivateKey
はcrypto.PrivateKey
型ですが、rsa.SignPKCS1v15
関数は*rsa.PrivateKey
型を期待するため、c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey)
と記述することで、インターフェース型の値を具象型である*rsa.PrivateKey
に変換しています。 - この変更は、APIの柔軟性を高めつつも、既存のRSAに特化した暗号処理ロジックを維持するための過渡的な措置です。将来的に他の鍵アルゴリズムがサポートされる際には、これらの型アサーションを含むコードも、より汎用的なインターフェースベースの処理に置き換えられる可能性があります。
関連リンク
- Go Issue #2319: https://github.com/golang/go/issues/2319
参考にした情報源リンク
- Go
crypto/tls
パッケージのCertificate
構造体に関するドキュメント: https://pkg.go.dev/crypto/tls#Certificate (現在のドキュメントでは、PrivateKey
フィールドがcrypto.Signer
インターフェースを実装する任意の型を受け入れることが明記されています。これは、このコミット以降のさらなる進化を示しています。)