DiffieHellman(迪菲-赫尔曼)密钥交换算法原理及其实现
本文参考来源https://segmentfault.com/a/1190000010917737与https://zh.wikipedia.org/wiki/%E6%A8%A1%E9%99%A4 及其 https://www.cnblogs.com/DarkValkyrie/p/10962231.html (大佬的这篇文章的基础公式帮了大忙)
什么是Diffie-Hellman密钥交换算法?
Diffie-Hellman:一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一个组成部分。Whitefield与Martin Hellman在1976年提出了一个奇妙的密钥交换协议,称为Diffie-Hellman密钥交换协议/算法(Diffie-Hellman Key Exchange/Agreement Algorithm).这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥。然后可以用这个密钥进行加密和解密。但是注意,这个密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实现加密和解密消息。
算法绪论 --- 取模运算规则
- 模运算与基本四则运算有些相似,但是除法例外。其规则如下:
- (a + b) % p = (a % p + b % p) % p (1)
- (a - b) % p = (a % p - b % p) % p (2)
- (a * b) % p = (a % p * b % p) % p (3)
- a ^ b % p = ((a % p)^b) % p (4)
- 结合律:
- ((a+b) % p + c) % p = (a + (b+c) % p) % p (5)
- ((a*b) % p * c)% p = (a * (b*c) % p) % p (6)
- 交换律:
- (a + b) % p = (b+a) % p (7)
- (a * b) % p = (b * a) % p (8)
- 分配律:
- (a+b) % p = ( a % p + b % p ) % p (9)
- ((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (10)
首先DiffieHellman密钥交换算法的数论基础为
1.(a^Xa mod p)^Xb mod p = a^(Xa * Xb) mod p
2.取模操作的等价性及其交换律
1>. (a mod n) mod n = a mod n 等价性
2>. a ^ b % p = ((a % p)^b) % p
算法实现原理:
1. 假设客户端、服务端挑选两个素数a、p(都公开),然后
-
客户端:选择自然数Xa,Ya = a^Xa mod p,并将Ya发送给服务端;
-
服务端:选择自然数Xb,Yb = a^Xb mod p,并将Yb发送给客户端;
-
客户端:计算 Ka = Yb^Xa mod p
- 服务端:计算 Kb = Ya^Xb mod p
推导过程
Ka = Yb^Xa mod p
= (a^Xb mod p)^Xa mod p
==> (a^Xb mod p)^Xa mod p 该式子由4可以拆分为 (a^Xb)^Xa mod p
= a^(Xb * Xa) mod p
= (a^Xa mod p)^Xb mod p
= Ya^Xb mod p
= Kb
可以看到,尽管客户端、服务端彼此不知道对方的Xa、Xb,但算出了相等的secret。
Nodejs代码示例
结合前面小结的介绍来看下面代码,其中,要点之一就是client、server采用相同的素数a、p。
var crypto = require('crypto’); var primeLength = 1024; // 素数p的长度 var generator = 5; // 素数a // 创建客户端的DH实例 var client = crypto.createDiffieHellman(primeLength, generator); // 产生公、私钥对,Ya = a^Xa mod pvar clientKey = client.generateKeys(); // 创建服务端的DH实例,采用跟客户端相同的素数a、p var server = crypto.createDiffieHellman(client.getPrime(), client.getGenerator()); // 产生公、私钥对,Yb = a^Xb mod p var serverKey = server.generateKeys(); // 计算 Ka = Yb^Xa mod p var clientSecret = client.computeSecret(server.getPublicKey()); // 计算 Kb = Ya^Xb mod p var serverSecret = server.computeSecret(client.getPublicKey()); // 由于素数p是动态生成的,所以每次打印都不一样 // 但是 clientSecret === serverSecret console.log(clientSecret.toString('hex')); console.log(serverSecret.toString('hex'));