C/C++

C 語言使用 OpenSSL 實作橢圓曲線 ECDH 金鑰交換教學與範例

介紹如何在 C 語言中使用 OpenSSL 實作橢圓曲線 ECDH 金鑰交換。

產生橢圓曲線金鑰對

在實作橢圓曲線 ECDH 金鑰交換之前,要先產生兩對橢圓曲線的金鑰對,以下是以 C 語言程式的方式來產生金鑰的範例:

#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>

// 產生並儲存橢圓曲線金鑰
void generateECKey(const char *privKeyFile, const char *pubKeyFile) {
	// 產生 secp256k1 金鑰對
	EVP_PKEY *ecKey = EVP_EC_gen("secp256k1");

	// 輸出公鑰與私鑰資訊
	//EVP_PKEY_print_public_fp(stdout, ecKey, 0, NULL);
	EVP_PKEY_print_private_fp(stdout, ecKey, 0, NULL);

	// 將私鑰寫入 privKeyFile 中
	BIO *bioPrivKey = BIO_new_file(privKeyFile, "w");
	PEM_write_bio_PrivateKey(bioPrivKey, ecKey, NULL, NULL, 0, NULL, NULL);
	BIO_free(bioPrivKey);

	// 將公鑰寫入 pubKeyFile 中
	BIO *bioPubKey = BIO_new_file(pubKeyFile, "w");
	PEM_write_bio_PUBKEY(bioPubKey, ecKey);
	BIO_free(bioPubKey);

	// 釋放橢圓曲線金鑰空間
	EVP_PKEY_free(ecKey);
}

int main() {
	// 產生兩組橢圓曲線金鑰對
	generateECKey("ec_private_key1.pem", "ec_public_key1.pem");
	generateECKey("ec_private_key2.pem", "ec_public_key2.pem");

	return EXIT_SUCCESS;
}

這裡為了方便初學者閱讀,所以實作上省略了所有的錯誤檢查,實務上每一條函數呼叫都要檢查傳回值是否正確。

將這段程式碼儲存至 gen_ec_keys.c,並用以下指令進行編譯:

# 編譯金鑰產生程式
gcc -o gen_ec_keys gen_ec_keys.c -lcrypto

編譯之後,會產生 gen_ec_keys 這個執行檔,執行後即可產生兩組橢圓曲線的金鑰對:

# 執行金鑰產生程式
./gen_ec_keys
Private-Key: (256 bit)
priv:
    71:0a:27:9e:11:58:cd:51:e6:7a:83:1b:18:0f:d2:
    cc:03:29:7b:80:61:43:23:6f:20:ea:b7:17:44:75:
    26:f5
pub:
    04:d1:7f:73:c6:31:14:bf:5e:01:52:14:15:5f:7e:
    bc:12:49:cf:ce:7c:e3:b0:57:31:43:25:15:27:6a:
    f7:9d:96:9e:06:67:be:78:dd:6e:c3:58:05:73:ba:
    92:c8:14:09:b5:82:7c:08:5c:91:ee:82:10:bd:b0:
    23:2c:59:c9:64
ASN1 OID: secp256k1
Private-Key: (256 bit)
priv:
    f3:d9:9f:d0:b7:c5:df:24:0d:cb:30:c9:b7:b6:7c:
    c9:08:ee:4d:71:eb:d4:65:bc:93:a8:08:c7:04:e2:
    68:f6
pub:
    04:b8:b2:71:6f:58:48:2a:97:61:04:86:8b:2d:98:
    ca:b8:ee:af:eb:65:90:ac:3a:39:69:4d:69:89:f1:
    92:e3:2e:2a:64:5e:9a:99:71:bc:46:50:fd:bc:de:
    c9:29:31:b7:d8:30:a6:bc:37:e9:50:87:a0:4c:fd:
    9a:7e:3d:99:22
ASN1 OID: secp256k1

