[インデックス 16604] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/x509
パッケージに、楕円曲線 (EC) 秘密鍵をASN.1 DER形式でエンコード(マーシャル)する機能を追加するものです。具体的には、MarshalECPrivateKey
関数が導入され、既存のEC秘密鍵のパース(デコード)機能と対をなす形で、鍵のシリアライズ機能を提供します。
コミット
commit 90352972660db161a04c80ea1bc5f832613592a9
Author: Adam Langley <agl@golang.org>
Date: Thu Jun 20 12:14:16 2013 -0400
crypto/x509: add function to marshal EC private keys.
This complements the parsing function that we already have.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/10426043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/90352972660db161a04c80ea1bc5f832613592a9
元コミット内容
crypto/x509
パッケージにEC秘密鍵をマーシャルする関数を追加します。これは、既に存在するパース関数を補完するものです。
変更の背景
Go言語の crypto/x509
パッケージには、既にASN.1 DER形式でエンコードされたEC秘密鍵をパース(デコード)する ParseECPrivateKey
関数が存在していました。しかし、Goの ecdsa.PrivateKey
型のEC秘密鍵オブジェクトを、標準的なASN.1 DER形式にエンコードして出力する(マーシャルする)機能が欠けていました。
この機能の欠如は、Goプログラム内で生成または操作されたEC秘密鍵を、ファイルに保存したり、他のシステムに転送したり、あるいは異なるプログラミング言語で書かれたアプリケーションと相互運用したりする際に問題となります。標準的な形式で出力できなければ、Goの外部で鍵を再利用することが困難になります。
このコミットは、このギャップを埋めるために MarshalECPrivateKey
関数を追加し、EC秘密鍵のパースとマーシャルの両方向の操作を可能にすることで、crypto/x509
パッケージのEC秘密鍵に関する機能の完全性を高めることを目的としています。これにより、GoのEC秘密鍵の取り扱いがより柔軟かつ実用的になります。
前提知識の解説
1. 楕円曲線暗号 (ECC: Elliptic Curve Cryptography)
楕円曲線暗号は、公開鍵暗号の一種で、従来のRSA暗号などと比較して、より短い鍵長で同等のセキュリティ強度を実現できるという特徴があります。これは、楕円曲線上の離散対数問題の困難性に基づいています。EC秘密鍵は、この楕円曲線暗号システムにおいて、署名の生成や鍵の導出などに使用される秘密の情報です。
2. crypto/x509
パッケージ
Go言語の crypto/x509
パッケージは、X.509証明書とPKIX (Public Key Infrastructure X.509) 関連の標準を実装しています。これには、証明書のパース、検証、生成、そして公開鍵および秘密鍵の標準形式(PKCS#1、PKCS#8、SEC1など)のエンコード・デコード機能が含まれます。このコミットで扱われるEC秘密鍵の形式は、SEC1標準に準拠しています。
3. ecdsa
パッケージ
Go言語の crypto/ecdsa
パッケージは、楕円曲線デジタル署名アルゴリズム (ECDSA) を実装しています。このパッケージは、EC秘密鍵と公開鍵の構造体 (ecdsa.PrivateKey
, ecdsa.PublicKey
) を定義し、署名の生成と検証のための関数を提供します。MarshalECPrivateKey
関数は、この ecdsa.PrivateKey
型のオブジェクトを入力として受け取ります。
4. elliptic
パッケージ
Go言語の crypto/elliptic
パッケージは、楕円曲線の基本的な操作(点のスカラー倍算、点の加算など)を実装しています。また、NIST標準曲線(P-256, P-384, P-521など)やSecp256k1などの一般的な楕円曲線の定義も提供します。MarshalECPrivateKey
関数では、公開鍵のバイト列を生成するために elliptic.Marshal
関数が使用されます。
5. ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための標準的な記法です。異なるシステム間でデータを交換する際に、データの形式を明確に定義するために使用されます。暗号化や通信プロトコルにおいて、鍵、証明書、署名などの構造を定義する際によく用いられます。
6. DER (Distinguished Encoding Rules)
DERは、ASN.1で記述されたデータ構造をバイト列にエンコードするための、厳密で一意なルールセットです。ASN.1のエンコーディングルールには複数ありますが、DERは特に暗号化の分野で広く採用されており、同じデータ構造は常に同じバイト列にエンコードされることが保証されます。EC秘密鍵の標準的な表現も、このASN.1 DER形式で定義されています。
7. SEC1
SEC1 (Standards for Efficient Cryptography Group 1) は、楕円曲線暗号に関する標準仕様を定めた文書です。EC秘密鍵のASN.1 DERエンコーディング形式もこのSEC1で定義されており、crypto/x509
パッケージの sec1.go
ファイルはこの標準に関連する処理を扱っています。
技術的詳細
このコミットで追加された MarshalECPrivateKey
関数は、ecdsa.PrivateKey
型のEC秘密鍵オブジェクトを受け取り、それをASN.1 DER形式のバイト列に変換します。
関数の内部では、以下のステップが実行されます。
-
曲線OIDの取得:
- 入力された
ecdsa.PrivateKey
オブジェクトから、その鍵が使用している楕円曲線 (key.Curve
) に対応するオブジェクト識別子 (OID: Object Identifier) を取得します。 - これは
oidFromNamedCurve
ヘルパー関数によって行われます。OIDは、特定の楕円曲線を一意に識別するための国際標準の数値です。 - もし未知の曲線が指定された場合(対応するOIDが見つからない場合)、エラーが返されます。
- 入力された
-
公開鍵のバイト列生成:
- EC秘密鍵には対応する公開鍵が含まれています(
key.X
,key.Y
)。 - これらの公開鍵の座標 (
key.X
,key.Y
) をelliptic.Marshal
関数を使用してバイト列に変換します。このバイト列は、ASN.1のBIT STRING
型としてエンコードされます。
- EC秘密鍵には対応する公開鍵が含まれています(
-
ASN.1構造体の構築:
- EC秘密鍵のASN.1構造は、SEC1標準で定義されている
ECPrivateKey
構造体に従います。 - この構造体は、バージョン番号、秘密鍵の値(
key.D.Bytes()
)、名前付き曲線のOID、そして公開鍵のビット列を含みます。 - Goの
asn1
パッケージで定義されたecPrivateKey
構造体(コミットには含まれていませんが、既存のコードで定義されているはずです)にこれらの値をマッピングします。
- EC秘密鍵のASN.1構造は、SEC1標準で定義されている
-
ASN.1 DERエンコード:
- 構築された
ecPrivateKey
構造体をasn1.Marshal
関数に渡すことで、ASN.1 DER形式のバイト列にエンコードします。 asn1.Marshal
は、Goの構造体のタグ情報(asn1:"..."
)に基づいて、適切なASN.1タイプとエンコーディングルールを適用し、最終的なDERバイト列を生成します。
- 構築された
このプロセスにより、Goの内部表現である ecdsa.PrivateKey
オブジェクトが、他のシステムやアプリケーションと互換性のある標準的なASN.1 DER形式に変換されます。
テストファイル sec1_test.go
では、TestParseECPrivateKey
関数が拡張され、既存の ParseECPrivateKey
でデコードした鍵を、新しく追加された MarshalECPrivateKey
でエンコードし直し、元のDERバイト列と一致するかどうかを検証しています。これにより、マーシャルとパースの往復変換が正しく機能することが保証されます。
コアとなるコードの変更箇所
src/pkg/crypto/x509/sec1.go
--- a/src/pkg/crypto/x509/sec1.go
+++ b/src/pkg/crypto/x509/sec1.go
@@ -33,6 +33,20 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
return parseECPrivateKey(nil, der)
}
+// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format.
+func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
+ oid, ok := oidFromNamedCurve(key.Curve)
+ if !ok {
+ return nil, errors.New("x509: unknown elliptic curve")
+ }
+ return asn1.Marshal(ecPrivateKey{
+ Version: 1,
+ PrivateKey: key.D.Bytes(),
+ NamedCurveOID: oid,
+ PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
+ })
+}
+
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
// The OID for the named curve may be provided from another source (such as
// the PKCS8 container) - if it is provided then use this instead of the OID
src/pkg/crypto/x509/sec1_test.go
--- a/src/pkg/crypto/x509/sec1_test.go
+++ b/src/pkg/crypto/x509/sec1_test.go
@@ -5,6 +5,7 @@
package x509
import (
+ "bytes"
"encoding/hex"
"testing"
)
@@ -15,8 +16,15 @@ var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4d
func TestParseECPrivateKey(t *testing.T) {
derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
- _, err := ParseECPrivateKey(derBytes)
+ key, err := ParseECPrivateKey(derBytes)
if err != nil {
t.Errorf("failed to decode EC private key: %s", err)
}
+ serialized, err := MarshalECPrivateKey(key)
+ if err != nil {
+ t.Fatalf("failed to encode EC private key: %s", err)
+ }
+ if !bytes.Equal(serialized, derBytes) {
+ t.Fatalf("serialized key differs: got %x, want %x", serialized, derBytes)
+ }
}
コアとなるコードの解説
src/pkg/crypto/x509/sec1.go
の変更
MarshalECPrivateKey
関数の追加:- この関数は
*ecdsa.PrivateKey
型の引数key
を受け取ります。 - まず、
oidFromNamedCurve(key.Curve)
を呼び出して、秘密鍵が使用している楕円曲線に対応するASN.1 OIDを取得します。もし対応するOIDが見つからない場合は、"x509: unknown elliptic curve" というエラーを返します。 - 次に、
asn1.Marshal
を使用して、ecPrivateKey
構造体をASN.1 DER形式にエンコードします。 ecPrivateKey
構造体は、以下のフィールドで初期化されます。Version: 1
: SEC1で定義されているEC秘密鍵のバージョン番号。PrivateKey: key.D.Bytes()
: 秘密鍵の整数値D
をバイト列に変換したもの。NamedCurveOID: oid
: 楕円曲線のOID。PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)}
: 公開鍵のX座標とY座標をelliptic.Marshal
でバイト列に変換し、それをasn1.BitString
でラップしたもの。これはASN.1のBIT STRING
型としてエンコードされます。
- この関数は
src/pkg/crypto/x509/sec1_test.go
の変更
TestParseECPrivateKey
関数の拡張:- 既存のテスト関数が拡張され、
ParseECPrivateKey
でデコードしたkey
オブジェクトを、新しく追加されたMarshalECPrivateKey
関数に渡して再エンコードする処理が追加されました。 serialized, err := MarshalECPrivateKey(key)
: デコードした鍵をマーシャルします。エラーが発生した場合はテストを失敗させます。if !bytes.Equal(serialized, derBytes)
: マーシャルされたバイト列 (serialized
) が、元のDERバイト列 (derBytes
) と完全に一致するかどうかをbytes.Equal
で比較します。一致しない場合は、テストを失敗させ、期待値と実際の値を出力します。
- 既存のテスト関数が拡張され、
これらの変更により、Goの crypto/x509
パッケージは、EC秘密鍵のパースとマーシャルの両方をサポートするようになり、より完全な鍵管理機能を提供できるようになりました。
関連リンク
- Go CL 10426043: https://golang.org/cl/10426043