Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13562] ファイルの概要

このコミットは、Go言語のcrypto/x509パッケージにおいて、証明書生成時にECDSA (Elliptic Curve Digital Signature Algorithm) 鍵のサポートを追加するものです。既存のコードベースではECDSA証明書の読み込みはサポートされていましたが、この変更により、ECDSA鍵を用いた証明書の書き込み(生成)が可能になります。

コミット

commit bbb5f1bffbcb03b442ea953dfbe41169530e450f
Author: Adam Langley <agl@golang.org>
Date:   Fri Aug 3 10:37:30 2012 -0400

    crypto/x509: support ECDSA keys when generating certificates.
    
    We already support reading ECDSA certificates and this change adds
    write support.
    
    R=golang-dev, bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/6422046
---
 src/pkg/crypto/x509/pkcs8.go     |   2 +-\
 src/pkg/crypto/x509/x509.go      | 123 +++++++++++++++++++++++++++++----------
 src/pkg/crypto/x509/x509_test.go | 117 +++++++++++++++++++++----------------
 3 files changed, 161 insertions(+), 81 deletions(-)

diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go
index 4d8e0518e0..8c3b65f807 100644
--- a/src/pkg/crypto/x509/pkcs8.go
+++ b/src/pkg/crypto/x509/pkcs8.go
@@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
 		return nil, err
 	}\n \tswitch {\n-\tcase privKey.Algo.Algorithm.Equal(oidRSA):\n+\tcase privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):\n \t\tkey, err = ParsePKCS1PrivateKey(privKey.PrivateKey)\n \t\tif err != nil {\n \t\t\treturn nil, errors.New(\"crypto/x509: failed to parse RSA private key embedded in PKCS#8: \" + err.Error())\ndiff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index d7ed8bdbd5..b9e4dc0f6a 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -262,18 +262,18 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm\n // id-ecPublicKey OBJECT IDENTIFIER ::= {\n //       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }\n var (\n-\toidPublicKeyRsa   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}\n-\toidPublicKeyDsa   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}\n-\toidPublicKeyEcdsa = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}\n+\toidPublicKeyRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}\n+\toidPublicKeyDSA   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}\n+\toidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}\n )\n \n func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {\n \tswitch {\n-\tcase oid.Equal(oidPublicKeyRsa):\n+\tcase oid.Equal(oidPublicKeyRSA):\n \t\treturn RSA\n-\tcase oid.Equal(oidPublicKeyDsa):\n+\tcase oid.Equal(oidPublicKeyDSA):\n \t\treturn DSA\n-\tcase oid.Equal(oidPublicKeyEcdsa):\n+\tcase oid.Equal(oidPublicKeyECDSA):\n \t\treturn ECDSA\n \t}\n \treturn UnknownPublicKeyAlgorithm\n@@ -302,7 +302,7 @@ var (\n \toidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}\n )\n \n-func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\n+func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\n \tswitch {\n \tcase oid.Equal(oidNamedCurveP224):\n \t\treturn elliptic.P224()\n@@ -316,6 +316,21 @@ func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\n \treturn nil\n }\n \n+func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {\n+\tswitch curve {\n+\tcase elliptic.P224():\n+\t\treturn oidNamedCurveP224, true\n+\tcase elliptic.P256():\n+\t\treturn oidNamedCurveP256, true\n+\tcase elliptic.P384():\n+\t\treturn oidNamedCurveP384, true\n+\tcase elliptic.P521():\n+\t\treturn oidNamedCurveP521, true\n+\t}\n+\n+\treturn nil, false\n+}\n+\n // KeyUsage represents the set of actions that are valid for a given key. It\'s\n // a bitmap of the KeyUsage* constants.\n type KeyUsage int\n@@ -648,7 +663,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{\n \t\tif err != nil {\n \t\t\treturn nil, err\n \t\t}\n-\t\tnamedCurve := getNamedCurveFromOID(*namedCurveOID)\n+\t\tnamedCurve := namedCurveFromOID(*namedCurveOID)\n \t\tif namedCurve == nil {\n \t\t\treturn nil, errors.New(\"crypto/x509: unsupported elliptic curve\")\n \t\t}\n@@ -1069,11 +1084,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {\n \treturn ret[0:n], nil\n }\n \n-var (\n-\toidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5}\n-\toidRSA         = []int{1, 2, 840, 113549, 1, 1, 1}\n-)\n-\n func subjectBytes(cert *Certificate) ([]byte, error) {\n \tif len(cert.RawSubject) > 0 {\n \t\treturn cert.RawSubject, nil\n@@ -1093,23 +1103,61 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) {\n-\trsaPub, ok := pub.(*rsa.PublicKey)\n-\tif !ok {\n-\t\treturn nil, errors.New(\"x509: non-RSA public keys not supported\")\n+\tvar publicKeyBytes []byte\n+\tvar publicKeyAlgorithm pkix.AlgorithmIdentifier\n+\n+\tswitch pub := pub.(type) {\n+\tcase *rsa.PublicKey:\n+\t\tpublicKeyBytes, err = asn1.Marshal(rsaPublicKey{\n+\t\t\tN: pub.N,\n+\t\t\tE: pub.E,\n+\t\t})\n+\t\tpublicKeyAlgorithm.Algorithm = oidPublicKeyRSA\n+\tcase *ecdsa.PublicKey:\n+\t\toid, ok := oidFromNamedCurve(pub.Curve)\n+\t\tif !ok {\n+\t\t\treturn nil, errors.New(\"x509: unknown elliptic curve\")\n+\t\t}\n+\t\tpublicKeyAlgorithm.Algorithm = oidPublicKeyECDSA\n+\t\tvar paramBytes []byte\n+\t\tparamBytes, err = asn1.Marshal(oid)\n+\t\tif err != nil {\n+\t\t\treturn\n+\t\t}\n+\t\tpublicKeyAlgorithm.Parameters.FullBytes = paramBytes\n+\t\tpublicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)\n+\tdefault:\n+\t\treturn nil, errors.New(\"x509: only RSA and ECDSA public keys supported\")\n \t}\n \n-\trsaPriv, ok := priv.(*rsa.PrivateKey)\n-\tif !ok {\n-\t\treturn nil, errors.New(\"x509: non-RSA private keys not supported\")\n+\tvar signatureAlgorithm pkix.AlgorithmIdentifier\n+\tvar hashFunc crypto.Hash\n+\n+\tswitch priv := priv.(type) {\n+\tcase *rsa.PrivateKey:\n+\t\tsignatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA\n+\t\thashFunc = crypto.SHA1\n+\tcase *ecdsa.PrivateKey:\n+\t\tswitch priv.Curve {\n+\t\tcase elliptic.P224(), elliptic.P256():\n+\t\t\thashFunc = crypto.SHA256\n+\t\t\tsignatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256\n+\t\tcase elliptic.P384():\n+\t\t\thashFunc = crypto.SHA384\n+\t\t\tsignatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384\n+\t\tcase elliptic.P521():\n+\t\t\thashFunc = crypto.SHA512\n+\t\t\tsignatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512\n+\t\tdefault:\n+\t\t\treturn nil, errors.New(\"x509: unknown elliptic curve\")\n+\t\t}\n+\tdefault:\n+\t\treturn nil, errors.New(\"x509: only RSA and ECDSA private keys supported\")\n \t}\n \n-\tasn1PublicKey, err := asn1.Marshal(rsaPublicKey{\n-\t\tN: rsaPub.N,\n-\t\tE: rsaPub.E,\n-\t})\n \tif err != nil {\n \t\treturn\n \t}\n@@ -1133,15 +1181,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf\n \t\treturn\n \t}\n \n-\tencodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}\n+\tencodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}\n \tc := tbsCertificate{\n \t\tVersion:            2,\n \t\tSerialNumber:       template.SerialNumber,\n-\t\tSignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},\n+\t\tSignatureAlgorithm: signatureAlgorithm,\n \t\tIssuer:             asn1.RawValue{FullBytes: asn1Issuer},\n \t\tValidity:           validity{template.NotBefore, template.NotAfter},\n \t\tSubject:            asn1.RawValue{FullBytes: asn1Subject},\n-\t\tPublicKey:          publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},\n+\t\tPublicKey:          publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},\n \t\tExtensions:         extensions,\n \t}\n \n@@ -1152,11 +1200,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf\n \n \tc.Raw = tbsCertContents\n \n-\th := sha1.New()\n+\th := hashFunc.New()\n \th.Write(tbsCertContents)\n \tdigest := h.Sum(nil)\n \n-\tsignature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest)\n+\tvar signature []byte\n+\n+\tswitch priv := priv.(type) {\n+\tcase *rsa.PrivateKey:\n+\t\tsignature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)\n+\tcase *ecdsa.PrivateKey:\n+\t\tvar r, s *big.Int\n+\t\tif r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {\n+\t\t\tsignature, err = asn1.Marshal(ecdsaSignature{r, s})\n+\t\t}\n+\tdefault:\n+\t\tpanic(\"internal error\")\n+\t}\n+\n \tif err != nil {\n \t\treturn\n \t}\n@@ -1164,7 +1225,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf\n \tcert, err = asn1.Marshal(certificate{\n \t\tnil,\n \t\tc,\n-\t\tpkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},\n+\t\tsignatureAlgorithm,\n \t\tasn1.BitString{Bytes: signature, BitLength: len(signature) * 8},\n \t})\n \treturn\ndiff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index 813a96409b..9e2e387316 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -8,6 +8,7 @@ import (\n 	\"bytes\"\n 	\"crypto/dsa\"\n 	\"crypto/ecdsa\"\n+\t\"crypto/elliptic\"\n 	\"crypto/rand\"\n 	\"crypto/rsa\"\n 	_ \"crypto/sha256\"\n@@ -240,65 +241,83 @@ func TestCreateSelfSignedCertificate(t *testing.T) {\n \trandom := rand.Reader\n \n \tblock, _ := pem.Decode([]byte(pemPrivateKey))\n-\tpriv, err := ParsePKCS1PrivateKey(block.Bytes)\n+\trsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)\n \tif err != nil {\n-\t\tt.Errorf(\"Failed to parse private key: %s\", err)\n-\t\treturn\n-\t}\n-\n-\tcommonName := \"test.example.com\"\n-\ttemplate := Certificate{\n-\t\tSerialNumber: big.NewInt(1),\n-\t\tSubject: pkix.Name{\n-\t\t\tCommonName:   commonName,\n-\t\t\tOrganization: []string{\"Σ Acme Co\"},\n-\t\t},\n-\t\tNotBefore: time.Unix(1000, 0),\n-\t\tNotAfter:  time.Unix(100000, 0),\n-\n-\t\tSubjectKeyId: []byte{1, 2, 3, 4},\n-\t\tKeyUsage:     KeyUsageCertSign,\n-\n-\t\tBasicConstraintsValid: true,\n-\t\tIsCA:                  true,\n-\t\tDNSNames:              []string{\"test.example.com\"},\n-\n-\t\tPolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},\n-\t\tPermittedDNSDomains: []string{\".example.com\", \"example.com\"},\n+\t\tt.Fatalf(\"Failed to parse private key: %s\", err)\n \t}\n \n-\tderBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)\n+\tecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n \tif err != nil {\n-\t\tt.Errorf(\"Failed to create certificate: %s\", err)\n-\t\treturn\n-\t}\n+\t\tt.Fatalf(\"Failed to generate ECDSA key: %s\", err)\n+\t}\n+\n+\ttests := []struct {\n+\t\tname      string\n+\t\tpub, priv interface{}\n+\t\tcheckSig  bool\n+\t}{\n+\t\t{\"RSA/RSA\", &rsaPriv.PublicKey, rsaPriv, true},\n+\t\t{\"RSA/ECDSA\", &rsaPriv.PublicKey, ecdsaPriv, false},\n+\t\t{\"ECDSA/RSA\", &ecdsaPriv.PublicKey, rsaPriv, false},\n+\t\t{\"ECDSA/ECDSA\", &ecdsaPriv.PublicKey, ecdsaPriv, true},\n+\t}\n+\n+\tfor _, test := range tests {\n+\t\tcommonName := \"test.example.com\"\n+\t\ttemplate := Certificate{\n+\t\t\tSerialNumber: big.NewInt(1),\n+\t\t\tSubject: pkix.Name{\n+\t\t\t\tCommonName:   commonName,\n+\t\t\t\tOrganization: []string{\"Σ Acme Co\"},\n+\t\t\t},\n+\t\t\tNotBefore: time.Unix(1000, 0),\n+\t\t\tNotAfter:  time.Unix(100000, 0),\n+\n+\t\t\tSubjectKeyId: []byte{1, 2, 3, 4},\n+\t\t\tKeyUsage:     KeyUsageCertSign,\n+\n+\t\t\tBasicConstraintsValid: true,\n+\t\t\tIsCA:                  true,\n+\t\t\tDNSNames:              []string{\"test.example.com\"},\n+\n+\t\t\tPolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},\n+\t\t\tPermittedDNSDomains: []string{\".example.com\", \"example.com\"},\n+\t\t}\n \n-\tcert, err := ParseCertificate(derBytes)\n-\tif err != nil {\n-\t\tt.Errorf(\"Failed to parse certificate: %s\", err)\n-\t\treturn\n-\t}\n+\t\tderBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)\n+\t\tif err != nil {\n+\t\t\tt.Errorf(\"%s: failed to create certificate: %s\", test.name, err)\n+\t\t\tcontinue\n+\t\t}\n \n-\tif len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {\n-\t\tt.Errorf(\"Failed to parse policy identifiers: got:%#v want:%#v\", cert.PolicyIdentifiers, template.PolicyIdentifiers)\n-\t}\n+\t\tcert, err := ParseCertificate(derBytes)\n+\t\tif err != nil {\n+\t\t\tt.Errorf(\"%s: failed to parse certificate: %s\", test.name, err)\n+\t\t\tcontinue\n+\t\t}\n \n-\tif len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != \".example.com\" || cert.PermittedDNSDomains[1] != \"example.com\" {\n-\t\tt.Errorf(\"Failed to parse name constraints: %#v\", cert.PermittedDNSDomains)\n-\t}\n+\t\tif len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {\n+\t\t\tt.Errorf(\"%s: failed to parse policy identifiers: got:%#v want:%#v\", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)\n+\t\t}\n \n-\tif cert.Subject.CommonName != commonName {\n-\t\tt.Errorf(\"Subject wasn\'t correctly copied from the template. Got %s, want %s\", cert.Subject.CommonName, commonName)\n-\t}\n+\t\tif len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != \".example.com\" || cert.PermittedDNSDomains[1] != \"example.com\" {\n+\t\t\tt.Errorf(\"%s: failed to parse name constraints: %#v\", test.name, cert.PermittedDNSDomains)\n+\t\t}\n \n-\tif cert.Issuer.CommonName != commonName {\n-\t\tt.Errorf(\"Issuer wasn\'t correctly copied from the template. Got %s, want %s\", cert.Issuer.CommonName, commonName)\n-\t}\n+\t\tif cert.Subject.CommonName != commonName {\n+\t\t\tt.Errorf(\"%s: subject wasn\'t correctly copied from the template. Got %s, want %s\", test.name, cert.Subject.CommonName, commonName)\n+\t\t}\n \n-\terr = cert.CheckSignatureFrom(cert)\n-\tif err != nil {\n-\t\tt.Errorf(\"Signature verification failed: %s\", err)\n-\t\treturn\n+\t\tif cert.Issuer.CommonName != commonName {\n+\t\t\tt.Errorf(\"%s: issuer wasn\'t correctly copied from the template. Got %s, want %s\", test.name, cert.Issuer.CommonName, commonName)\n+\t\t}\n+\n+\t\tif test.checkSig {\n+\t\t\terr = cert.CheckSignatureFrom(cert)\n+\t\t\tif err != nil {\n+\t\t\t\tt.Errorf(\"%s: signature verification failed: %s\", test.name, err)\n+\t\t\t}\n+\t\t}\n \t}\n }\n \n```

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/bbb5f1bffbcb03b442ea953dfbe41169530e450f](https://github.com/golang/go/commit/bbb5f1bffbcb03b442ea953dfbe41169530e450f)

## 元コミット内容

crypto/x509: support ECDSA keys when generating certificates.

We already support reading ECDSA certificates and this change adds write support.

R=golang-dev, bradfitz, rsc CC=golang-dev https://golang.org/cl/6422046


## 変更の背景

この変更の背景には、Go言語の`crypto/x509`パッケージが、X.509証明書の生成においてECDSA鍵をサポートしていなかったという制約がありました。既存の実装では、ECDSA証明書の「読み込み」は可能でしたが、ECDSA鍵を使用して新しい証明書を「生成」する機能が欠けていました。

デジタル証明書の世界では、RSA (Rivest–Shamir–Adleman) とECDSA (Elliptic Curve Digital Signature Algorithm) は主要な公開鍵暗号アルゴリズムです。RSAは長年にわたり広く利用されてきましたが、同等のセキュリティレベルを維持するためにはより長い鍵長が必要となり、それに伴い計算コストも増加します。一方、ECDSAは楕円曲線暗号に基づいており、RSAよりも短い鍵長で同等以上のセキュリティ強度を提供できるため、計算効率や帯域幅の面で優位性があります。特に、モバイルデバイスやIoTデバイスなど、リソースが限られた環境での利用に適しています。

このような背景から、Go言語の標準ライブラリである`crypto/x509`がECDSA鍵による証明書生成をサポートすることは、現代の暗号技術のトレンドに追従し、より効率的でセキュアな証明書運用を可能にする上で不可欠でした。このコミットは、その機能的なギャップを埋めることを目的としています。

## 前提知識の解説

### X.509 証明書

X.509は、公開鍵証明書の標準フォーマットを定義するITU-T(国際電気通信連合電気通信標準化部門)の勧告です。インターネットにおける公開鍵基盤(PKI)の基盤となっており、SSL/TLS、電子メールの署名、コード署名など、様々なセキュリティプロトコルで利用されています。X.509証明書は、公開鍵とその所有者(エンティティ)の識別情報を紐付け、信頼できる第三者である認証局(CA)によって署名されます。

主要な構成要素は以下の通りです。
*   **バージョン**: 証明書のバージョン(例: v3が一般的)。
*   **シリアル番号**: CAによって発行される証明書の一意な識別子。
*   **署名アルゴリズム**: 証明書の署名に使用されたアルゴリズム(例: SHA256withRSA)。
*   **発行者**: 証明書を発行したCAの識別名。
*   **有効期間**: 証明書が有効である期間(開始日時と終了日時)。
*   **サブジェクト**: 証明書が発行されたエンティティの識別名。
*   **公開鍵情報**: サブジェクトの公開鍵と、その公開鍵のアルゴリズム(例: RSA公開鍵、ECDSA公開鍵)。
*   **拡張領域**: 証明書の追加情報(例: 鍵用途、拡張鍵用途、サブジェクト代替名、基本制約など)。

### RSA (Rivest–Shamir–Adleman)

RSAは、公開鍵暗号方式の一つで、素因数分解の困難性に基づいています。公開鍵と秘密鍵のペアを使用し、公開鍵で暗号化されたデータは対応する秘密鍵でのみ復号でき、秘密鍵で署名されたデータは公開鍵で検証できます。広く普及しており、デジタル署名や鍵交換、データ暗号化に利用されます。

### ECDSA (Elliptic Curve Digital Signature Algorithm)

ECDSAは、楕円曲線暗号(ECC)に基づくデジタル署名アルゴリズムです。RSAと同様に公開鍵と秘密鍵のペアを使用しますが、そのセキュリティは楕円曲線上の離散対数問題の困難性に基づいています。RSAと比較して、同等のセキュリティ強度をより短い鍵長で実現できるため、計算リソースや帯域幅の節約に貢献します。これは、特にモバイルデバイスやIoTデバイスなど、リソースが限られた環境で大きな利点となります。

### PKCS#8 (Public-Key Cryptography Standards #8)

PKCS#8は、公開鍵暗号の秘密鍵情報を格納するための標準フォーマットです。秘密鍵のアルゴリズム識別子と、そのアルゴリズムに特有の秘密鍵データを含みます。暗号化された形式と暗号化されていない形式の両方をサポートしており、秘密鍵の安全な保存と交換に利用されます。

### ASN.1 (Abstract Syntax Notation One)

ASN.1は、データ構造を記述するための標準的な記法です。通信プロトコルやデータストレージにおいて、異なるシステム間でデータを交換する際に、データの表現方法を統一するために使用されます。X.509証明書もASN.1で定義された構造に従ってエンコードされます。

### OID (Object Identifier)

OIDは、ASN.1で定義されたオブジェクトを一意に識別するための数値列です。例えば、特定の暗号アルゴリズム、証明書の拡張、組織などを識別するために使用されます。X.509証明書では、公開鍵アルゴリズムや署名アルゴリズムなどをOIDで指定します。

### 楕円曲線 (Elliptic Curve)

楕円曲線は、特定の数学的な方程式で定義される曲線です。楕円曲線暗号では、この曲線上の点と、その点に対する加算やスカラー倍算といった演算を利用して、暗号システムを構築します。これにより、従来の暗号方式よりも高いセキュリティ効率を実現します。

### デジタル署名 (Digital Signature)

デジタル署名は、電子文書の作成者の身元を証明し、文書が改ざんされていないことを保証するための技術です。送信者は秘密鍵で文書のハッシュ値を署名し、受信者は送信者の公開鍵を使ってその署名を検証します。これにより、データの完全性、認証、否認防止が実現されます。

## 技術的詳細

このコミットの主要な変更点は、`crypto/x509`パッケージ内の`CreateCertificate`関数が、RSA鍵だけでなくECDSA鍵もサポートするように拡張されたことです。

具体的には、以下の点が変更されています。

1.  **公開鍵と秘密鍵の型チェックと処理の分岐**:
    *   `CreateCertificate`関数は、引数として渡される公開鍵(`pub`)と秘密鍵(`priv`)の型をチェックし、RSAまたはECDSAのいずれかであるかに応じて処理を分岐します。
    *   公開鍵が`*rsa.PublicKey`または`*ecdsa.PublicKey`の場合、それぞれのアルゴリズムに応じた公開鍵バイト列を生成します。ECDSAの場合、公開鍵バイト列は楕円曲線の種類(Named Curve)とX, Y座標から構成されます。
    *   秘密鍵が`*rsa.PrivateKey`または`*ecdsa.PrivateKey`の場合、それぞれのアルゴリズムに応じた署名アルゴリズムとハッシュ関数を選択します。ECDSAの場合、使用する楕円曲線の種類(P224, P256, P384, P521)によって、対応するSHAハッシュ関数(SHA256, SHA384, SHA512)とECDSA署名アルゴリズム(oidSignatureECDSAWithSHA256など)が決定されます。

2.  **OIDの定義と利用の変更**:
    *   公開鍵アルゴリズムのOID定義が、より一般的な命名規則(`oidPublicKeyRSA`, `oidPublicKeyDSA`, `oidPublicKeyECDSA`)に変更されました。
    *   `pkcs8.go`では、PKCS#8形式の秘密鍵をパースする際に、RSA秘密鍵のOIDとして`oidRSA`ではなく`oidPublicKeyRSA`を使用するように修正されました。これは、公開鍵アルゴリズムのOIDと秘密鍵のOIDが一致するようにするための整合性のある変更です。
    *   `x509.go`では、`oidSHA1WithRSA`と`oidRSA`というグローバル変数が削除され、それぞれの用途に応じて適切なOIDが直接使用されるようになりました。

3.  **楕円曲線とOIDのマッピング**:
    *   `namedCurveFromOID`関数は、OIDから対応する楕円曲線オブジェクトを取得するために使用されます。
    *   新たに`oidFromNamedCurve`関数が追加され、楕円曲線オブジェクトから対応するOIDを取得できるようになりました。これは、ECDSA公開鍵をASN.1でエンコードする際に、使用する楕円曲線のOIDをパラメータとして含めるために必要です。

4.  **署名生成ロジックの拡張**:
    *   `CreateCertificate`関数内で、証明書の署名を生成する部分が、RSAとECDSAの両方に対応するように拡張されました。
    *   RSA秘密鍵の場合、`rsa.SignPKCS1v15`関数を使用して署名を生成します。
    *   ECDSA秘密鍵の場合、`ecdsa.Sign`関数を使用して署名を生成し、その結果(rとs)をASN.1でエンコードして署名バイト列とします。

5.  **テストケースの追加と修正**:
    *   `x509_test.go`に、RSA鍵とECDSA鍵の様々な組み合わせ(RSA/RSA, RSA/ECDSA, ECDSA/RSA, ECDSA/ECDSA)で自己署名証明書を生成し、検証するテストケースが追加されました。これにより、新しいECDSAサポートが正しく機能することを確認しています。
    *   特に、`ECDSA/RSA`や`RSA/ECDSA`のように、公開鍵と秘密鍵のアルゴリズムが異なる組み合わせのテストケースも含まれていますが、これらのケースでは署名検証が失敗することが期待されており、`checkSig`フラグで制御されています。これは、証明書の署名アルゴリズムが公開鍵のアルゴリズムと一致しない場合に、署名検証が失敗することを確認するためです。

これらの変更により、Go言語の`crypto/x509`パッケージは、より現代的な暗号アルゴリズムであるECDSAを完全にサポートし、証明書生成の柔軟性とセキュリティオプションを向上させています。

## コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は、主に以下の3つのファイルに集中しています。

1.  **`src/pkg/crypto/x509/pkcs8.go`**:
    *   `ParsePKCS8PrivateKey`関数内で、RSA秘密鍵のOID比較が`oidRSA`から`oidPublicKeyRSA`に変更されました。これは、公開鍵アルゴリズムのOIDと秘密鍵のOIDの整合性を保つための修正です。

    ```diff
    --- a/src/pkg/crypto/x509/pkcs8.go
    +++ b/src/pkg/crypto/x509/pkcs8.go
    @@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
     		return nil, err
     	}
     	switch {
    -	case privKey.Algo.Algorithm.Equal(oidRSA):
    +	case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
     		key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
     		if err != nil {
     			return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
    ```

2.  **`src/pkg/crypto/x509/x509.go`**:
    *   公開鍵アルゴリズムのOID定数名が、より明確な`oidPublicKeyRsa`から`oidPublicKeyRSA`など、大文字を含む形式に変更されました。
    *   `getNamedCurveFromOID`関数が`namedCurveFromOID`にリネームされ、新たに`oidFromNamedCurve`関数が追加されました。これは、楕円曲線オブジェクトから対応するOIDを取得するために使用されます。
    *   `CreateCertificate`関数が大幅に修正され、RSAとECDSAの両方の公開鍵と秘密鍵を処理できるように拡張されました。
        *   公開鍵の型に応じた`publicKeyBytes`と`publicKeyAlgorithm`の生成ロジックが追加されました。
        *   秘密鍵の型に応じた`signatureAlgorithm`と`hashFunc`の選択ロジックが追加されました。特にECDSAの場合、楕円曲線に応じて異なるハッシュ関数と署名アルゴリズムが選択されます。
        *   証明書の署名生成部分が、RSAとECDSAの両方に対応するように`switch`文で分岐されました。
    *   不要になったグローバル変数`oidSHA1WithRSA`と`oidRSA`が削除されました。

    ```diff
    --- a/src/pkg/crypto/x509/x509.go
    +++ b/src/pkg/crypto/x509/x509.go
    @@ -262,18 +262,18 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
     // id-ecPublicKey OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
     var (
    -	oidPublicKeyRsa   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
    -	oidPublicKeyDsa   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
    -	oidPublicKeyEcdsa = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
    +	oidPublicKeyRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
    +	oidPublicKeyDSA   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
    +	oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
     )
     
     func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
     	switch {
    -	case oid.Equal(oidPublicKeyRsa):
    +	case oid.Equal(oidPublicKeyRSA):
     		return RSA
    -	case oid.Equal(oidPublicKeyDsa):
    +	case oid.Equal(oidPublicKeyDSA):
     		return DSA
    -	case oid.Equal(oidPublicKeyEcdsa):
    +	case oid.Equal(oidPublicKeyECDSA):
     		return ECDSA
     	}
     	return UnknownPublicKeyAlgorithm
    @@ -302,7 +302,7 @@ var (\
     	oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
     )
     
    -func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\
    +func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\
     	switch {
     	case oid.Equal(oidNamedCurveP224):
     		return elliptic.P224()
    @@ -316,6 +316,21 @@ func getNamedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {\
     	return nil
     }
     
    +func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
    +	switch curve {
    +	case elliptic.P224():
    +		return oidNamedCurveP224, true
    +	case elliptic.P256():
    +		return oidNamedCurveP256, true
    +	case elliptic.P384():
    +		return oidNamedCurveP384, true
    +	case elliptic.P521():
    +		return oidNamedCurveP521, true
    +	}
    +
    +	return nil, false
    +}
    +
     // KeyUsage represents the set of actions that are valid for a given key. It's
     // a bitmap of the KeyUsage* constants.\
     type KeyUsage int
    @@ -648,7 +663,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{\
     		if err != nil {
     			return nil, err
     		}
    -		namedCurve := getNamedCurveFromOID(*namedCurveOID)
    +		namedCurve := namedCurveFromOID(*namedCurveOID)
     		if namedCurve == nil {
     			return nil, errors.New("crypto/x509: unsupported elliptic curve")
     		}
    @@ -1069,11 +1084,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {\
     	return ret[0:n], nil
     }
     
    -var (
    -	oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5}
    -	oidRSA         = []int{1, 2, 840, 113549, 1, 1, 1}
    -)
    -
     func subjectBytes(cert *Certificate) ([]byte, error) {
     	if len(cert.RawSubject) > 0 {
     		return cert.RawSubject, nil
    @@ -1093,23 +1103,61 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) {\
    -	rsaPub, ok := pub.(*rsa.PublicKey)
    -	if !ok {
    -		return nil, errors.New("x509: non-RSA public keys not supported")
    +	var publicKeyBytes []byte
    +	var publicKeyAlgorithm pkix.AlgorithmIdentifier
    +
    +	switch pub := pub.(type) {
    +	case *rsa.PublicKey:
    +		publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
    +			N: pub.N,
    +			E: pub.E,
    +		})
    +		publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
    +	case *ecdsa.PublicKey:
    +		oid, ok := oidFromNamedCurve(pub.Curve)
    +		if !ok {
    +			return nil, errors.New("x509: unknown elliptic curve")
    +		}
    +		publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
    +		var paramBytes []byte
    +		paramBytes, err = asn1.Marshal(oid)
    +		if err != nil {
    +			return
    +		}
    +		publicKeyAlgorithm.Parameters.FullBytes = paramBytes
    +		publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
    +	default:
    +		return nil, errors.New("x509: only RSA and ECDSA public keys supported")
     	}
     
    -	rsaPriv, ok := priv.(*rsa.PrivateKey)
    -	if !ok {
    -		return nil, errors.New("x509: non-RSA private keys not supported")
    +	var signatureAlgorithm pkix.AlgorithmIdentifier
    +	var hashFunc crypto.Hash
    +
    +	switch priv := priv.(type) {
    +	case *rsa.PrivateKey:
    +		signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA
    +		hashFunc = crypto.SHA1
    +	case *ecdsa.PrivateKey:
    +		switch priv.Curve {
    +		case elliptic.P224(), elliptic.P256():
    +			hashFunc = crypto.SHA256
    +			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256
    +		case elliptic.P384():
    +			hashFunc = crypto.SHA384
    +			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384
    +		case elliptic.P521():
    +			hashFunc = crypto.SHA512
    +			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512
    +		default:
    +			return nil, errors.New("x509: unknown elliptic curve")
    +		}
    +	default:
    +		return nil, errors.New("x509: only RSA and ECDSA private keys supported")
     	}
     
    -	asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
    -		N: rsaPub.N,
    -		E: rsaPub.E,
    -	})
     	if err != nil {
     		return
     	}
    @@ -1133,15 +1181,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
     		return
     	}
     
    -	encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
    +	encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
     	c := tbsCertificate{
     		Version:            2,
     		SerialNumber:       template.SerialNumber,
    -		SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
    +		SignatureAlgorithm: signatureAlgorithm,
     		Issuer:             asn1.RawValue{FullBytes: asn1Issuer},
     		Validity:           validity{template.NotBefore, template.NotAfter},
     		Subject:            asn1.RawValue{FullBytes: asn1Subject},
    -		PublicKey:          publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
    +		PublicKey:          publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
     		Extensions:         extensions,
     	}
     
    @@ -1152,11 +1200,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
     
     	c.Raw = tbsCertContents
     
    -	h := sha1.New()
    +	h := hashFunc.New()
     	h.Write(tbsCertContents)
     	digest := h.Sum(nil)
     
    -	signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest)
    +	var signature []byte
    +
    +	switch priv := priv.(type) {
    +	case *rsa.PrivateKey:
    +		signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
    +	case *ecdsa.PrivateKey:
    +		var r, s *big.Int
    +		if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
    +			signature, err = asn1.Marshal(ecdsaSignature{r, s})
    +		}
    +	default:
    +		panic("internal error")
    +	}
    +
     	if err != nil {
     		return
     	}
    @@ -1164,7 +1225,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
     	cert, err = asn1.Marshal(certificate{
     		nil,
     		c,
    -		pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
    +		signatureAlgorithm,
     		asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
     	})
     	return
    ```

3.  **`src/pkg/crypto/x509/x509_test.go`**:
    *   `TestCreateSelfSignedCertificate`テスト関数が大幅に修正され、RSAとECDSAの公開鍵/秘密鍵の様々な組み合わせで証明書を生成し、検証するテストが追加されました。
    *   `crypto/elliptic`パッケージがインポートされました。

    ```diff
    --- a/src/pkg/crypto/x509/x509_test.go
    +++ b/src/pkg/crypto/x509/x509_test.go
    @@ -8,6 +8,7 @@ import (\
     	"bytes"
     	"crypto/dsa"
     	"crypto/ecdsa"
    +	"crypto/elliptic"
     	"crypto/rand"
     	"crypto/rsa"
     	_ "crypto/sha256"
    @@ -240,65 +241,83 @@ func TestCreateSelfSignedCertificate(t *testing.T) {\
     	random := rand.Reader
     
     	block, _ := pem.Decode([]byte(pemPrivateKey))\
    -	priv, err := ParsePKCS1PrivateKey(block.Bytes)
    +	rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
     	if err != nil {\
    -		t.Errorf("Failed to parse private key: %s", err)
    -		return
    -	}
    -
    -	commonName := "test.example.com"
    -	template := Certificate{
    -		SerialNumber: big.NewInt(1),
    -		Subject: pkix.Name{
    -			CommonName:   commonName,
    -			Organization: []string{"Σ Acme Co"},
    -		},
    -		NotBefore: time.Unix(1000, 0),
    -		NotAfter:  time.Unix(100000, 0),
    -
    -		SubjectKeyId: []byte{1, 2, 3, 4},
    -		KeyUsage:     KeyUsageCertSign,
    -
    -		BasicConstraintsValid: true,
    -		IsCA:                  true,
    -		DNSNames:              []string{"test.example.com"},
    -
    -		PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
    -		PermittedDNSDomains: []string{".example.com", "example.com"},
    +		t.Fatalf("Failed to parse private key: %s", err)
     	}
     
    -	derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
    +	ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
     	if err != nil {\
    -		t.Errorf("Failed to create certificate: %s", err)
    -		return
    -	}
    +		t.Fatalf("Failed to generate ECDSA key: %s", err)
    +	}
    +
    +	tests := []struct {
    +		name      string
    +		pub, priv interface{}
    +		checkSig  bool
    +	}{
    +		{"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true},
    +		{"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false},
    +		{"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false},
    +		{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true},
    +	}
    +
    +	for _, test := range tests {
    +		commonName := "test.example.com"
    +		template := Certificate{
    +			SerialNumber: big.NewInt(1),
    +			Subject: pkix.Name{
    +				CommonName:   commonName,
    +				Organization: []string{"Σ Acme Co"},
    +			},
    +			NotBefore: time.Unix(1000, 0),
    +			NotAfter:  time.Unix(100000, 0),
    +
    +			SubjectKeyId: []byte{1, 2, 3, 4},
    +			KeyUsage:     KeyUsageCertSign,
    +
    +			BasicConstraintsValid: true,
    +			IsCA:                  true,
    +			DNSNames:              []string{"test.example.com"},
    +
    +			PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
    +			PermittedDNSDomains: []string{".example.com", "example.com"},
    +		}
     
    -	cert, err := ParseCertificate(derBytes)
    -	if err != nil {\
    -		t.Errorf("Failed to parse certificate: %s", err)
    -		return
    -	}
    +		derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
    +		if err != nil {
    +			t.Errorf("%s: failed to create certificate: %s", test.name, err)
    +			continue
    +		}
     
    -	if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {\
    -		t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers)
    -	}
    +		cert, err := ParseCertificate(derBytes)
    +		if err != nil {
    +			t.Errorf("%s: failed to parse certificate: %s", test.name, err)
    +			continue
    +		}
     
    -	if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {\
    -		t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains)
    -	}
    +		if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
    +			t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
    +		}
     
    -	if cert.Subject.CommonName != commonName {\
    -		t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
    -	}
    +		if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
    +			t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
    +		}
     
    -	if cert.Issuer.CommonName != commonName {\
    -		t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
    -	}
    +		if cert.Subject.CommonName != commonName {
    +			t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
    +		}
     
    -	err = cert.CheckSignatureFrom(cert)
    -	if err != nil {\
    -		t.Errorf("Signature verification failed: %s", err)
    -		return
    +		if cert.Issuer.CommonName != commonName {
    +			t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName)
    +		}
    +
    +		if test.checkSig {
    +			err = cert.CheckSignatureFrom(cert)
    +			if err != nil {
    +				t.Errorf("%s: signature verification failed: %s", test.name, err)
    +			}
    +		}
     	}
     }
     
    ```

## コアとなるコードの解説

### `src/pkg/crypto/x509/x509.go` の `CreateCertificate` 関数

このコミットの最も重要な変更は、`CreateCertificate`関数のロジック拡張です。この関数は、X.509証明書を生成する主要なエントリポイントであり、公開鍵と秘密鍵のペアを受け取って証明書に署名します。

変更前は、この関数はRSA鍵のみをサポートしていました。変更後は、`pub`(公開鍵)と`priv`(秘密鍵)のインターフェース型を`switch`文でチェックし、`*rsa.PublicKey`、`*ecdsa.PublicKey`、`*rsa.PrivateKey`、`*ecdsa.PrivateKey`のいずれかであるかに応じて、適切な処理を行うようになりました。

#### 公開鍵の処理

```go
	var publicKeyBytes []byte
	var publicKeyAlgorithm pkix.AlgorithmIdentifier

	switch pub := pub.(type) {
	case *rsa.PublicKey:
		publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
			N: pub.N,
			E: pub.E,
		})
		publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
	case *ecdsa.PublicKey:
		oid, ok := oidFromNamedCurve(pub.Curve)
		if !ok {
			return nil, errors.New("x509: unknown elliptic curve")
		}
		publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
		var paramBytes []byte
		paramBytes, err = asn1.Marshal(oid)
		if err != nil {
			return
		}
		publicKeyAlgorithm.Parameters.FullBytes = paramBytes
		publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
	default:
		return nil, errors.New("x509: only RSA and ECDSA public keys supported")
	}

この部分では、入力された公開鍵pubの型をチェックします。

  • *rsa.PublicKeyの場合、RSA公開鍵のモジュラスNと公開指数EをASN.1でマーシャルしてpublicKeyBytesとし、アルゴリズム識別子をoidPublicKeyRSAに設定します。
  • *ecdsa.PublicKeyの場合、まずoidFromNamedCurve関数を使って、公開鍵が属する楕円曲線(pub.Curve)に対応するOIDを取得します。このOIDは、ECDSA公開鍵のパラメータとしてASN.1でエンコードされます。publicKeyBytesは、elliptic.Marshal関数を使って楕円曲線上の公開鍵のX, Y座標をバイト列に変換したものです。アルゴリズム識別子はoidPublicKeyECDSAに設定されます。
  • RSAとECDSA以外の公開鍵が渡された場合はエラーを返します。

秘密鍵と署名アルゴリズムの処理

	var signatureAlgorithm pkix.AlgorithmIdentifier
	var hashFunc crypto.Hash

	switch priv := priv.(type) {
	case *rsa.PrivateKey:
		signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA
		hashFunc = crypto.SHA1
	case *ecdsa.PrivateKey:
		switch priv.Curve {
		case elliptic.P224(), elliptic.P256():
			hashFunc = crypto.SHA256
			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256
		case elliptic.P384():
			hashFunc = crypto.SHA384
			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384
		case elliptic.P521():
			hashFunc = crypto.SHA512
			signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512
		default:
			return nil, errors.New("x509: unknown elliptic curve")
		}
	default:
		return nil, errors.New("x509: only RSA and ECDSA private keys supported")
	}

次に、入力された秘密鍵privの型をチェックし、署名に使用するアルゴリズムとハッシュ関数を決定します。

  • *rsa.PrivateKeyの場合、署名アルゴリズムはoidSignatureSHA1WithRSA(SHA-1 with RSA)に、ハッシュ関数はcrypto.SHA1に設定されます。
  • *ecdsa.PrivateKeyの場合、使用する楕円曲線(priv.Curve)に応じて、適切なハッシュ関数とECDSA署名アルゴリズムが選択されます。例えば、P224やP256曲線ではSHA256とoidSignatureECDSAWithSHA256が、P384ではSHA384とoidSignatureECDSAWithSHA384が、P521ではSHA512とoidSignatureECDSAWithSHA512が使用されます。
  • RSAとECDSA以外の秘密鍵が渡された場合はエラーを返します。

署名生成

	// ... (TBSCertificateの構築) ...

	h := hashFunc.New()
	h.Write(tbsCertContents)
	digest := h.Sum(nil)

	var signature []byte

	switch priv := priv.(type) {
	case *rsa.PrivateKey:
		signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
	case *ecdsa.PrivateKey:
		var r, s *big.Int
		if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
			signature, err = asn1.Marshal(ecdsaSignature{r, s})
		}
	default:
		panic("internal error") // Should not happen due to earlier checks
	}

証明書の署名対象データ(TBSCertificate)のハッシュ値を計算した後、秘密鍵の型に応じて実際の署名処理を行います。

  • *rsa.PrivateKeyの場合、rsa.SignPKCS1v15関数を使用してPKCS#1 v1.5パディングスキームで署名を生成します。
  • *ecdsa.PrivateKeyの場合、ecdsa.Sign関数を使用して署名(rとsのペア)を生成し、その結果をASN.1でマーシャルしてsignatureバイト列とします。

これらの変更により、CreateCertificate関数はRSAとECDSAの両方の鍵タイプを透過的に処理できるようになり、Go言語のX.509証明書生成機能が大幅に強化されました。

src/pkg/crypto/x509/x509.gooidFromNamedCurve 関数

func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
	switch curve {
	case elliptic.P224():
		return oidNamedCurveP224, true
	case elliptic.P256():
		return oidNamedCurveP256, true
	case elliptic.P384():
		return oidNamedCurveP384, true
	case elliptic.P521():
		return oidNamedCurveP521, true
	}

	return nil, false
}

この新しく追加された関数は、crypto/ellipticパッケージで定義されている楕円曲線オブジェクト(例: elliptic.P256())を受け取り、それに対応するASN.1のObject Identifier (OID) を返します。ECDSA公開鍵をASN.1でエンコードする際、どの楕円曲線が使用されているかを示すためにこのOIDが必要となります。これにより、楕円曲線オブジェクトとOID間の双方向マッピングが提供され、ECDSA鍵の処理がより堅牢になります。

関連リンク

参考にした情報源リンク