WEBKT

用Python和C++手把手教你实现ECDH密钥交换

17 0 0 0

1. ECDH是个啥?

1.1 椭圆曲线密码学(ECC)简介

1.2 ECDH的流程

2. Python实现ECDH

2.1 安装cryptography库

2.2 代码实现

2.3 代码解释

2.4 运行结果

3. C++实现ECDH

3.1 安装OpenSSL库

3.2 代码实现

3.3 代码解释

3.4 编译和运行

4. 安全性考虑

5. 进阶:带身份验证的ECDH

5.1 数字签名简介

5.2 结合数字签名的ECDH流程

5.3 Python实现(简化版)

5.4 代码解释

5.5 总结

6. 总结与展望

你好,老伙计!今天咱们聊聊ECDH(Elliptic Curve Diffie-Hellman)密钥交换,这玩意儿在安全通信里头可算是个硬通货。我会用Python和C++两种语言,带你从头到尾实现它,让你对这玩意儿有个透彻的理解。别怕,我会用最通俗易懂的方式,让你轻松上手。

1. ECDH是个啥?

简单来说,ECDH是一种基于椭圆曲线密码学的密钥交换协议。它的作用是,两个通信方(比如你和我)在不安全信道上(比如互联网)协商出一个共享的秘密密钥,而这个密钥只有我们俩知道。这个密钥可以用来加密后续的通信内容,保证通信的安全性。

1.1 椭圆曲线密码学(ECC)简介

ECC是ECDH的基础。它基于椭圆曲线上的离散对数问题(ECDLP),这个问题的难度比传统的RSA的整数分解问题要高得多。这意味着,在相同安全级别下,ECC所需的密钥长度更短,计算量更小,效率更高。

椭圆曲线长啥样? 别被“椭圆”二字给唬住了,它长得不一定像椭圆。它其实是一个满足特定数学方程的点集,这个方程通常是这样的:

y^2 = x^3 + ax + b

其中,a和b是常数,不同的a和b会定义不同的椭圆曲线。还有一个无穷远点O,它和椭圆曲线上的点一起构成了群。椭圆曲线上的点加法定义了ECDH的核心。

1.2 ECDH的流程

ECDH的流程其实很简单,主要就这么几步:

  1. 双方约定椭圆曲线:比如常用的secp256k1、secp256r1等。这些曲线的参数(a, b, 阶n, 基点G)都是公开的。
  2. 密钥生成
    • Alice(甲方)
      • 选择一个私钥a(一个随机的整数,通常很大)。
      • 计算公钥A = a * G(椭圆曲线点乘,G是基点)。
    • Bob(乙方)
      • 选择一个私钥b(一个随机的整数)。
      • 计算公钥B = b * G
  3. 公钥交换:Alice和Bob互相交换公钥AB
  4. 密钥计算
    • Alice:计算共享密钥K = a * B。(点乘)
    • Bob:计算共享密钥K = b * A。(点乘)

由于椭圆曲线点乘的特性,a * B = a * (b * G) = b * (a * G) = b * A,所以Alice和Bob计算出的K是相同的。而对于窃听者来说,知道AB,想计算出K,就得解决椭圆曲线的离散对数问题,这在计算上是不可行的。

2. Python实现ECDH

Python以其简洁的语法和丰富的库,非常适合用来快速实现ECDH。这里我们使用cryptography库,它提供了ECC相关的支持。

2.1 安装cryptography

pip install cryptography

2.2 代码实现

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
# 1. 双方约定椭圆曲线
curve = ec.SECP256R1()
# 2. 密钥生成
def generate_key_pair():
private_key = ec.generate_private_key(curve, default_backend())
public_key = private_key.public_key()
return private_key, public_key
# 3. 公钥交换
def serialize_public_key(public_key):
return public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
def deserialize_public_key(public_key_bytes):
return serialization.load_pem_public_key(
public_key_bytes, backend=default_backend()
)
# 4. 密钥计算
def calculate_shared_secret(private_key, peer_public_key):
shared_key = private_key.exchange(ec.ECDH(), peer_public_key)
return shared_key
# Alice的实现
alice_private_key, alice_public_key = generate_key_pair()
alice_public_key_bytes = serialize_public_key(alice_public_key)
# Bob的实现
bob_private_key, bob_public_key = generate_key_pair()
bob_public_key_bytes = serialize_public_key(bob_public_key)
# 公钥交换(模拟)
received_alice_public_key = deserialize_public_key(alice_public_key_bytes)
received_bob_public_key = deserialize_public_key(bob_public_key_bytes)
# 密钥计算
alice_shared_secret = calculate_shared_secret(alice_private_key, received_bob_public_key)
bob_shared_secret = calculate_shared_secret(bob_private_key, received_alice_public_key)
# 验证
print("Alice shared secret (bytes):", alice_shared_secret.hex())
print("Bob shared secret (bytes):", bob_shared_secret.hex())
# 提取共享密钥用于后续加密(可选)
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
def derive_key(shared_secret, info, length=32):
hkdf = HKDF(algorithm=hashes.SHA256(), length=length, salt=None, info=info, backend=default_backend())
return hkdf.derive(shared_secret)
# 假设要用于AES加密,生成一个密钥
aes_key_alice = derive_key(alice_shared_secret, b'aes_key')
aes_key_bob = derive_key(bob_shared_secret, b'aes_key')
print("Alice AES key (hex):", aes_key_alice.hex())
print("Bob AES key (hex):", aes_key_bob.hex())

