对称加密算法解决了数据加密的问题。
我们以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算法交换密钥的步骤。假设甲乙双方需要传递密钥,他们之间可以这么做:
- 甲首先选择一个素数
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算法的代码:
public class Main{public static void main(String[] args){//BOb 和AlicePerson bob = new Person("Bob");Person alice = new Person("Alice");//各自生成KeyPairbob.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 {private final String name;public PublicKey publicKey;private Privatekey privateKey;private byte[] secretKey;public Person(String name){this.name = name;}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[] 恢复 PublicKeyX509EncodedKeySpec keyspec = new X509EncodedKeySpec(receivedPubKeyBytes);KeyFactory kf = KeyFactory.getInstance("DH");PublicKey receivedPublickey = kf.generatePublic(keySpec);//生成本地密钥KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");KeyAgreement.init(this.privateKey);//自己的PrivatekeykeyAgreement.doPhase(receivedPublicKey, true);//对方的PublicKey//生成SecretKey密钥this.secretKey = keyAgreement.generateSecret();}catch (GeneralSecurityExcetpion e) {throw new RuntimException(e);}}public void printKeys(){System.out.printf("name: %s\n", this.name);System.out.printf("Public key: %x\n", new BigInteger(1,this.privteKey,getEncoded());System.out.printf("Secret key: %x\n",new BigInteger(1,this.secretKey));}}
