对称加密算法解决了数据加密的问题,例如AES加密可以有效地保护文件的安全性。然而,一个关键的挑战是如何在不安全的通信信道上安全地传输密钥。
假设小明需要向路人甲发送一个加密文件,他可以先生成一个AES密钥,使用该密钥对文件进行加密,然后将加密后的文件发送给对方。但是,问题在于对方需要密钥才能解密文件,因此密钥的传输必须是安全的。
在传统的密钥传输方法中,密钥通常通过不同的方式发送,如口头告知、书面传递或者其他安全信道。然而,在不安全的通信信道上,这些方法可能会暴露密钥,导致密钥被截获或篡改,从而威胁到加密数据的安全性。
为了解决这个问题,出现了密钥交换算法,例如Diffie-Hellman算法。Diffie-Hellman算法允许通信双方在不安全的通信信道上协商一个共享密钥,而不需要事先共享任何秘密信息。通过该算法,通信双方可以在不直接传输密钥的情况下安全地协商出一个共享的密钥,从而实现安全的加密通信。
DH算法是一种通过数学原理实现安全密钥交换的方法,它允许通信双方在不直接传输密钥的情况下协商出一个共享的密钥。
综上所述,密钥交换算法的出现弥补了传统密钥传输方法的不足,在不安全的通信信道上安全地传输密钥,为加密通信提供了更加可靠的保障。
Diffie-Hellman算法是一种用于安全地交换密钥的协议,通常用于在不安全的通信信道上建立共享密钥,以便进行加密通信。这个算法允许两个对等方在没有事先共享密钥的情况下,通过公开的交换来生成共享的密钥。Diffie-Hellman算法的核心思想是利用离散对数问题的困难性,使得即使在公开的通信信道上,攻击者也无法推导出共享密钥。
简单来说,Diffie-Hellman算法的步骤如下:
我们举个例子来看
A=g^a mod p
,得到A=34。B=g^b mod p
,得到B=75。同时,乙方计算出共享密钥s = A^b mod p
,得到s=22。s = B^a mod p
,得到的结果与乙方计算的结果一样,都是22。因此,最终双方都得到了相同的共享密钥s=22。需要注意的是,通过网络传输的参数p、g、A和B无法推算出密钥s,因为实际应用中选择的素数p非常大,计算s的复杂度很高,从而保证了密钥的安全性。
通过这种方式,甲乙双方在不直接传输密钥的情况下成功完成了密钥交换,从而实现了安全的通信。
由于Diffie-Hellman算法的数学基础比较复杂,它的安全性建立在一个数学难题上,即计算离散对数的困难性。攻击者需要解决这个数学难题才能推导出共享密钥,因此Diffie-Hellman算法被广泛应用于安全通信领域。
简单的Java实现示例:
import java.math.BigInteger;
import java.security.SecureRandom;
public class DiffieHellman {
// 素数p
private static final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16);
// 原根g
private static final BigInteger g = BigInteger.valueOf(2);
// 生成私钥
public static BigInteger generatePrivateKey() {
SecureRandom random = new SecureRandom();
BigInteger privateKey = new BigInteger(p.bitLength(), random);
// 私钥必须小于p
return privateKey.mod(p);
}
// 计算公钥
public static BigInteger calculatePublicKey(BigInteger privateKey) {
return g.modPow(privateKey, p);
}
// 计算共享密钥
public static BigInteger calculateSharedSecret(BigInteger privateKey, BigInteger otherPublicKey) {
return otherPublicKey.modPow(privateKey, p);
}
public static void main(String[] args) {
// Alice生成私钥和公钥
BigInteger alicePrivateKey = generatePrivateKey();
BigInteger alicePublicKey = calculatePublicKey(alicePrivateKey);
System.out.println("Alice's Private Key: " + alicePrivateKey);
System.out.println("Alice's Public Key: " + alicePublicKey);
// Bob生成私钥和公钥
BigInteger bobPrivateKey = generatePrivateKey();
BigInteger bobPublicKey = calculatePublicKey(bobPrivateKey);
System.out.println("Bob's Private Key: " + bobPrivateKey);
System.out.println("Bob's Public Key: " + bobPublicKey);
// Alice和Bob计算共享密钥
BigInteger aliceSharedSecret = calculateSharedSecret(alicePrivateKey, bobPublicKey);
BigInteger bobSharedSecret = calculateSharedSecret(bobPrivateKey, alicePublicKey);
// 验证共享密钥是否相同
System.out.println("Alice's Shared Secret: " + aliceSharedSecret);
System.out.println("Bob's Shared Secret: " + bobSharedSecret);
System.out.println("Shared Secrets Match: " + aliceSharedSecret.equals(bobSharedSecret));
}
}
这个示例演示了两个对等方(Alice和Bob)如何使用Diffie-Hellman算法协商共享密钥。每个对等方都生成一个私钥,并计算出对应的公钥。然后,它们交换公钥,并使用自己的私钥和对方的公钥计算出共享的密钥。最后,它们验证计算得到的共享密钥是否相同。
package com.artisan.securityalgjava.DiffieHellman;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import javax.crypto.KeyAgreement;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
public class DHExample {
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");
// 自己的PrivateKey
keyAgreement.init(this.privateKey);
// 对方的PublicKey
keyAgreement.doPhase(receivedPublicKey, true);
// 生成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));
}
}
输出
Name: Bob
Private key: 3081d202010030819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4020201800433023100d28250d0fe90e3b85afa61ada18204c97f3e4073618cfcdbeea3ca18336e42bda8d1143b976a34eac1954e94f1e26f76
Public key: 3081df30819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4020201800343000240172702a9a278c0fb05770ae3904e2002dbc3cd5fa1bb98263053c3ca871228139481cb619f3d2178ce591c7c1caf7fccb2092c3bf1fb00d2dd00f4b308c283d4
Secret key: f2b99ef3c77657dbe06e1c9b037c9e5ae0b44ee294239b2bee0166d4d6f9a05d0a9c00bf669e05562fe83d63049d23a7f6a4ac1ab3a0aae5e9169d0a4889141c
Name: Alice
Private key: 3081d202010030819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4020201800433023100a5e352efc6a8cbf155fd780fdfdac894327960cc025f71c3a17739d2fba898ab92fef6286656408265db80a4ab17d1cb
Public key: 3081df30819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca40202018003430002400e8f0bee5867c4964a71ff3e1ca0b777f05a38fd7bae6255c42af346f4464465e77390254ed6dc474451b0e9a6b7e1d9c15fc2058adf4bbe64622a4e726c353b
Secret key: f2b99ef3c77657dbe06e1c9b037c9e5ae0b44ee294239b2bee0166d4d6f9a05d0a9c00bf669e05562fe83d63049d23a7f6a4ac1ab3a0aae5e9169d0a4889141c
Diffie-Hellman(DH)算法是一种强大的密钥协商协议,但它也存在一些缺点:
综上所述,虽然Diffie-Hellman算法在密钥交换方面具有重要的优势,但在实际应用中,必须结合其他安全机制来解决身份认证和中间人攻击等问题,以确保通信的安全性和可靠性。