2.3 代码解释

  1. 导入必要的库cryptography库提供了ECC相关的各种功能。
  2. 约定椭圆曲线:我们选择了SECP256R1曲线,这是一种常用的曲线。
  3. generate_key_pair()函数:生成私钥和公钥对。私钥是随机生成的,公钥由私钥计算得出。
  4. serialize_public_key()deserialize_public_key()函数:用于公钥的序列化和反序列化,以便在网络上传输。公钥通常以PEM格式编码。
  5. calculate_shared_secret()函数:计算共享密钥。这个函数接受私钥和对方的公钥作为输入,然后使用ECDH算法计算出共享密钥。
  6. Alice和Bob的实现:模拟了Alice和Bob的密钥生成、公钥交换和密钥计算过程。请注意,实际应用中,公钥交换是通过网络进行的。
  7. 验证:打印Alice和Bob的共享密钥,你应该看到它们是相同的。
  8. derive_key()函数:从共享密钥派生出一个用于加密的密钥。由于ECDH生成的共享密钥可能不适合直接用于加密,通常需要使用密钥派生函数(KDF)来生成一个更适合的密钥。这里我们使用HKDF算法。

2.4 运行结果

运行上述代码,你会看到Alice和Bob的共享密钥是相同的。而且,派生出的AES密钥也是相同的。这证明了ECDH的成功实现。

3. C++实现ECDH

C++以其高性能和对底层的控制,在密码学领域有着广泛的应用。这里我们使用OpenSSL库来实现ECDH。

3.1 安装OpenSSL

  • Linux (Debian/Ubuntu)

    sudo apt-get update
    sudo apt-get install libssl-dev
  • Linux (CentOS/RHEL)

    sudo yum install openssl-devel
    
  • macOS (使用Homebrew)

    brew install openssl
    
  • Windows:可以从OpenSSL官方网站下载预编译的二进制文件,或者使用包管理器(如vcpkg)安装。

3.2 代码实现

