https密鑰交換DH算法

我們來看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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章