我們來看DH算法交換密鑰的步驟。假設甲乙雙方需要傳遞密鑰,他們之間可以這麼做:
甲首選選擇一個素數p,例如509,底數g,任選,例如5,隨機數a,例如123,然後計算A=g^a mod p,結果是215,然後,甲發送p=509,g=5,A=215給乙;
乙方收到後,也選擇一個隨機數b,例如,456,然後計算B=g^b mod p,結果是181,乙再同時計算s=A^b mod p,結果是121;
乙把計算的B=181發給甲,甲計算s=B^a mod p的餘數,計算結果與乙算出的結果一樣,都是121。
所以最終雙方協商出的密鑰s是121。注意到這個密鑰s並沒有在網絡上傳輸。而通過網絡傳輸的p,g,A和B是無法推算出s的,因爲實際算法選擇的素數是非常大的。
所以,更確切地說,DH算法是一個密鑰協商算法,雙方最終協商出一個共同的密鑰,而這個密鑰不會通過網絡傳輸。
如果我們把a看成甲的私鑰,A看成甲的公鑰,b看成乙的私鑰,B看成乙的公鑰,DH算法的本質就是雙方各自生成自己的私鑰和公鑰,私鑰僅對自己可見,然後交換公鑰,並根據自己的私鑰和對方的公鑰,生成最終的密鑰secretKey,DH算法通過數學定律保證了雙方各自計算出的secretKey是相同的。
使用Java實現DH算法的代碼如下:
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import javax.crypto.KeyAgreement;
public class Main {
public static void main(String[] args) {
// Bob和Alice:
Person bob = new Person("Bob");
Person alice = new Person("Alice");
// 各自生成KeyPair:
bob.generateKeyPair();
alice.generateKeyPair();
// 雙方交換各自的PublicKey:
// Bob根據Alice的PublicKey生成自己的本地密鑰:
bob.generateSecretKey(alice.publicKey.getEncoded());
// Alice根據Bob的PublicKey生成自己的本地密鑰:
alice.generateSecretKey(bob.publicKey.getEncoded());
// 檢查雙方的本地密鑰是否相同:
bob.printKeys();
alice.printKeys();
// 雙方的SecretKey相同,後續通信將使用SecretKey作爲密鑰進行AES加解密...
}
}
class Person {
public final String name;
public PublicKey publicKey;
private PrivateKey privateKey;
private byte[] secretKey;
public Person(String name) {
this.name = name;
}
// 生成本地KeyPair:
public void generateKeyPair() {
try {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH");
kpGen.initialize(512);
KeyPair kp = kpGen.generateKeyPair();
this.privateKey = kp.getPrivate();
this.publicKey = kp.getPublic();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
public void generateSecretKey(byte[] receivedPubKeyBytes) {
try {
// 從byte[]恢復PublicKey:
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes);
KeyFactory kf = KeyFactory.getInstance("DH");
PublicKey receivedPublicKey = kf.generatePublic(keySpec);
// 生成本地密鑰:
KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
keyAgreement.init(this.privateKey); // 自己的PrivateKey
keyAgreement.doPhase(receivedPublicKey, true); // 對方的PublicKey
// 生成SecretKey密鑰:
this.secretKey = keyAgreement.generateSecret();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
public void printKeys() {
System.out.printf("Name: %s\n", this.name);
System.out.printf("Private key: %x\n", new BigInteger(1, this.privateKey.getEncoded()));
System.out.printf("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded()));
System.out.printf("Secret key: %x\n", new BigInteger(1, this.secretKey));
}
}
參考文獻
https://www.liaoxuefeng.com/wiki/1252599548343744/1304227905273889