#include <iostream>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <string>
#include <vector>
// 错误处理函数
void handle_error(const char* msg) {
std::cerr << msg << std::endl;
ERR_print_errors_fp(stderr);
exit(1);
}
// 1. 双方约定椭圆曲线
const EC_GROUP* get_ec_group() {
EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_secp256r1);
if (!group) {
handle_error("EC_GROUP_new_by_curve_name failed");
}
return group;
}
// 2. 密钥生成
void generate_key_pair(const EC_GROUP* group, EC_KEY** key, BIGNUM** private_key) {
*key = EC_KEY_new();
if (!*key) {
handle_error("EC_KEY_new failed");
}
if (!EC_KEY_set_group(*key, group)) {
handle_error("EC_KEY_set_group failed");
}
// 生成私钥
*private_key = BN_new();
if (!*private_key) {
handle_error("BN_new failed");
}
if (!EC_KEY_generate_key(*key)) {
handle_error("EC_KEY_generate_key failed");
}
}
// 3. 公钥交换
std::vector<unsigned char> get_public_key_bytes(const EC_KEY* key) {
unsigned char* buf = nullptr;
int len = i2o_ECPublicKey(key, &buf);
if (len <= 0) {
handle_error("i2o_ECPublicKey failed");
}
std::vector<unsigned char> public_key_bytes(buf, buf + len);
OPENSSL_free(buf);
return public_key_bytes;
}
EC_KEY* set_public_key_bytes(const EC_GROUP* group, const std::vector<unsigned char>& public_key_bytes) {
EC_KEY* key = EC_KEY_new();
if (!key) {
handle_error("EC_KEY_new failed");
}
if (!EC_KEY_set_group(key, group)) {
handle_error("EC_KEY_set_group failed");
}
const unsigned char* buf = public_key_bytes.data();
if (!o2i_ECPublicKey(&key, &buf, public_key_bytes.size())) {
handle_error("o2i_ECPublicKey failed");
}
return key;
}
// 4. 密钥计算
std::vector<unsigned char> compute_shared_secret(const EC_KEY* private_key, const EC_KEY* peer_public_key) {
EVP_PKEY* pkey = EVP_PKEY_new();
if (!pkey) {
handle_error("EVP_PKEY_new failed");
}
if (!EVP_PKEY_set1_EC_KEY(pkey, peer_public_key)) {
handle_error("EVP_PKEY_set1_EC_KEY failed");
}
unsigned char* shared_secret = nullptr;
size_t shared_secret_len = 0;
if (!ECDH_compute_key( &shared_secret, &shared_secret_len, pkey, private_key, nullptr)) {
handle_error("ECDH_compute_key failed");
}
std::vector<unsigned char> shared_secret_vec(shared_secret, shared_secret + shared_secret_len);
OPENSSL_free(shared_secret);
EVP_PKEY_free(pkey);
return shared_secret_vec;
}
int main() {
// 初始化OpenSSL
OPENSSL_init_ssl(0, NULL);
// 1. 双方约定椭圆曲线
const EC_GROUP* group = get_ec_group();
// 2. 密钥生成
EC_KEY* alice_key = nullptr;
BIGNUM* alice_private_key = nullptr;
generate_key_pair(group, &alice_key, &alice_private_key);
EC_KEY* bob_key = nullptr;
BIGNUM* bob_private_key = nullptr;
generate_key_pair(group, &bob_key, &bob_private_key);
// 3. 公钥交换(模拟)
std::vector<unsigned char> alice_public_key_bytes = get_public_key_bytes(alice_key);
std::vector<unsigned char> bob_public_key_bytes = get_public_key_bytes(bob_key);
EC_KEY* received_alice_key = set_public_key_bytes(group, alice_public_key_bytes);
EC_KEY* received_bob_key = set_public_key_bytes(group, bob_public_key_bytes);
// 4. 密钥计算
std::vector<unsigned char> alice_shared_secret = compute_shared_secret(alice_key, received_bob_key);
std::vector<unsigned char> bob_shared_secret = compute_shared_secret(bob_key, received_alice_key);
// 验证
std::cout << "Alice shared secret (hex): ";
for (unsigned char byte : alice_shared_secret) {
printf("%02x", byte);
}
std::cout << std::endl;
std::cout << "Bob shared secret (hex): ";
for (unsigned char byte : bob_shared_secret) {
printf("%02x", byte);
}
std::cout << std::endl;
// 清理资源
EC_GROUP_free(group);
EC_KEY_free(alice_key);
EC_KEY_free(bob_key);
EC_KEY_free(received_alice_key);
EC_KEY_free(received_bob_key);
BN_free(alice_private_key);
BN_free(bob_private_key);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
return 0;
}

3.3 代码解释

  1. 包含头文件:引入OpenSSL相关的头文件。
  2. handle_error()函数:用于处理OpenSSL的错误。OpenSSL的错误处理机制比较复杂,务必检查每个函数的返回值,并在出错时调用ERR_print_errors_fp(stderr)打印错误信息。
  3. get_ec_group()函数:获取椭圆曲线的参数。这里我们使用NID_secp256r1曲线。
  4. generate_key_pair()函数:生成私钥和公钥对。使用EC_KEY_generate_key()生成密钥对。
  5. get_public_key_bytes()函数:将公钥转换为字节数组,以便传输。
  6. set_public_key_bytes()函数:将字节数组转换为公钥。
  7. compute_shared_secret()函数:计算共享密钥。使用ECDH_compute_key()函数计算共享密钥。
  8. main()函数
    • 初始化OpenSSL。
    • 模拟Alice和Bob的密钥生成、公钥交换和密钥计算过程。
    • 验证Alice和Bob的共享密钥是否相同。
    • 清理资源。 OpenSSL使用完后要释放资源,避免内存泄漏。

3.4 编译和运行

  1. 编译

    g++ ecdh.cpp -o ecdh -lcrypto -lssl
    

    确保你的系统已经安装了OpenSSL,并且-lcrypto-lssl链接了OpenSSL的库文件。

  2. 运行

    ./ecdh
    

    你将会看到Alice和Bob的共享密钥的十六进制表示,它们应该是一样的。

