对称加密算法解决了数据加密的问题。
    我们以AES加密为例,在现实世界中,小明要向路人甲发送一个加密文件,他可以先生成一个AES密钥,对文件进行加密,然后把加密文件发送给对方。
    因为对方要解密,就必须需要小明生成的密钥。
    现在问题来了:如何传递密钥?
    在不安全的信道上传递加密文件是没有问题的,因为黑客拿到加密文件没有用。但是,如何在不安全的信道上安全地传输密钥?
    要解决这个问题,密钥交换算法即DH算法:Diffie-Hellman算法应运而生。
    DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。
    先复习一下数学术语

    因数:是指整数a除以整数b(b!=0) 的商正好是整数而没有余数,我们就说b是a的因数。 素数:是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。 底数:是指幂(x = n^m) 中的n,或者对数(x=logaN)中的a(a>0且a!=1)。

    我们来看DH算法交换密钥的步骤。假设甲乙双方需要传递密钥,他们之间可以这么做:

    1. 甲首先选择一个素数p,例如509,底数g,任选,例如5,随机数a,例如123,然后计算A=g^a mod p

    结果是215,然后甲发送p = 509g=5A=215给乙;

    1. 乙方收到后,也选择一个随机数b,例如,456,然后计算B = g^b mod p,结果是181,乙再同时计算

    s=A^b mod p,结果是121

    1. 乙把计算的B=181发给甲,甲计算s=B^a mod p的余数,计算结果与乙计算出的结果一样都是121。

    所以最终双方协商出的密钥s是121。注意到这个密钥s并没有在网络上传输。而通过网络传输的p,gA``B是无法推算出s 的,因为实际算法选择的素数是非常大的。
    所以,更确切地说,DH算法是一个密钥协商算法,双方最终协商出一个共同的密钥,而这个密钥不会通过网络传输。
    如果我们把a看成甲的私钥,A看成甲的公钥,b看成乙的私钥,B看成乙的公钥,DH算法的本质就是双方各自生成自己的私钥和公钥,私钥仅对自己可见,然后交换公钥,并根据自己的私钥和对方的公钥,生成最终的密钥secretKey,DH算法通过数学定律保证了双方各自计算出的secretKey是相同的。

    使用Java实现DH算法的代码:

    1. public class Main{
    2. public static void main(String[] args){
    3. //BOb 和Alice
    4. Person bob = new Person("Bob");
    5. Person alice = new Person("Alice");
    6. //各自生成KeyPair
    7. bob.generateKeyPair();
    8. alice.generateKeyPair();
    9. //双方交换各自的PublicKey
    10. //Bob根据Alice的PublicKey生成自己本地密钥:
    11. bob.generateSecretKey(alice.publicKey.getEncoded());
    12. //Alice根据Bob的PublicKey生成自己的本地密钥
    13. alice.generateSecretKey(bob.publicKey.getEncoded());
    14. //检查双方的本地密钥是否相同
    15. bob.printKeys();
    16. alice.printkeys();
    17. //双方的SecretKey相同,后续通信将使用Secretkey作为密钥进行AES加解密
    18. }
    19. }
    20. class Person {
    21. private final String name;
    22. public PublicKey publicKey;
    23. private Privatekey privateKey;
    24. private byte[] secretKey;
    25. public Person(String name){
    26. this.name = name;
    27. }
    28. public void generatekeyPair() {
    29. try{
    30. KeyPairGenerator KpGen = KeyPairGenerator.getInstance("DH");
    31. KpGen.initialize(512);
    32. KeyPair kp = KpGen.generateKeyPair();
    33. this.privateKey = kp.getPrivate();
    34. this.publicKey = kp.getPublic();
    35. } catch (GeneralSecurityException e) {
    36. throw new RuntimeException(e);
    37. }
    38. }
    39. public void generateSecretKey(byte[] receivedPubkeyBytes) {
    40. try{
    41. //从byte[] 恢复 PublicKey
    42. X509EncodedKeySpec keyspec = new X509EncodedKeySpec(receivedPubKeyBytes);
    43. KeyFactory kf = KeyFactory.getInstance("DH");
    44. PublicKey receivedPublickey = kf.generatePublic(keySpec);
    45. //生成本地密钥
    46. KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
    47. KeyAgreement.init(this.privateKey);//自己的Privatekey
    48. keyAgreement.doPhase(receivedPublicKey, true);//对方的PublicKey
    49. //生成SecretKey密钥
    50. this.secretKey = keyAgreement.generateSecret();
    51. }catch (GeneralSecurityExcetpion e) {
    52. throw new RuntimException(e);
    53. }
    54. }
    55. public void printKeys(){
    56. System.out.printf("name: %s\n", this.name);
    57. System.out.printf("Public key: %x\n", new BigInteger(1,this.privteKey,getEncoded());
    58. System.out.printf("Secret key: %x\n",new BigInteger(1,this.secretKey));
    59. }
    60. }