[インデックス 18046] ファイルの概要
このコミットは、Go言語のcrypto/x509
パッケージにおいて、X.509証明書の署名に使用されるデフォルトのハッシュアルゴリズムをSHA-1からSHA-256に変更し、さらに呼び出し元が署名アルゴリズムを明示的に指定できるようにする機能を追加したものです。これは、SHA-1のセキュリティ上の懸念と、MicrosoftによるX.509証明書におけるSHA-1の非推奨化ポリシーに対応するための重要な変更です。
コミット
commit ca3ff9251dbe34edb539b661a30222d0f3d755bd
Author: Adam Langley <agl@golang.org>
Date: Wed Dec 18 10:57:56 2013 -0500
crypto/x509: set default signature hash to SHA256 and allow override.
Previously the hash used when signing an X.509 certificate was fixed
and, for RSA, it was fixed to SHA1. Since Microsoft have announced the
deprecation of SHA1 in X.509 certificates, this change switches the
default to SHA256.
It also allows the hash function to be controlled by the caller by
setting the SignatureAlgorithm field of the template.
[1] http://blogs.technet.com/b/pki/archive/2013/11/12/sha1-deprecation-policy.aspx
Fixes #5302.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/40720047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ca3ff9251dbe34edb539b661a30222d0f3d755bd
元コミット内容
crypto/x509: set default signature hash to SHA256 and allow override.
以前は、X.509証明書に署名する際に使用されるハッシュは固定されており、RSAの場合、SHA-1に固定されていました。MicrosoftがX.509証明書におけるSHA-1の非推奨化を発表したため、この変更はデフォルトをSHA-256に切り替えます。
また、テンプレートのSignatureAlgorithm
フィールドを設定することで、呼び出し元がハッシュ関数を制御できるようにします。
[1] http://blogs.technet.com/b/pki/archive/2013/11/12/sha1-deprecation-policy.aspx
Fixes #5302.
変更の背景
この変更の主な背景は、SHA-1ハッシュアルゴリズムのセキュリティ上の脆弱性と、それに対応する業界の動向です。
-
SHA-1の脆弱性: SHA-1(Secure Hash Algorithm 1)は、かつて広く使用されていた暗号学的ハッシュ関数ですが、2005年頃から衝突攻撃(異なる入力から同じハッシュ値が生成されること)の可能性が指摘され始め、セキュリティ上の懸念が高まっていました。実際に、2017年にはSHA-1に対する実用的な衝突攻撃が成功したことがGoogleによって発表されています。このような脆弱性があるハッシュ関数をデジタル署名に使用することは、署名の偽造や証明書のなりすましのリスクを高めます。
-
MicrosoftによるSHA-1非推奨化ポリシー: コミットメッセージに記載されているように、Microsoftは2013年11月12日に、X.509証明書におけるSHA-1の非推奨化ポリシーを発表しました。このポリシーは、2017年1月1日以降、SHA-1で署名されたSSL/TLS証明書を主要なWebブラウザ(Internet Explorer、Edge)で信頼しないことを示唆していました。これにより、SHA-1を使用しているWebサイトやサービスは、互換性の問題やセキュリティ警告に直面する可能性がありました。
Go言語のcrypto/x509
パッケージは、X.509証明書の生成と検証を扱うため、この業界の動向に追従し、よりセキュアなハッシュアルゴリズムへの移行を促進する必要がありました。特に、RSA鍵を用いた署名においてデフォルトでSHA-1が使用されていたため、この部分の変更が急務でした。
このコミットは、デフォルトのハッシュアルゴリズムをSHA-256に切り替えることで、Goアプリケーションが生成する証明書のセキュリティを向上させ、将来的な互換性問題を回避することを目的としています。また、開発者が特定の要件に応じて署名アルゴリズムを明示的に選択できる柔軟性も提供しています。
前提知識の解説
このコミットを理解するためには、以下の暗号学およびPKI(公開鍵基盤)に関する基本的な知識が必要です。
-
X.509証明書:
- X.509は、公開鍵証明書の標準フォーマットです。デジタル証明書は、公開鍵と、その公開鍵の所有者(エンティティ)の身元情報を結びつける役割を果たします。
- WebサイトのSSL/TLS通信、コード署名、電子メールの暗号化など、様々なセキュリティプロトコルで利用されます。
- 証明書は、信頼された第三者である認証局(CA)によってデジタル署名されます。この署名により、証明書の改ざんが検出され、その正当性が保証されます。
-
デジタル署名:
- デジタル署名は、メッセージや文書の真正性、完全性、否認防止を保証するための暗号技術です。
- 仕組み:
- 署名者は、署名したいデータ(この場合はX.509証明書の内容)のハッシュ値を計算します。
- 計算されたハッシュ値を、署名者の秘密鍵で暗号化(署名)します。
- 署名されたハッシュ値(デジタル署名)を元のデータに添付して送信します。
- 検証者は、元のデータからハッシュ値を再計算します。
- 検証者は、署名者の公開鍵を使用してデジタル署名を復号し、元のハッシュ値を取得します。
- 両方のハッシュ値が一致すれば、データは改ざんされておらず、署名者が署名したことが確認できます。
- 重要性: X.509証明書の場合、CAが証明書に署名することで、その証明書がCAによって発行された正当なものであることを保証します。
-
ハッシュ関数(暗号学的ハッシュ関数):
- 任意の長さの入力データから、固定長の短い出力(ハッシュ値、メッセージダイジェスト、フィンガープリントとも呼ばれる)を生成する一方向関数です。
- 暗号学的ハッシュ関数の特性:
- 一方向性: ハッシュ値から元のデータを復元することが計算上困難である。
- 衝突耐性: 異なる入力から同じハッシュ値が生成されること(衝突)が計算上困難である。
- 原像計算困難性: 特定のハッシュ値を持つ入力を見つけることが計算上困難である。
- 第二原像計算困難性: 特定の入力と同じハッシュ値を持つ別の入力を見つけることが計算上困難である。
- SHA-1 (Secure Hash Algorithm 1): 160ビットのハッシュ値を生成します。かつて広く使われましたが、衝突攻撃の脆弱性が発見され、現在では非推奨とされています。
- SHA-2 (Secure Hash Algorithm 2): SHA-256, SHA-384, SHA-512など、複数のバリエーションがあります。それぞれ異なる長さのハッシュ値を生成し、SHA-1よりも高いセキュリティを提供します。SHA-256は256ビットのハッシュ値を生成し、現在広く推奨されているハッシュ関数の一つです。
-
公開鍵アルゴリズム (RSA, ECDSA):
- デジタル署名に使用される主要な公開鍵暗号アルゴリズムです。
- RSA: Rivest–Shamir–Adlemanの略。最も広く使用されている公開鍵暗号アルゴリズムの一つで、デジタル署名と鍵交換の両方に使用されます。
- ECDSA (Elliptic Curve Digital Signature Algorithm): 楕円曲線暗号に基づくデジタル署名アルゴリズムです。RSAよりも短い鍵長で同等のセキュリティレベルを提供できるため、モバイルデバイスなどリソースが限られた環境での利用が増えています。
これらの要素が組み合わさって、X.509証明書の信頼性とセキュリティが確立されます。このコミットは、その中でも特にデジタル署名におけるハッシュ関数の選択という、セキュリティの根幹に関わる部分を改善しています。
技術的詳細
このコミットは、Go言語のcrypto/x509
パッケージにおける証明書署名ロジックを修正し、以下の主要な変更を導入しています。
-
デフォルト署名ハッシュのSHA-256への変更:
- 以前は、
CreateCertificate
関数内でRSA秘密鍵が使用される場合、署名アルゴリズムはoidSignatureSHA1WithRSA
(SHA1WithRSA)に、ハッシュ関数はcrypto.SHA1
に固定されていました。 - このコミットにより、RSA秘密鍵の場合のデフォルトが
oidSignatureSHA256WithRSA
(SHA256WithRSA)とcrypto.SHA256
に変更されました。これにより、明示的な指定がない限り、よりセキュアなSHA-256が使用されるようになります。
- 以前は、
-
SignatureAlgorithm
フィールドによる署名アルゴリズムのオーバーライド:Certificate
構造体にSignatureAlgorithm
という新しいフィールドが追加され、証明書テンプレート作成時に署名アルゴリズムを明示的に指定できるようになりました。CreateCertificate
関数内で、template.SignatureAlgorithm
が0(デフォルト値)でない場合、ユーザーが指定した署名アルゴリズムが優先されます。- 指定された
SignatureAlgorithm
が、使用される秘密鍵のタイプ(RSA, ECDSAなど)と互換性があるかどうかの検証が追加されました。例えば、RSA鍵でECDSA署名アルゴリズムを指定することはできません。 - 指定された署名アルゴリズムに対応するハッシュ関数が利用可能かどうかもチェックされます。
-
署名アルゴリズム詳細のテーブル化:
signatureAlgorithmDetails
という新しいグローバル変数([]struct
のスライス)が導入されました。これは、各SignatureAlgorithm
に対応するOID(Object Identifier)、公開鍵アルゴリズムのタイプ、および使用されるcrypto.Hash
関数をマッピングするテーブルです。- これにより、
getSignatureAlgorithmFromOID
関数内の冗長なswitch
文が、このテーブルをループして検索する形に置き換えられ、コードの可読性と保守性が向上しました。新しい署名アルゴリズムが追加された場合も、このテーブルにエントリを追加するだけで済みます。
-
テストケースの追加と修正:
TestCreateSelfSignedCertificate
テスト関数が修正され、SignatureAlgorithm
フィールドを明示的に設定するテストケースが追加されました。これにより、新しい機能が正しく動作すること、およびデフォルトの変更が意図通りであることを確認できます。- テスト構造体
test
にsigAlgo SignatureAlgorithm
フィールドが追加され、各テストケースで異なる署名アルゴリズムを指定できるようになりました。 - 生成された証明書の
SignatureAlgorithm
がテンプレートで指定されたものと一致するかどうかの検証が追加されました。
これらの変更により、Goのcrypto/x509
パッケージは、より現代的でセキュアな暗号学的プラクティスに準拠し、開発者により柔軟な制御を提供できるようになりました。
コアとなるコードの変更箇所
src/pkg/crypto/x509/x509.go
-
signatureAlgorithmDetails
テーブルの追加:var signatureAlgorithmDetails = []struct { algo SignatureAlgorithm oid asn1.ObjectIdentifier pubKeyAlgo PublicKeyAlgorithm hash crypto.Hash }{ {MD2WithRSA, oidSignatureMD2WithRSA, RSA, crypto.Hash(0) /* no value for MD2 */}, {MD5WithRSA, oidSignatureMD5WithRSA, RSA, crypto.MD5}, {SHA1WithRSA, oidSignatureSHA1WithRSA, RSA, crypto.SHA1}, {SHA256WithRSA, oidSignatureSHA256WithRSA, RSA, crypto.SHA256}, // <-- SHA256WithRSAが追加 {SHA384WithRSA, oidSignatureSHA384WithRSA, RSA, crypto.SHA384}, {SHA512WithRSA, oidSignatureSHA512WithRSA, RSA, crypto.SHA512}, {DSAWithSHA1, oidSignatureDSAWithSHA1, DSA, crypto.SHA1}, {DSAWithSHA256, oidSignatureDSAWithSHA256, DSA, crypto.SHA256}, {ECDSAWithSHA1, oidSignatureECDSAWithSHA1, ECDSA, crypto.SHA1}, {ECDSAWithSHA256, oidSignatureECDSAWithSHA256, ECDSA, crypto.SHA256}, {ECDSAWithSHA384, oidSignatureECDSAWithSHA384, ECDSA, crypto.SHA384}, {ECDSAWithSHA512, oidSignatureECDSAWithSHA512, ECDSA, crypto.SHA512}, }
-
getSignatureAlgorithmFromOID
関数の簡素化:switch
文がsignatureAlgorithmDetails
テーブルのループ検索に置き換えられました。func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { for _, details := range signatureAlgorithmDetails { if oid.Equal(details.oid) { return details.algo } } return UnknownSignatureAlgorithm }
-
CreateCertificate
関数におけるデフォルトハッシュの変更とオーバーライドロジックの追加:- RSA秘密鍵の場合のデフォルトハッシュがSHA-1からSHA-256に変更されました。
template.SignatureAlgorithm
が設定されている場合の処理が追加されました。
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (certBytes []byte, err error) { // ... 既存のコード ... var signatureAlgorithm pkix.AlgorithmIdentifier var hashFunc crypto.Hash var privType PublicKeyAlgorithm // <-- 追加: 秘密鍵のタイプを保持 switch priv := priv.(type) { case *rsa.PrivateKey: privType = RSA // <-- 追加 signatureAlgorithm.Algorithm = oidSignatureSHA256WithRSA // <-- SHA256がデフォルトに hashFunc = crypto.SHA256 // <-- SHA256がデフォルトに case *ecdsa.PrivateKey: privType = ECDSA // <-- 追加 // ... 既存のECDSAハッシュ選択ロジック ... default: return nil, errors.New("x509: only RSA and ECDSA private keys supported") } // <-- ここから新しいロジックの追加 --> if template.SignatureAlgorithm != 0 { // テンプレートで署名アルゴリズムが指定されている場合 found := false for _, details := range signatureAlgorithmDetails { if details.algo == template.SignatureAlgorithm { if details.pubKeyAlgo != privType { // 秘密鍵のタイプと指定されたアルゴリズムの互換性チェック return nil, errors.New("x509: requested SignatureAlgorithm does not match private key type") } signatureAlgorithm.Algorithm, hashFunc = details.oid, details.hash if hashFunc == 0 { // ハッシュ関数が利用可能かチェック return nil, errors.New("x509: cannot sign with hash function requested") } found = true break } } if !found { // 未知の署名アルゴリズムの場合 return nil, errors.New("x509: unknown SignatureAlgorithm") } } // <-- 新しいロジックの追加ここまで --> // ... 既存のコード ... }
src/pkg/crypto/x509/x509_test.go
-
テスト構造体
test
の変更:sigAlgo SignatureAlgorithm
フィールドが追加されました。func TestCreateSelfSignedCertificate(t *testing.T) { tests := []struct { name string pub, priv interface{} checkSig bool sigAlgo SignatureAlgorithm // <-- 追加 }{ {"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true, SHA1WithRSA}, // <-- sigAlgoの指定 {"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false, ECDSAWithSHA384}, // <-- sigAlgoの指定 {"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false, SHA256WithRSA}, // <-- sigAlgoの指定 {"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true, ECDSAWithSHA1}, // <-- sigAlgoの指定 } // ... }
-
証明書テンプレートへの
SignatureAlgorithm
の設定:func TestCreateSelfSignedCertificate(t *testing.T) { // ... template := Certificate{ // ... SignatureAlgorithm: test.sigAlgo, // <-- テンプレートにsigAlgoを設定 // ... } // ... }
-
生成された証明書の
SignatureAlgorithm
の検証:func TestCreateSelfSignedCertificate(t *testing.T) { // ... if cert.SignatureAlgorithm != test.sigAlgo { t.Errorf("%s: SignatureAlgorithm wasn't copied from template. Got %s, want %s", test.name, cert.SignatureAlgorithm, test.sigAlgo) } // ... }
コアとなるコードの解説
src/pkg/crypto/x509/x509.go
-
signatureAlgorithmDetails
テーブル: このテーブルは、Goのcrypto/x509
パッケージがサポートするすべての署名アルゴリズムに関するメタデータを一元的に管理するために導入されました。各エントリは、SignatureAlgorithm
の列挙値、対応するASN.1 Object Identifier (OID)、そのアルゴリズムが使用する公開鍵のタイプ(RSA, ECDSAなど)、そして署名時に使用される暗号学的ハッシュ関数(crypto.Hash
)を定義しています。 このテーブルの導入により、以前はgetSignatureAlgorithmFromOID
関数内にハードコードされていたOIDとアルゴリズムのマッピングがデータ駆動型になり、コードの重複が排除され、新しいアルゴリズムの追加や既存アルゴリズムの変更が容易になりました。特に、SHA256WithRSA
のエントリが追加され、これがRSA署名の新しいデフォルトとして機能するための基盤となります。 -
getSignatureAlgorithmFromOID
関数の変更: 以前は長いswitch
文でOIDをSignatureAlgorithm
にマッピングしていましたが、この変更によりsignatureAlgorithmDetails
テーブルを線形探索するシンプルなループに置き換えられました。これにより、コードがより簡潔になり、保守性が向上しました。 -
CreateCertificate
関数における署名アルゴリズムの選択ロジック: この関数はX.509証明書を生成し、署名するGoの主要なAPIです。- デフォルトの変更: 最も重要な変更は、RSA秘密鍵が使用される場合のデフォルトの署名アルゴリズムが
SHA1WithRSA
からSHA256WithRSA
(および対応するcrypto.SHA1
からcrypto.SHA256
)に変更された点です。これにより、明示的に指定しない限り、Goで生成されるRSA署名付き証明書はよりセキュアなSHA-256を使用するようになります。 template.SignatureAlgorithm
によるオーバーライド:Certificate
テンプレートにSignatureAlgorithm
フィールドが追加されたことで、開発者は証明書生成時に特定の署名アルゴリズムを明示的に指定できるようになりました。- このフィールドが0(デフォルト値)でない場合、指定されたアルゴリズムが優先されます。
- 互換性チェック: 指定された
SignatureAlgorithm
が、実際に署名に使用される秘密鍵のタイプ(RSA鍵でECDSA署名アルゴリズムを指定するなど)と互換性があるかどうかが厳密にチェックされます。これにより、無効な組み合わせによるエラーを防ぎます。 - ハッシュ関数利用可能性チェック: 指定された署名アルゴリズムに対応するハッシュ関数が
crypto
パッケージでサポートされているかどうかも確認されます。 - これらのチェックは、証明書生成の堅牢性を高め、開発者が誤った設定を行うことを防ぐための重要なバリデーションです。
- デフォルトの変更: 最も重要な変更は、RSA秘密鍵が使用される場合のデフォルトの署名アルゴリズムが
src/pkg/crypto/x509/x509_test.go
TestCreateSelfSignedCertificate
の変更: このテストは、自己署名証明書の生成と検証を行います。sigAlgo
フィールドの追加: テストケースを定義する匿名構造体にsigAlgo SignatureAlgorithm
フィールドが追加されました。これにより、各テストシナリオで異なる署名アルゴリズムを試すことが可能になりました。- テンプレートへの設定: 生成される証明書のテンプレートに
test.sigAlgo
が設定されるようになりました。これにより、CreateCertificate
関数がSignatureAlgorithm
フィールドを正しく処理するかどうかがテストされます。 - 検証の追加: 生成された証明書の
SignatureAlgorithm
が、テンプレートで指定されたtest.sigAlgo
と一致するかどうかの検証が追加されました。これは、CreateCertificate
関数がテンプレートから署名アルゴリズムを正しくコピーしていることを保証します。
これらのコード変更は、GoのX.509証明書処理におけるセキュリティと柔軟性を大幅に向上させるものです。
関連リンク
- Go Issue #5302: https://code.google.com/p/go/issues/detail?id=5302 (このコミットが修正したGoのイシュートラッカーのエントリ)
- Go Code Review (CL) 40720047: https://golang.org/cl/40720047 (この変更のGoコードレビューリンク)
参考にした情報源リンク
- Microsoft TechNet Blog - SHA1 Deprecation Policy: http://blogs.technet.com/b/pki/archive/2013/11/12/sha1-deprecation-policy.aspx (MicrosoftによるSHA-1非推奨化ポリシーの発表)
- Google Security Blog - Announcing the first SHA1 collision: https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html (SHA-1衝突攻撃の成功に関するGoogleの発表)
- X.509 - Wikipedia: https://ja.wikipedia.org/wiki/X.509
- デジタル署名 - Wikipedia: https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B8%E3%82%BF%E3%83%AB%E7%BD%B2%E5%90%8D
- 暗号学的ハッシュ関数 - Wikipedia: https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E5%AD%A6%E7%9A%84%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E9%96%A2%E6%95%B0
- SHA-2 - Wikipedia: https://ja.wikipedia.org/wiki/SHA-2
- RSA暗号 - Wikipedia: https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7
- 楕円曲線DSA - Wikipedia: https://ja.wikipedia.org/wiki/%E6%A5%95%E5%86%86%E6%9B%B2%E7%B7%9ADSA