4. 安全性考虑

ECDH本身是安全的,但正确地实现ECDH并将其应用于实际场景中,需要注意以下几点:

  • 密钥的生成:私钥必须是随机生成的,且必须保密。如果私钥泄露,整个通信的安全性就会被破坏。
  • 椭圆曲线的选择:选择经过广泛审查的、安全的椭圆曲线,比如secp256r1curve25519等。不要使用自己定义的或未经充分验证的曲线。
  • 公钥的验证:接收方应该验证接收到的公钥是否合法。例如,可以检查公钥是否在椭圆曲线上,以及是否满足特定的约束条件。
  • 密钥的派生:ECDH计算出的共享密钥通常不能直接用于加密。需要使用密钥派生函数(KDF)来生成更适合用于加密的密钥。KDF可以增加密钥的随机性,并防止某些攻击。
  • 中间人攻击:ECDH本身不提供身份验证。如果没有身份验证,就容易受到中间人攻击。攻击者可以伪装成通信双方,与他们分别建立ECDH密钥交换,从而窃取通信内容。解决这个问题的方法是使用数字签名或证书来验证对方的身份。
  • 侧信道攻击:ECDH的实现可能受到侧信道攻击的影响,例如计时攻击、功耗分析攻击等。为了防止侧信道攻击,需要采取一些防御措施,例如使用恒定时间算法、掩码技术等。

5. 进阶:带身份验证的ECDH

前面提到,ECDH本身不提供身份验证,容易受到中间人攻击。为了解决这个问题,我们可以结合数字签名来实现带身份验证的ECDH。

5.1 数字签名简介

数字签名是一种用于验证消息完整性和发送者身份的技术。它基于非对称加密,主要流程如下:

  1. 密钥生成:签名者(比如Alice)生成一对密钥:私钥(用于签名)和公钥(用于验证签名)。
  2. 签名:签名者使用私钥对消息进行签名,生成签名值。签名值通常是消息的哈希值,经过私钥加密后的结果。
  3. 验证:验证者(比如Bob)使用签名者的公钥和签名值,对消息进行验证。验证过程包括计算消息的哈希值,然后使用签名者的公钥对签名值进行解密。如果解密后的结果与消息的哈希值匹配,则签名有效,说明消息是完整的,并且确实是由签名者发送的。

5.2 结合数字签名的ECDH流程

  1. 密钥生成
    • Alice:生成ECDH密钥对(a, A)和签名密钥对(sk_a, pk_a)。
    • Bob:生成ECDH密钥对(b, B)和签名密钥对(sk_b, pk_b)。
  2. 公钥交换和签名
    • Alice
      • 使用私钥sk_a对自己的ECDH公钥A进行签名,生成签名值sig_a
      • Asig_a发送给Bob。
    • Bob
      • 使用私钥sk_b对自己的ECDH公钥B进行签名,生成签名值sig_b
      • Bsig_b发送给Alice。
  3. 验证签名
    • Alice
      • 使用Bob的公钥pk_b验证sig_b,确认B确实是Bob发送的。
    • Bob
      • 使用Alice的公钥pk_a验证sig_a,确认A确实是Alice发送的。
  4. 密钥计算
    • Alice和Bob使用各自的ECDH私钥和对方的ECDH公钥,计算共享密钥K

5.3 Python实现(简化版)

