目录
离散对数问题(Discrete Logarithm Problem,DLP)
ECDH (Elliptic Curve Diffie-Hellman)算法
ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)算法
基本原理
密钥交换算法的基本原理是使得两个通信方能够在不安全的通信渠道上安全地协商出一个共享密钥,该共享密钥用于对后续通信进行加密,以确保通信信息的机密性和完整性。密钥交换算法通常利用数学上的难题来确保即使交换过程被窃听,窃听者也不能轻易推导出共享密钥。
以下是密钥交换算法的一些基本概念:
公开参数
在大多数密钥交换协议中,参与者需要预先同意一些公开的参数。这些参数通常是通过不安全的渠道公开交换的,因此不会泄露任何秘密信息。例如,在Diffie-Hellman密钥交换中,公开参数是一个大素数和一个基数。
私有密钥
每个参与者都会生成一个私有密钥,这个密钥是秘密的,通常是通信设备自己保存,不会通过不安全的渠道公开。私有密钥在密钥交换过程中起到了至关重要的作用。
公开密钥
每个参与者都会使用公开参数和自己的私有密钥生成一个公开密钥。公开密钥在不安全的渠道上传输,虽然它是由私有密钥和公开参数生成的,但依然无法直接推导出私有密钥。
密钥推导
当通信双方收到对方的公开密钥后,他们将使用自己的私有密钥和对方的公开密钥来生成共享密钥。重要的是,尽管窃听者可能知道公开参数和双方的公开密钥,但没有私有密钥,他们不能计算出共享密钥。
数学难题
密钥交换算法通常基于数学上的难题,如离散对数问题或椭圆曲线上的类似问题。这些问题在一定条件下是计算上可行的,但在没有足够信息的情况下(例如,没有私有密钥)是极其困难的。
前向保密性(Forward Secrecy)
一些密钥交换算法,如ECDHE,提供了前向保密性。这意味着即使在将来某个时刻一个参与者的私有密钥被发现,之前的通信记录也无法被解密,因为每次通信都使用了不同的临时密钥对。
相关数学
基本数学公式
先来回忆一下相关的数学公式
%加法
(a + b) mod n = [(a mod n) + (b mod n)] mod n
%减法
(a - b) mod n = [(a mod n) - (b mod n) + n] mod n
%乘法
(a * b) mod n = [(a mod n) * (b mod n)] mod n
%指数律
(a^b) mod n = [(a mod n)^b] mod n
%指数分解
(a^(b+c)) mod n = [(a^b mod n) * (a^c mod n)] mod n
%指数乘法
(a^(b*c)) mod n = [(a^b mod n)^c] mod n
离散对数问题
(Discrete Logarithm Problem,DLP)
在数学中,一个群(Group)是一种代数结构,由一组元素以及一个在这些元素上定义的操作(通常称为群运算)组成,满足以下四个基本性质:
- 封闭性(Closure): 对于群内的任意两个元素a和b,a和b的群运算的结果也必须在群内。
- 结合律(Associativity): 对于群内的任意三个元素a、b和c,(ab)c和a(bc)有相同的结果(这里的
*
代表群运算)。 - 单位元素(Identity element): 群内必须存在一个特殊的元素e,对于群内的任意元素a,e和a的群运算结果仍然是a。
- 逆元素(Invertibility): 对于群内的任意元素a,必须存在一个元素b(称为a的逆元素),使得a和b的群运算结果是单位元素e。
一个有限群是指群中元素的数量是有限的。换句话说,有限群包含了有限个元素,并对这些元素进行群运算仍然会得到群内的元素。有限群的大小,也就是它包含的元素个数,称为该群的阶(Order)。
生成元(Generator)是有限群中的一个特殊元素,它可以通过群运算生成群中的所有其他元素。如果一个群的所有元素都可以通过对一个元素进行重复的群运算(例如加法或乘法)来获得,那么这个元素被称为该群的生成元。
离散对数问题的定义: 给定一个有限群G,其生成元为g,以及群G中的一个元素h,离散对数问题要求找到一个整数x,使得 g^x = h 在群G中成立。这里的x就是h关于底数g的离散对数。
在更加直观的数学术语中,如果我们考虑模p的乘法群(其中p是质数),问题可以表述为:给定两个元素g和h,找到一个整数x,使得 g^x ≡ h (mod p)。
离散对数问题的计算困难性: 在有限群中进行乘法运算(例如计算g^x)通常是可行的,甚至对于非常大的x值也是如此,因为可以使用如“快速幂”等有效的算法。然而,反过来解决离散对数问题,即在给定g和h的情况下找到x,却是非常困难的,尤其是当群的阶(即群中元素的数量)非常大时。
目前为止,没有已知的多项式时间算法能够解决离散对数问题。这意味着所有已知的算法,在最坏情况下,它们的运行时间都是关于群阶数目大小的指数函数。随着群的阶数增加,解决离散对数问题所需的计算复杂性迅速增长,这使得破解相应的密码系统变得不切实际。
在实际应用中,确保离散对数问题的困难性通常涉及选择足够大的群,使得使用当前的计算技术在合理时间内解决DLP变得不可行。例如,对于Diffie-Hellman密钥交换,推荐使用至少2048位的质数p;对于椭圆曲线密码学,虽然群的阶可以小得多(比如256位或384位),但由于椭圆曲线的特殊性质,解决对应的椭圆曲线离散对数问题(ECDLP)仍然是困难的。
需要注意的是,离散对数问题的困难性是基于当前的数学和计算知识。未来的突破,比如量子计算,可能会使得解决离散对数问题变得容易。
DH (Diffie-Hellman)算法
1976年,Whitfield Diffie 和 Martin Hellman 发表了一种革命性的密钥交换方法,使得两个通信方能够在不安全的通道上安全地协商出共享密钥。Diffie-Hellman 密钥交换依赖于离散对数问题的计算困难性。
基本流程
DH算法的基本流程如下:
- Alice和Bob同意使用一个公开的大素数
p
和一个基数(也叫生成元)g
。 - Alice选择一个私有密钥
a
,计算A = g^a mod p
作为她的公开密钥,并将A
发送给Bob。 - Bob选择一个私有密钥
b
,计算B = g^b mod p
作为他的公开密钥,并将B
发送给Alice。 - Alice接收到
B
,计算S = B^a mod p
作为共享密钥。 - Bob接收到
A
,计算S = A^b mod p
作为共享密钥。 - 如果算法正确执行,Alice和Bob现在共享一个密钥
S
%Alice
S = B^a mod p = (g^b mod p)^a mod p = g^ab mod p
%Bob
S = A^b mod p = (g^a mod p)^b mod p = g^ab mod p
通过这种方式,即使一个窃听者知道A
和B
以及公开参数p
和g
,也无法轻易计算出共享密钥,因为解决离散对数问题在计算上是不可行的。
代码实现
import random
import math
class DHKeyExchange:
def __init__(self, p, g):
"""
初始化DH密钥交换类,p为素数,g为生成元
"""
self.p = p
self.g = g
def generate_private_key(self):
"""
生成私钥
私钥应该是一个随机数,这里简化处理,随机生成一个小于p的数
"""
return random.randint(2, self.p - 2)
def compute_public_key(self, private_key):
"""
根据私钥计算公钥
"""
return pow(self.g, private_key, self.p)
@staticmethod
def compute_shared_secret(private_key, other_public_key, p):
"""
根据本地私钥和对方公钥计算共享密钥
"""
return pow(other_public_key, private_key, p)
def main():
# 定义一个简单的小型有限域参数,实际应用请使用更大更安全的参数
p = 23 # 实际中应当使用足够大的素数
g = 5 # 基数(生成元),需确保g与p-1互质
alice = DHKeyExchange(p, g)
bob = DHKeyExchange(p, g)
# 生成私钥
private_key_alice = alice.generate_private_key()
private_key_bob = bob.generate_private_key()
# 计算公钥
public_key_alice = alice.compute_public_key(private_key_alice)
public_key_bob = bob.compute_public_key(private_key_bob)
# 交换公钥
# 在实际网络通信中,alice_public_key会被发送给Bob,bob_public_key会被发送给Alice
# 此处假定已经通过网络完成公钥交换
# 计算共享密钥
shared_key_alice = DHKeyExchange.compute_shared_secret(private_key_alice, public_key_bob, p)
shared_key_bob = DHKeyExchange.compute_shared_secret(private_key_bob, public_key_alice, p)
# 在DH算法中,Alice和Bob计算出的共享密钥应该是相同的
assert shared_key_alice == shared_key_bob
print(f"Alice的共享密钥为:{shared_key_alice}")
print(f'Bob的共享密钥为: {shared_key_bob}')
if __name__ == "__main__":
main()
运行结果:
/home/xxx/Project/Python/Demos/.venv/bin/python /home/xxx/Project/Python/Demos/DH.py
Alice的共享密钥为:18
Bob的共享密钥为: 18
这里的素数和基数是直接给定的,而在实际应用中,应该使用大的随机素数和基数来增强安全性。另外,为了确保密钥的安全性,会对生成的共享密钥进行哈希处理,然后使用哈希值作为对称加密算法的密钥。上面这段代码主要是演示用途,在实际应用中可以使用现成的加密库,如Python的cryptography
库,这些库会处理更多的安全细节,并且会使用更安全的参数。
使用cryptography库的代码实现
import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# 生成Diffie-Hellman参数。这通常是预先约定好的。
def generate_dh_parameters():
# 使用2048位的质数
parameters = dh.generate_parameters(generator=2, key_size=2048, backend=default_backend())
return parameters
# 生成密钥对
def generate_dh_keys(parameters):
private_key = parameters.generate_private_key()
public_key = private_key.public_key()
return private_key, public_key
# 计算共享密钥
def compute_shared_secret(private_key, peer_public_key):
shared_key = private_key.exchange(peer_public_key)
# 使用HKDF来派生可用的共享密钥
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b'handshake data',
backend=default_backend()
).derive(shared_key)
return derived_key
# 主函数
def main():
# 起始时间
time_start = time.time()
# 生成DH参数
parameters = generate_dh_parameters()
# Alice生成密钥对
private_key_alice, public_key_alice = generate_dh_keys(parameters)
# Bob生成密钥对
private_key_bob, public_key_bob = generate_dh_keys(parameters)
# Alice和Bob交换公钥并计算共享密钥
# 在实际网络通信中,alice_public_key会被发送给Bob,bob_public_key会被发送给Alice
# 此处假定已经通过网络完成公钥交换
shared_key_alice = compute_shared_secret(private_key_alice, public_key_bob)
shared_key_bob = compute_shared_secret(private_key_bob, public_key_alice)
# 结束时间
time_end = time.time()
# 验证Alice和Bob的共享密钥是否相同
assert shared_key_alice == shared_key_bob
# 输出共享密钥
print(f"Alice的共享密钥为: {shared_key_bob.hex()}")
print(f"Bob的共享密钥为: {shared_key_bob.hex()}")
print(f'运行时间:{time_end - time_start}')
if __name__ == "__main__":
main()
运行结果:
/home/xxx/Project/Python/Demos/.venv/bin/python /home/xxx/Project/Python/Demos/DHCryptography.py
Alice的共享密钥为: 55a61c83d4f4db5fd4c7da843060b13f3cfe55266a92d3f8a2ccab35f3fb2c88
Bob的共享密钥为: 55a61c83d4f4db5fd4c7da843060b13f3cfe55266a92d3f8a2ccab35f3fb2c88
运行时间:23.391730546951294
DH算法的运行时间会有波动,这里取其中一次的时间,整体上运行时间还是很长的。
ECDH (Elliptic Curve Diffie-Hellman)算法
随着计算机算力的提升,DH 算法需要更大的素数以保持安全性,这导致计算效率下降且对设备的算力要求变高,对嵌入式设备使用DH算法变得困难。ECDH的基本原理类似于传统的Diffie-Hellman密钥交换,但它使用椭圆曲线密码学(ECC)代替模数幂运算,提供了相同安全级别的前提下更短的密钥长度。
基本流程
ECDH的基本流程如下
- 双方同意使用一个椭圆曲线
E
和一个基点G
,这两个参数都是公开的。 - 甲方选择一个私有的随机数
a
,计算A = aG
(椭圆曲线上的点乘运算),然后将A
发送给乙方。 - 乙方选择一个私有的随机数
b
,计算B = bG
,然后将B
发送给甲方。 - 甲方收到
B
后,计算S = aB
。 - 乙方收到
A
后,计算S = bA
。 - 由于椭圆曲线的性质,
aB
和bA
都是相同的点P
,P
的横坐标(通常)用作共享密钥。
代码实现
import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# 生成ECDH的密钥对
def generate_ecdh_key_pair():
# 选择椭圆曲线
curve = ec.SECP521R1()
# 生成私钥
private_key = ec.generate_private_key(curve, default_backend())
# 生成公钥
public_key = private_key.public_key()
return private_key, public_key
# 计算共享密钥
def compute_shared_secret(private_key, peer_public_key):
# 使用私钥和对方的公钥计算共享密钥
shared_key = private_key.exchange(ec.ECDH(), peer_public_key)
# 使用HKDF来派生可用的共享密钥
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b'handshake data',
backend=default_backend()
).derive(shared_key)
return derived_key
# 主程序
def main():
# 起始时间
time_start = time.time()
# Alice生成密钥对
private_key_alice, public_key_alice = generate_ecdh_key_pair()
# Bob生成密钥对
private_key_bob, public_key_bob = generate_ecdh_key_pair()
# Alice和Bob交换公钥并计算共享密钥
# 在实际网络通信中,alice_public_key会被发送给Bob,bob_public_key会被发送给Alice
# 此处假定已经通过网络完成公钥交换
shared_key_alice = compute_shared_secret(private_key_alice, public_key_bob)
shared_key_bob = compute_shared_secret(private_key_bob, public_key_alice)
#结束时间
time_end = time.time()
# 检查双方是否得到相同的共享密钥
assert shared_key_alice == shared_key_bob
# 输出共享密钥
print(f'Alice的共享密钥为: {shared_key_alice.hex()}')
print(f'Bob的共享密钥为: {shared_key_bob.hex()}')
print(f'运行时间:{time_end-time_start}')
if __name__ == '__main__':
main()
运行结果:
/home/xxx/Project/Python/Demos/.venv/bin/python /home/xxx/Project/Python/Demos/ECDH.py
Alice的共享密钥为: a6aa7cfd76bbd07eae62e40122a6970f4f7a7fcf01ba3ece35bb83c6009c7063
Bob的共享密钥为: a6aa7cfd76bbd07eae62e40122a6970f4f7a7fcf01ba3ece35bb83c6009c7063
运行时间:0.018299579620361328
运行时间稳定,效率高于DH算法。
ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)算法
ECDHE和ECDH在算法上是非常相似的,主要区别在于“临时性”(Ephemeral)的概念。ECDHE在每次通信时都会生成一对新的密钥,而不是使用一对固定的密钥。这样一来,即使某个会话的密钥被破解,之前的通信记录仍然是安全的,因为它们使用的是不同的密钥。这提供了所谓的“前向保密性”(Forward Secrecy),即即使长期私钥泄露,也不会影响之前会话的安全性。
在实际应用中,ECDHE通常与TLS(传输层安全协议)一起使用,以提供安全的网络通信。它是现代互联网安全通信的关键组成部分,包括HTTPS、SSH等协议中都有广泛应用。
ECDHE和ECDH的差异点在于每次通信时都会重新生成密钥对,因此具体的代码实现可以参考ECDH的代码实现即可。