密钥交换,也有称作密钥协商,这套机制,最主要的作用是用来得到通信双方的临时会话密钥。
这里的临时会话密钥,可以理解为对称加密的密钥,只不过他的有效性仅限于一次会话链接,并不是长期有效的。
前面其实我们分析过,分对称密钥比如RSA也是可以做加解密的,那么在实际的通信过程比如TLS中,为什么还需要生成临时的对称密钥呢?
这里最核心的原因有两个:
基于RSA进行密钥交换,基于非对称密钥的两个基本特性:
在简单的密钥交换场景中,有两个基本角色,客户端与服务端。
客户端与服务端进行正常的业务流通信前,总是需要先线上一把数据密钥,用于给业务流进行加解密。
这里我们约定服务端总是权威的,其公钥是被所有客户端所感知的,客户端是任意的,没有身份上的要求,客户端总是主动发起于服务端的通信,并且服务端总是回复可信的数据给客户端。
在基于RSA的密钥交换体系中,总是由客户端来生成密钥。
这是因为,客户端总是能够获取到服务端的公钥,由客户端生成随机密钥,然后由服务端使用私钥解密,这样可以保证随机密钥的保密性。
私钥总是由服务端私密保存,绝对不会公开。
这是基于RSA进行数据安全加密的前提。
也是基于RSA进行密钥交换的基础。
服务端在使用私钥对随机密钥密文解密后便默认承认了与客户端使用的是同一把随机密钥。
随后的业务流数据便使用这把随机密钥进行数据通信。
客户端是无法区分公钥来源的,这是RSA可能被中间人攻击的前提。
客户端只能够使用公钥对随机密钥进行加密,但是,这份公钥究竟是属于真实服务端,还是属于中间人的,客户端自己无法区分。
一旦客户端接受了中间人的公钥,那么就意味着客户端默认了中间人是真实的服务端。
那么在前面我们描述的数据密钥的交换过程,就可以被中间人完全监听甚至是篡改。
客户端在生成随机密钥并加密后,其消息会被中间人全部监听,由于客户端使用的是中间人的公钥,因此即使随机密钥被加密,中间人还是可以完全获取其明文。
中间人同样可以对服务端进行迷惑。
比如,在使用自己的私钥对随机密钥解密后,再试用服务端公钥对随机密钥加密,然后发送给服务端。
此时的服务端,其实也无法区分,发送数据过来的,究竟是客户端还是中间人。
在RSA密钥交换过程中,中间人需要保证:
防止中间人攻击的方法实际上就是身份证认证方式,目前主流方式就是数字签名的方式。
在前面的文章《非对称密钥沉思系列(3):公钥、签名与证书》中我们聊了证书与身份认证的一些底层逻辑。
在RSA的这种密钥交换过程中,同样可以很好地应用证书来进行身份的鉴别与认证。
在对服务端的认证过程中,最重要的仍然是要解决三个问题:
这里细节就不赘述了,在前文中有详细描述,感兴趣的可以翻一下《非对称密钥沉思系列(3):公钥、签名与证书》。
在前面的描述中,我们其实只是着重的描述了客户端使用公钥加密随机密钥,然后服务端使用私钥对随机密钥解密的过程。其实结合《非对称密钥沉思系列(2):聊聊RSA与数字签名》中的内容,我们在做随机密钥的交换时,还可以结合对随机密钥的HMAC等手段,保证随机密钥的不被篡改。这里感兴趣的同学可以自己思考下。
前面我们聊了很多RSA,但其实,RSA更侧重于非对称密钥算法,主要功能其实还是在于加密与解密。
而密钥交换协议DH,是专门用于协商密钥生成的。
RSA可以用来传输信息,DH更适合用来协商密钥。
DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。
由于DH系列算法涉及比较复杂的数学推理运算,这里不做过多展开讲解,感兴趣的同学可以翻阅相关RFC文档等。
最原始的DH算法并不能对抗MIMT(Man-in-the-middle attack),所以一般需要配合签名技术,不配合签名技术的DH称为,DH-ANON;
配合RSA签名的称为,DH-RSA;
配合DSA签名的称为,DH-DSA;
配合ECDSA签名的称为,DH-ECDSA;
总的来说,DH协议也怕中间人攻击,一般来说,也需要配置数字证书来进行身份的认证。
Forward security前向保密,最初用来定义绘画密钥交换协议的一种安全性。即使长期密钥已经泄露,也不会影响之前的会话密钥的泄露,也就不会暴露之前的会话内容。
DH和ECDH算法为了实现前向安全,变种加入了另一个随机变量ephemeral key得到新的算法DHE、ECDHE。
RSA、DH和DSA都是基于整数有限域离散对数来实现。
ECC和ECDH都是基于椭圆曲线的离散对数难题来实现的。
现在实际使用中,优先选择 ECDHE>DHE> DH,RSA ...等。
from typing import Tuple
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.asymmetric.dh import DHParameters, DHPrivateKey, DHPublicKey
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
def data_key_exchange(owner_pri_key: DHPrivateKey, peer_pub_key: DHPublicKey) -> bytes:
shared_key = owner_pri_key.exchange(peer_pub_key)
derived_key = HKDF(algorithm = hashes.SHA256(),
length = 32,
salt = None,
info = b'handshake data',
backend = default_backend()).derive(shared_key)
print("derived_key:{}, length:{}".format(list(derived_key), len(derived_key)))
return derived_key
def generate_common_parameters() -> DHParameters:
parameters = dh.generate_parameters(generator = 2, key_size = 2048,
backend = default_backend())
return parameters
def generate_dh_key(parameters: DHParameters) -> Tuple[DHPrivateKey, DHPublicKey]:
private_key = parameters.generate_private_key()
public_key = private_key.public_key()
return private_key, public_key
if __name__ == '__main__':
param = generate_common_parameters()
a_pri_key, a_pub_key = generate_dh_key(param)
b_pri_key, b_pub_key = generate_dh_key(param)
a_data_key = data_key_exchange(a_pri_key, b_pub_key)
b_data_key = data_key_exchange(b_pri_key, a_pub_key)
print(a_data_key == b_data_key)
最终生成的密钥数据:
derived_key:[3, 139, 194, 229, 249, 124, 131, 14, 141, 172, 168, 29, 26, 152, 63, 253, 227, 234, 189, 101, 130, 192, 139, 99, 50, 8, 153, 75, 31, 247, 200, 24], length:32
derived_key:[3, 139, 194, 229, 249, 124, 131, 14, 141, 172, 168, 29, 26, 152, 63, 253, 227, 234, 189, 101, 130, 192, 139, 99, 50, 8, 153, 75, 31, 247, 200, 24], length:32
True
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。