本文參考來源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'));