由于篇幅限制,这里给出一个简化的Python示例,展示了带身份验证的ECDH的基本流程。 实际应用中,需要使用更完善的签名算法和库。

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, rsa, padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
# 1. 双方约定椭圆曲线
curve = ec.SECP256R1()
# 2. 密钥生成 (ECDH and Signatures)
def generate_key_pairs():
# ECDH keys
ec_private_key = ec.generate_private_key(curve, default_backend())
ec_public_key = ec_private_key.public_key()
# Signature keys (RSA example, could be other algorithms like ECDSA)
signature_private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
signature_public_key = signature_private_key.public_key()
return ec_private_key, ec_public_key, signature_private_key, signature_public_key
# 3. 公钥交换 and Signature
def sign_message(private_key, message):
signature = private_key.sign(
message,
padding.PKCS1v15(),
hashes.SHA256()
)
return signature
def verify_signature(public_key, message, signature):
try:
public_key.verify(
signature,
message,
padding.PKCS1v15(),
hashes.SHA256()
)
return True # Signature is valid
except Exception:
return False # Signature is invalid
# Serialization (Simplified for ECDH Public Key)
def serialize_ec_public_key(public_key):
return public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
def deserialize_ec_public_key(public_key_bytes):
return serialization.load_pem_public_key(
public_key_bytes, backend=default_backend()
)
# Alice's implementation
ec_private_key_alice, ec_public_key_alice, sig_private_key_alice, sig_public_key_alice = generate_key_pairs()
ec_public_key_alice_bytes = serialize_ec_public_key(ec_public_key_alice)
# Sign Alice's ECDH public key
signature_alice = sign_message(sig_private_key_alice, ec_public_key_alice_bytes)
# Bob's implementation
ec_private_key_bob, ec_public_key_bob, sig_private_key_bob, sig_public_key_bob = generate_key_pairs()
ec_public_key_bob_bytes = serialize_ec_public_key(ec_public_key_bob)
# Sign Bob's ECDH public key
signature_bob = sign_message(sig_private_key_bob, ec_public_key_bob_bytes)
# Simulate the exchange
received_ec_public_key_alice = deserialize_ec_public_key(ec_public_key_alice_bytes)
received_ec_public_key_bob = deserialize_ec_public_key(ec_public_key_bob_bytes)
# Verify Signatures
if verify_signature(sig_public_key_bob, ec_public_key_bob_bytes, signature_bob):
print("Alice: Bob's signature verified!")
else:
print("Alice: Bob's signature verification failed!")
exit(1)
if verify_signature(sig_public_key_alice, ec_public_key_alice_bytes, signature_alice):
print("Bob: Alice's signature verified!")
else:
print("Bob: Alice's signature verification failed!")
exit(1)
# Calculate shared secrets
def calculate_shared_secret(private_key, peer_public_key):
shared_key = private_key.exchange(ec.ECDH(), peer_public_key)
return shared_key
alice_shared_secret = calculate_shared_secret(ec_private_key_alice, received_ec_public_key_bob)
bob_shared_secret = calculate_shared_secret(ec_private_key_bob, received_ec_public_key_alice)
# Verify shared secrets (they should be equal)
print("Alice shared secret (bytes):", alice_shared_secret.hex())
print("Bob shared secret (bytes):", bob_shared_secret.hex())

5.4 代码解释

  1. 密钥生成:Alice和Bob分别生成ECDH密钥对和签名密钥对。这里用RSA作为签名算法。 实际上,ECDSA(Elliptic Curve Digital Signature Algorithm)更常用,因为它和ECC更匹配。
  2. 签名和验证sign_message()函数使用私钥对消息进行签名,verify_signature()函数使用公钥验证签名。 消息就是ECDH公钥的序列化后的字节串。
  3. 公钥交换:Alice和Bob交换ECDH公钥和签名。
  4. 验证签名:Alice和Bob分别验证对方的签名,确认对方的身份。
  5. 密钥计算:Alice和Bob使用各自的ECDH私钥和对方的ECDH公钥,计算共享密钥。

5.5 总结

通过结合数字签名,我们可以实现带身份验证的ECDH,有效地防止中间人攻击。 当然,这只是一个简化的示例,实际应用中需要使用更安全的签名算法、更完善的错误处理机制和更规范的协议。

6. 总结与展望

今天,咱们深入探讨了ECDH密钥交换,从理论到实践,从Python到C++,希望你对ECDH有了更全面的理解。咱们一起学习了:

  • ECDH的基本原理和流程。
  • Python和C++的实现方法,包括代码示例和详细解释。
  • ECDH的安全考虑,包括密钥的生成、椭圆曲线的选择、公钥的验证、密钥的派生、中间人攻击和侧信道攻击等。
  • 带身份验证的ECDH的实现方法,通过结合数字签名,增强了安全性。

ECDH作为一种重要的密钥交换协议,在安全通信中发挥着关键作用。 随着量子计算的发展,传统的密码学算法面临着新的挑战。 后量子密码学(Post-Quantum Cryptography)是未来密码学研究的重要方向,它研究如何在量子计算机的攻击下保持密码学的安全性。 基于格密码、多变量密码、哈希密码等的新型密码算法正在不断涌现,ECDH和其他密码学技术也在不断发展,以适应新的安全需求。 希望今天的学习对你有所启发,让你在密码学的世界里更进一步。

记住,安全无小事,密码学知识是保护信息安全的重要基石。 继续学习,不断探索,你就能成为一名出色的密码学专家! 加油!

码神老李 ECDH密钥交换椭圆曲线密码学PythonC++

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/8609