LoginSignup
0
0

【Golang】ECDSA 公開鍵から DH 鍵交換で共通鍵を作成する(ディフィー・ヘルマン鍵共有)

Last updated at Posted at 2023-03-19

Go 言語(以下 golang)で、共通鍵暗号用の共通の秘密鍵を、お互いの公開鍵から作成したい

つまり「相手の公開鍵」と「私の秘密鍵」をゴニョゴニョして作成された鍵が、相手も同じように「私の公開鍵」と「相手の秘密鍵」でゴニョゴニョして作成された鍵と同じであれば、2 人の共通の秘密鍵として利用できるということです。

GPG で作成できる鍵で、GitHub に登録できるアルゴリズム(RSA, ElGamal, DSA, ECDH, ECDSA, EdDSA)のうち、電子署名用アルゴリズム ECDSA(楕円曲線電子署名アルゴリズム) の公開鍵から Golang で作成したい。ついでに Golang で鍵のペアの作成もしたいのです。

ECDSA は DSA (Digital Signature Algorithm) と付いているように、電子署名のために考えられたアルゴリズムです。ここでは、ECDSA の鍵でも DH 鍵共有ができる程度に考えてください。なお、DSA は以下の問題があるので、これから検討するなら ECDH + X25519 などを検討する方がいいと思います。

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)
	}
}

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0