Go 言語(以下 golang)で、共通鍵暗号用の共通の秘密鍵を、お互いの公開鍵から作成したい。
つまり「相手の公開鍵」と「私の秘密鍵」をゴニョゴニョして作成された鍵が、相手も同じように「私の公開鍵」と「相手の秘密鍵」でゴニョゴニョして作成された鍵と同じであれば、2 人の共通の秘密鍵として利用できるということです。
GPG で作成できる鍵で、GitHub に登録できるアルゴリズム(RSA, ElGamal, DSA, ECDH, ECDSA, EdDSA)のうち、電子署名用アルゴリズム ECDSA(楕円曲線電子署名アルゴリズム) の公開鍵から Golang で作成したい。ついでに Golang で鍵のペアの作成もしたいのです。
- ECDSA 公開鍵から DH 鍵交換で共通鍵を作成する ← いまここ
- ECDH 公開鍵から DH 鍵交換で共通鍵を作成する @ Qiita
ECDSA は DSA (Digital Signature Algorithm) と付いているように、電子署名のために考えられたアルゴリズムです。ここでは、ECDSA の鍵でも DH 鍵共有ができる程度に考えてください。なお、DSA は以下の問題があるので、これから検討するなら ECDH + X25519 などを検討する方がいいと思います。
- NIST でなく NSA がデザインしており、バックドア埋め込み疑惑などもあった
- 特許の問題が明確にクリアされていない
- 2025 年に OpenSSH から外されるとアナウンスされている
TL; DR (今北産業)
AliceとBobの事前合意
import "crypto/elliptic"
// Alice と Bob の事前合意。共通パラメーターとして NIST P256 の曲線を使用する、とする。
// 選択肢は: P224, P256, P384, P521
paramCommon := elliptic.P256()
各々の公開鍵暗号ペア鍵作成
import "crypto/ecdsa"
// --------------------------------------------------------------
// Alice の公開鍵暗号のペア鍵作成
// --------------------------------------------------------------
alicePriv, err := ecdsa.GenerateKey(paramCommon, rand.Reader)
PanicOnError(err)
alicePub := alicePriv.PublicKey
// 作成例 (作成するごとに変わる):
// Private key (Alice): f963265a0effb618cc133346c3c083d022fcac1229baa524561002250a7b2e65
// Public key (Alice) : X=f07591a19733607b4ea596e5ff0f10848cb069d7751e7347c216808ace86e4cd, Y=e921b0c68b28757dbd042323645c183d0a38cac392b86720bd957810656a5d57
// --------------------------------------------------------------
// Bob の公開鍵暗号のペア鍵作成
// --------------------------------------------------------------
bobPriv, err := ecdsa.GenerateKey(paramCommon, rand.Reader)
PanicOnError(err)
bobPub := bobPriv.PublicKey
// 作成例 (作成するごとに変わる):
// Private key (Bob): 76fea912bb5a7fa991348bb8cd8a2264c24831b2c98a9400dbf234a0ccc664a1
// Public key (Bob) : X=f7ad06524a8aa5e05e53114a0b09ede7928ea01f7c2b1912d67626c56e87ec03, Y=a010b028c007f89c8802833e198a87cc3e70b7cf771a630af2f4e20faf7a978e)
各々の共通秘密鍵の生成
// Alice が、Bob の公開鍵から Alice の秘密鍵で 2 者の共通秘密鍵を作成
aliceSharedRaw, _ := bobPub.Curve.ScalarMult(bobPub.X, bobPub.Y, alicePriv.D.Bytes())
aliceShared := sha3.Sum256(aliceSharedRaw.Bytes()) // 扱いやすいようにバイトデータをハッシュ化
// Bob が、Alice の公開鍵から Bob の秘密鍵で 2 者の共通秘密鍵を作成
bobSharedRaw, _ := alicePub.Curve.ScalarMult(alicePub.X, alicePub.Y, bobPriv.D.Bytes())
bobShared := sha3.Sum256(bobSharedRaw.Bytes()) // 扱いやすいようにバイトデータをハッシュ化
// 両者が各々算出した共通秘密鍵
// aliceShared: c572eb446818b1883a5a0986e7ed190f745065567459fd5f668b4d13dc6290b9
// bobShared : c572eb446818b1883a5a0986e7ed190f745065567459fd5f668b4d13dc6290b9
全ソースコードを見る
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"golang.org/x/crypto/sha3"
)
func main() {
// --------------------------------------------------------------------
// Alice と Bob の事前合意。共通パラメーターとして NIST P256 の曲線を使用するとする。
// 選択肢は: P224, P256, P384, P521
// --------------------------------------------------------------------
paramCommon := elliptic.P256()
fmt.Printf("-- 共通パラメーター情報(ECC Parameters) --\n")
fmt.Printf(" Name: %s\n", paramCommon.Params().Name) // 使用される曲線の名前
fmt.Printf(" P : %x\n", paramCommon.Params().P) // フィールドの位数
fmt.Printf(" N : %x\n", paramCommon.Params().N) // 基点の位数
fmt.Printf(" B : %x\n", paramCommon.Params().B) // 曲線の方程式の定数
fmt.Printf(" Gx: %x\n", paramCommon.Params().Gx) // 基点の X 座標
fmt.Printf(" Gy: %x\n", paramCommon.Params().Gy) // 基点の Y 座標
fmt.Printf(" Bitsize: %x\n\n", paramCommon.Params().BitSize) // フィールドのサイズ
// --------------------------------------------------------------------
// 公開鍵暗号のペア鍵作成
// --------------------------------------------------------------------
// Alice の公開鍵暗号ペア鍵作成
alicePriv, err := ecdsa.GenerateKey(paramCommon, rand.Reader)
PanicOnError(err)
alicePub := alicePriv.PublicKey
fmt.Printf("-- Alice のペア鍵 --\n")
fmt.Printf("Private key (Alice): %x\n", alicePriv.D)
fmt.Printf("Public key (Alice) : X=%x, Y=%x\n\n", alicePub.X, alicePub.Y)
// Bob の公開鍵暗号ペア鍵作成
bobPriv, err := ecdsa.GenerateKey(paramCommon, rand.Reader)
PanicOnError(err)
bobPub := bobPriv.PublicKey
fmt.Printf("-- Bob のペア鍵 --\n")
fmt.Printf("Private key (Bob): %x\n", bobPriv.D)
fmt.Printf("Public key (Bob) : X=%x, Y=%x)\n\n", bobPub.X, bobPub.Y)
// --------------------------------------------------------------------
// 共通秘密鍵の作成
// --------------------------------------------------------------------
// Alice が、Bob の公開鍵から Alice の秘密鍵で 2 者の共通秘密鍵を作成
aliceSharedRaw, _ := bobPub.Curve.ScalarMult(bobPub.X, bobPub.Y, alicePriv.D.Bytes())
aliceShared := sha3.Sum256(aliceSharedRaw.Bytes()) // 扱いやすいようにバイトデータをハッシュ化
// Bob が、Alice の公開鍵から Bob の秘密鍵で 2 者の共通秘密鍵を作成
bobSharedRaw, _ := alicePub.Curve.ScalarMult(alicePub.X, alicePub.Y, bobPriv.D.Bytes())
bobShared := sha3.Sum256(bobSharedRaw.Bytes()) // 扱いやすいようにバイトデータをハッシュ化
// 共通秘密鍵の確認
fmt.Printf("-- 両者の各々算出した共通秘密鍵 --\n")
fmt.Printf("Shared key (Alice): %x\n", aliceShared)
fmt.Printf("Shared key (Bob) : %x\n", bobShared)
}
func PanicOnError(err error) {
if err != nil {
panic(err)
}
}
- オンラインで動作を見る @ GoPlayground
参考文献
- Standard library/crypto/ecdh @ godoc
- Standard library/crypto/elliptic @ godoc
- 楕円曲線暗号 @ Wikipedia
- 楕円曲線ディフィー・ヘルマン鍵共有 @ Wikipedia
- Diffie-Hellman鍵交換 @ okumuralab.org (三重大学 奥村名誉教授 雑記ブログ)
- Issue #52221 crypto/ecdh: new package | Golang @ GitHub
- Golang ECDH @ asecuritysite.com
- Efficient and Secure Elliptic Curve Cryptography Implementation of Curve P-256 @ NIST (PDF)
- 公開鍵暗号を巡る新しい動き:RSAから楕円曲線暗号へ @ 日本銀行金融研究所