除了使用 C 語言之外,我們也可以參考 OpenSSL 指令實作橢圓曲線 ECDH 金鑰交換與 AES 加密教學與範例,以 OpenSSL 指令來產生橢圓曲線金鑰對。

橢圓曲線 ECDH 金鑰交換

當雙方都準備好自己的橢圓曲線金鑰對,並將公鑰交給對方之後,就可以透過 ECDH 金鑰交換的方式,計算出共享密鑰,以下是 C 語言實作的範例:

#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>

// 橢圓曲線 ECDH 金鑰交換
void ecdh(const char *privKeyFile, const char *peerPubKeyFile, const char *secretFile) {
	// 讀取己方私鑰
	EVP_PKEY *privKey;
	BIO *bioPrivKey = BIO_new_file(privKeyFile, "r");
	PEM_read_bio_PrivateKey(bioPrivKey, &privKey, NULL, NULL);
	BIO_free(bioPrivKey);

	// 讀取對方公鑰
	EVP_PKEY *peerPubKey;
	BIO *bioPubKey = BIO_new_file(peerPubKeyFile, "r");
	PEM_read_bio_PUBKEY(bioPubKey, &peerPubKey, NULL, NULL);
	BIO_free(bioPrivKey);

	// 建立 context
	EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(NULL, privKey, NULL);
	EVP_PKEY_derive_init(ctx);
	EVP_PKEY_derive_set_peer(ctx, peerPubKey);

	// 取得共享密鑰長度
	size_t secretLen;
	EVP_PKEY_derive(ctx, NULL, &secretLen);

	// 計算共享密鑰
	unsigned char secret[secretLen];
	EVP_PKEY_derive(ctx, secret, &secretLen);

	// 將共享密鑰儲存至檔案
	BIO *bioSecret = BIO_new_file(secretFile, "wb");
	BIO_write(bioSecret, secret, secretLen);
	BIO_free(bioSecret);

	// 釋放 context 空間
	EVP_PKEY_CTX_free(ctx);
}

int main() {
	// 橢圓曲線 ECDH 金鑰交換
	ecdh("ec_private_key1.pem", "ec_public_key2.pem", "ec_shared_secret1.bin");
	ecdh("ec_private_key2.pem", "ec_public_key1.pem", "ec_shared_secret2.bin");

	return EXIT_SUCCESS;
}

將這段程式碼儲存至 ecdh.c,並用以下指令進行編譯:

# 編譯金鑰產生程式
gcc -o ecdh ecdh.c -lcrypto

編譯之後,會產生 ecdh 這個執行檔,執行後就會模擬雙方各拿自己的私鑰加上對方的公鑰,計算共享密鑰:

# 執行金鑰產生程式
./ecdh

產生的密鑰會存放於 ec_shared_secret1.binec_shared_secret2.bin,理論上這兩組密鑰會是一模一樣的。

我們同時可以使用 OpenSSL 指令以同樣的方式計算共享密鑰:

# 產生共享密鑰
openssl pkeyutl -derive -inkey ec_private_key1.pem \
    -peerkey ec_public_key2.pem -out ec_shared_secret1_cmd.bin
openssl pkeyutl -derive -inkey ec_private_key2.pem \
    -peerkey ec_public_key1.pem -out ec_shared_secret2_cmd.bin

最後驗證所有的共享密鑰是否相同:

# 驗證共享密鑰一致性
sha1sum ec_shared_secret*.bin
a62d3612640007905ac21e827631a6bce9ab6bcd  ec_shared_secret1.bin
a62d3612640007905ac21e827631a6bce9ab6bcd  ec_shared_secret1_cmd.bin
a62d3612640007905ac21e827631a6bce9ab6bcd  ec_shared_secret2.bin
a62d3612640007905ac21e827631a6bce9ab6bcd  ec_shared_secret2_cmd.bin

參考資料

Share
Published by
Office Guide

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

2 年 ago