[インデックス 13115] ファイルの概要
このコミットは、Go言語の標準ライブラリである crypto/x509
パッケージに楕円曲線デジタル署名アルゴリズム (ECDSA) のサポートを追加するものです。具体的には、X.509証明書におけるECDSA署名の検証と、ECDSA公開鍵のパース機能が拡張されています。
コミット
commit 5c6162cdd16d7d706342946c0f2a8a944f2796fe
Author: Benjamin Black <b@b3k.us>
Date: Tue May 22 11:03:59 2012 -0400
crypto/x509: Add ECDSA support
R=golang-dev, agl, rsc
CC=golang-dev
https://golang.org/cl/6208087
---
src/pkg/crypto/x509/x509.go | 144 +++++++++++++++++++++++++++++++++++----
src/pkg/crypto/x509/x509_test.go | 111 ++++++++++++++++++++++++++++++
2 files changed, 241 insertions(+), 14 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5c6162cdd16d7d706342946c0f2a8a944f2796fe
元コミット内容
crypto/x509: Add ECDSA support
R=golang-dev, agl, rsc
CC=golang-dev
https://golang.org/cl/6208087
変更の背景
この変更の背景には、デジタル署名技術の進化と、より効率的でセキュアな暗号アルゴリズムへの需要があります。ECDSAは、従来のRSAやDSAといったデジタル署名アルゴリズムと比較して、同等のセキュリティレベルをより短い鍵長で実現できるという利点があります。これにより、証明書のサイズを小さくし、署名生成・検証の計算コストを削減できるため、特にリソースが限られた環境や、大量のトランザクションを処理するシステムにおいて有利です。
Go言語の crypto/x509
パッケージは、X.509証明書のパース、検証、生成など、公開鍵基盤 (PKI) の中心的な機能を提供します。ECDSAのサポートを追加することで、GoアプリケーションがECDSAベースの証明書を扱うことが可能になり、より広範なPKIインフラストラクチャとの相互運用性が向上します。これは、Webサーバー(HTTPS)、コード署名、VPNなど、X.509証明書が広く利用される様々な分野において、Go言語の適用範囲を広げる上で重要なステップとなります。
前提知識の解説
X.509 証明書
X.509は、公開鍵証明書の標準フォーマットを定義するITU-Tの標準規格です。公開鍵と、その公開鍵の所有者に関する情報(識別名、有効期間など)を紐付け、認証局(CA)によってデジタル署名されたものです。これにより、公開鍵の正当性を検証し、通信相手の身元を確認することができます。X.509証明書は、TLS/SSL、VPN、コード署名など、様々なセキュリティプロトコルやアプリケーションで広く利用されています。
デジタル署名アルゴリズム (DSA, RSA, ECDSA)
デジタル署名アルゴリズムは、メッセージの完全性と送信者の認証を保証するために使用されます。
- RSA (Rivest–Shamir–Adleman): 広く使われている公開鍵暗号アルゴリズムで、デジタル署名にも利用されます。大きな素数の積を因数分解することの困難性に基づいています。
- DSA (Digital Signature Algorithm): 米国政府によって標準化されたデジタル署名アルゴリズムです。離散対数問題の困難性に基づいています。
- ECDSA (Elliptic Curve Digital Signature Algorithm): 楕円曲線暗号 (ECC) をベースにしたデジタル署名アルゴリズムです。DSAと同様に離散対数問題に基づきますが、楕円曲線上の点を利用することで、同等のセキュリティレベルをより短い鍵長で実現できます。これにより、計算効率が向上し、署名サイズも小さくなります。
Object Identifier (OID)
OIDは、ASN.1 (Abstract Syntax Notation One) で定義されたオブジェクトを一意に識別するための数値の階層構造です。X.509証明書では、署名アルゴリズム、公開鍵アルゴリズム、拡張フィールドなど、様々な要素を識別するためにOIDが使用されます。例えば、1.2.840.113549.1.1.5
は sha1WithRSAEncryption
を表すOIDです。
楕円曲線 (Elliptic Curve)
楕円曲線暗号 (ECC) は、楕円曲線上の点の加算とスカラー倍算という数学的操作に基づいています。特定の楕円曲線とベースポイントが定義され、その曲線上の点の離散対数問題の困難性が暗号の安全性に利用されます。このコミットで言及されている secp224r1
, secp256r1
, secp384r1
, secp521r1
は、NIST (National Institute of Standards and Technology) によって標準化された、広く利用されている楕円曲線です。
- secp256r1 (P-256): 最も広く使われている楕円曲線の一つで、TLS/SSLなどで頻繁に利用されます。
- secp384r1 (P-384): P-256よりも高いセキュリティレベルを提供します。
- secp521r1 (P-521): さらに高いセキュリティレベルを提供します。
- secp224r1 (P-224): 比較的小さな曲線で、リソースが限られた環境で利用されることがあります。
技術的詳細
このコミットは、Go言語の crypto/x509
パッケージにECDSAのサポートを統合するために、以下の主要な変更を加えています。
-
ECDSA関連の定数とOIDの追加:
SignatureAlgorithm
型にECDSAWithSHA1
,ECDSAWithSHA256
,ECDSAWithSHA384
,ECDSAWithSHA512
を追加。これらは、ECDSA署名に使用されるハッシュアルゴリズムを示します。PublicKeyAlgorithm
型にECDSA
を追加。- 対応するOID (
oidSignatureECDSAWithSHA1
,oidSignatureECDSAWithSHA256
,oidSignatureECDSAWithSHA384
,oidSignatureECDSAWithSHA512
,oidPublicKeyEcdsa
) を定義。これらのOIDは、X.509証明書内でECDSA関連のアルゴリズムを識別するために使用されます。 - RFC 3279 (ECDSA Signature Algorithm) および RFC 5758 (ECDSA Signature Algorithm) に基づくOIDのコメントが追加されています。
-
楕円曲線のOIDとマッピングの追加:
secp224r1
,secp256r1
,secp384r1
,secp521r1
の各楕円曲線に対応するOID (oidNamedCurveP224
,oidNamedCurveP256
,oidNamedCurveP384
,oidNamedCurveP521
) を定義。getNamedCurveFromOID
関数を追加し、これらのOIDをGoのcrypto/elliptic
パッケージで提供される具体的な楕円曲線 (elliptic.P224()
,elliptic.P256()
,elliptic.P384()
,elliptic.P521()
) にマッピングします。これは、X.509証明書内の公開鍵情報から使用されている楕円曲線を特定するために不可欠です。
-
ECDSA公開鍵のパース機能の追加:
parsePublicKey
関数にECDSA
アルゴリズムのケースを追加。- X.509証明書からECDSA公開鍵をパースするロジックが実装されています。これには、公開鍵情報から楕円曲線のOIDを抽出し、対応する楕円曲線を取得し、その後、公開鍵のX座標とY座標をアンマーシャルして
ecdsa.PublicKey
構造体を構築する処理が含まれます。
-
ECDSA署名検証機能の追加:
Certificate
型のCheckSignature
メソッドにECDSA署名の検証ロジックを追加。- 署名アルゴリズムがECDSAの場合、署名データがASN.1でエンコードされたECDSA署名 (
ecdsaSignature
構造体) としてデコードされます。 - デコードされたRとSの値が正であることを確認した後、
ecdsa.Verify
関数を使用して署名の検証が行われます。
-
テストケースの追加:
x509_test.go
にECDSA署名された自己署名証明書のPEMエンコードされたデータが複数追加されています。これらは、SHA1, SHA256, SHA384ハッシュアルゴリズムと、secp256r1, secp384r1, secp521r1楕円曲線を使用した証明書です。TestECDSA
関数が追加され、これらのテスト証明書をパースし、署名アルゴリズム、公開鍵アルゴリズム、および署名検証が正しく行われることを確認しています。
これらの変更により、Goの crypto/x509
パッケージは、ECDSAベースのX.509証明書を完全にサポートできるようになり、Goアプリケーションがより現代的な暗号技術を利用できるようになります。
コアとなるコードの変更箇所
src/pkg/crypto/x509/x509.go
import
文にcrypto/ecdsa
とcrypto/elliptic
を追加。ecdsaSignature
型をdsaSignature
と同様に定義。SignatureAlgorithm
およびPublicKeyAlgorithm
列挙型にECDSA関連の定数を追加。- ECDSA関連のOID定数 (
oidSignatureECDSAWithSHA1
など) を追加。 getSignatureAlgorithmFromOID
関数にECDSA関連のOIDから署名アルゴリズムを返すケースを追加。oidPublicKeyEcdsa
定数を追加。getPublicKeyAlgorithmFromOID
関数にoidPublicKeyEcdsa
からECDSA
を返すケースを追加。- 楕円曲線 (
secp224r1
,secp256r1
,secp384r1
,secp521r1
) のOID定数を追加。 getNamedCurveFromOID
関数を追加し、OIDから対応するelliptic.Curve
オブジェクトを返すように実装。Certificate.CheckSignature
メソッドのswitch algo
文にECDSAWithSHA1
,ECDSAWithSHA256
,ECDSAWithSHA384
,ECDSAWithSHA512
のケースを追加し、対応するハッシュタイプを設定。Certificate.CheckSignature
メソッドのswitch pub := c.PublicKey.(type)
文に*ecdsa.PublicKey
のケースを追加し、ECDSA署名検証ロジックを実装。parsePublicKey
関数にcase ECDSA:
を追加し、ECDSA公開鍵のパースロジックを実装。
src/pkg/crypto/x509/x509_test.go
import
文にcrypto/ecdsa
を追加。_ "crypto/sha256"
と_ "crypto/sha512"
を追加(ハッシュアルゴリズムの登録のため)。- ECDSA署名された自己署名証明書のPEMエンコードされた文字列変数 (
ecdsaSHA1CertPem
,ecdsaSHA256p256CertPem
,ecdsaSHA256p384CertPem
,ecdsaSHA384p521CertPem
) を追加。 ecdsaTests
スライスを定義し、各テスト証明書と期待される署名アルゴリズムをマッピング。TestECDSA
関数を追加し、ecdsaTests
をループして各証明書のパースと検証を行うテストロジックを実装。
コアとなるコードの解説
src/pkg/crypto/x509/x509.go
type ecdsaSignature dsaSignature
これは、ECDSA署名がDSA署名と同様に、2つの大きな整数RとSで構成されることを示しています。Goの crypto/ecdsa
パッケージの Sign
関数も (R, S *big.Int, err error)
を返すため、この構造は自然です。
OIDの追加とマッピング
// OIDs for signature algorithms
var (
// ... 既存のOID ...
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
// ...
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm {
switch {
// ... 既存のケース ...
case oid.Equal(oidSignatureECDSAWithSHA1):
return ECDSAWithSHA1
case oid.Equal(oidSignatureECDSAWithSHA256):
return ECDSAWithSHA256
case oid.Equal(oidSignatureECDSAWithSHA384):
return ECDSAWithSHA384
case oid.Equal(oidSignatureECDSAWithSHA512):
return ECDSAWithSHA512
}
return UnknownSignatureAlgorithm
}
これらの変更は、X.509証明書内で使用されるECDSA署名アルゴリズムを識別するための標準的なOIDを定義し、それらをGoの内部表現である SignatureAlgorithm
型にマッピングします。これにより、証明書をパースする際に、どのECDSAアルゴリズムが使用されているかを正確に認識できるようになります。
楕円曲線のOIDと getNamedCurveFromOID
var (
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveP224):
return elliptic.P224()
case oid.Equal(oidNamedCurveP256):
return elliptic.P256()
case oid.Equal(oidNamedCurveP384):
return elliptic.P384()
case oid.Equal(oidNamedCurveP521):
return elliptic.P521()
}
return nil
}
ECDSA公開鍵は、使用される楕円曲線の種類に依存します。このセクションでは、標準的な楕円曲線(NIST P-224, P-256, P-384, P-521)に対応するOIDを定義し、それらをGoの crypto/elliptic
パッケージが提供する具体的な曲線オブジェクトに変換する関数を提供します。これにより、証明書から公開鍵を抽出する際に、正しい楕円曲線コンテキストを確立できます。
parsePublicKey
におけるECDSAサポート
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
switch algo {
// ... 既存のケース ...
case ECDSA:
paramsData := keyData.Algorithm.Parameters.FullBytes
namedCurveOID := new(asn1.ObjectIdentifier)
_, err := asn1.Unmarshal(paramsData, namedCurveOID)
if err != nil {
return nil, err
}
namedCurve := getNamedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
return nil, errors.New("crypto/x509: unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
if x == nil {
return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
X: x,
Y: y,
}
return pub, nil
// ...
}
}
このコードは、X.509証明書からECDSA公開鍵をパースする中心的なロジックです。
keyData.Algorithm.Parameters.FullBytes
から楕円曲線のOIDを抽出します。getNamedCurveFromOID
を使用して、対応するelliptic.Curve
オブジェクトを取得します。asn1Data
(公開鍵のビット列) をelliptic.Unmarshal
を使用して、楕円曲線上の点 (X, Y座標) に変換します。- 最終的に、パースされた曲線と座標を使用して
ecdsa.PublicKey
構造体を構築し、返します。
Certificate.CheckSignature
におけるECDSA検証
func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) error {
// ... ハッシュタイプの決定 ...
switch pub := c.PublicKey.(type) {
// ... 既存のケース ...
case *ecdsa.PublicKey:
ecdsaSig := new(ecdsaSignature)
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("crypto/x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("crypto/x509: ECDSA verification failure")
}
return nil
}
return ErrUnsupportedAlgorithm
}
このセクションは、証明書の署名を検証する際にECDSA署名を処理します。
- 署名データ (
signature
バイト列) をecdsaSignature
構造体にASN.1デコードします。 - デコードされたRとSの値が正であることを確認します(ECDSAの仕様による)。
ecdsa.Verify
関数を呼び出して、公開鍵 (pub
)、ハッシュ値 (digest
)、および署名RとSを使用して署名を検証します。検証が成功すればnil
を返し、失敗すればエラーを返します。
src/pkg/crypto/x509/x509_test.go
テスト証明書と TestECDSA
関数
// Self-signed certificate using ECDSA with SHA1 & secp256r1
var ecdsaSHA1CertPem = `...` // PEMエンコードされた証明書データ
// ... 他のECDSAテスト証明書 ...
var ecdsaTests = []struct {
sigAlgo SignatureAlgorithm
pemCert string
}{
{ECDSAWithSHA1, ecdsaSHA1CertPem},
{ECDSAWithSHA256, ecdsaSHA256p256CertPem},
{ECDSAWithSHA256, ecdsaSHA256p384CertPem},
{ECDSAWithSHA384, ecdsaSHA384p521CertPem},
}
func TestECDSA(t *testing.T) {
for i, test := range ecdsaTests {
pemBlock, _ := pem.Decode([]byte(test.pemCert))
cert, err := ParseCertificate(pemBlock.Bytes)
// ... 検証ロジック ...
}
}
このテストコードは、実際にECDSA署名された証明書をGoの crypto/x509
パッケージでパースし、その署名アルゴリズム、公開鍵アルゴリズム、そして最も重要な署名検証が正しく機能するかを確認します。異なるハッシュアルゴリズムと楕円曲線の組み合わせをテストすることで、実装の堅牢性を保証しています。
関連リンク
- Go言語
crypto/x509
パッケージのドキュメント: https://pkg.go.dev/crypto/x509 - Go言語
crypto/ecdsa
パッケージのドキュメント: https://pkg.go.dev/crypto/ecdsa - Go言語
crypto/elliptic
パッケージのドキュメント: https://pkg.go.dev/crypto/elliptic
参考にした情報源リンク
- RFC 3279: PKIX Certificate and CRL Profile. Defines ECDSA signature algorithm OIDs. https://datatracker.ietf.org/doc/html/rfc3279
- RFC 5758: Internet X.509 Public Key Infrastructure: Additional Algorithms and Identifiers for DSA and ECDSA. https://datatracker.ietf.org/doc/html/rfc5758
- RFC 5480: Elliptic Curve Cryptography Subject Public Key Information. Defines OIDs for named curves. https://datatracker.ietf.org/doc/html/rfc5480
- X.509 - Wikipedia: https://ja.wikipedia.org/wiki/X.509
- 楕円曲線デジタル署名アルゴリズム - Wikipedia: https://ja.wikipedia.org/wiki/%E6%A5%95%E5%86%86%E6%9B%B2%E7%B7%9A%E3%83%87%E3%82%B8%E3%82%BF%E3%83%AB%E7%BD%B2%E5%90%8D%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0
- Object Identifier - Wikipedia: https://ja.wikipedia.org/wiki/Object_Identifier