diffie-Hellman(DH)算法原理
Diffie-Hellman算法是Whitefield Diffie和Martin Hellman在1976年公布的一种秘钥交换算法,它是一种建立秘钥的方法,而不是加密方法,所以秘钥必须和其他一种加密算法结合使用。这种秘钥交换技术的目的在于使两个用户安全的交换一个秘钥一遍后面的报文加密。
Diffie-Hellman密钥交换算法的有效性依赖于计算离散对数的难度。
模型分析
1)由消息发送的一方构建密钥,这里由甲方构建密钥。
2)由构建密钥的一方向对方公布其公钥,这里由甲方向乙方发布公钥。
3)由消息接收的一方通过对方公钥构建自身密钥,这里由乙方使用甲方公钥构建乙方密钥。
4)由消息接收的一方向对方公布其公钥,这里由乙方向甲方公布公钥。
实例说明
1、有两个全局公开的参数,一个素数q和一个整数a,a是q的一个原根。
2、假设用户A和B希望交换一个密钥,
用户A选择一个作为私有密钥的随机数XA<q,并计算公开密钥YA=a^XA mod q。A对XA的值保密存放而使YA能被B公开获得。
类似地,用户B选择一个私有的随机数XB<q,并计算公开密钥YB=a^XB mod q。B对XB的值保密存放而使YB能被A公开获得。
3、用户A产生共享秘密密钥的计算方式是K1= (YB)^XA mod q。
同样,用户B产生共享秘密密钥的计算是k2 = (YA)^XB mod q。
这两个计算产生相同的结果:
k1= (YB)XA modq
= (a^XB modq)^XA mod q
= (aXB)XA modq =a^(XB*XA) mod q (根据取模运算规则得到)
= (aXA)XB modq
= (a^XA modq)^XB mod q
= (YA)^XB modq = k2
所以 (YB)XA modq = K =(YA)XB modq
因此相当于双方已经交换了一个相同的秘密密钥。
4、因为XA和XB是保密的,一个敌对方可以利用的参数只有q、a、YA和YB。因而敌对方被迫取离散对数来确定密钥。例如,要获取用户B的秘密密钥,敌对方必须先计算
XB = inda ,q(YB)
然后再使用用户B采用的同样方法计算其秘密密钥K。
Diffie-Hellman密钥交换算法的安全性依赖于这样一个事实:虽然计算以一个素数为模的指数相对容易,但计算离散对数却很困难。对于大的素数,计算出离散对数几乎是不可能的。
如果上面的理解有点费力,那就换一种思想
如果你拿到了一个由两种颜色混合而成的颜色,你能判断除哪两种颜色是什么吗?
就算你知晓了其中一种颜色,也无法判断出另一种颜色(别杠,或许你说一个个试很容易试出来,但我这里是想让你更容易理解DH算法,公开得p,a很容容易知道,但你去试?DH算法就是基于计算离散对数的难度。就是基于这个得就算难度出现的算法)
(这个图看完,应该了解DH算法大概是怎么操作的了吧)
一些参考的地址
上代码
(2)基于Diffie-Hellman的socket通信
之所以要花时间学习Diffie-Hellma这个算法,就是因为老师课上实现了基于Diffie-Hellman的socket通信。由于我太笨根本没听懂,T^T。课后的socket安全通信的实验也没做的很好
server端
import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;
public class DHDemoServer {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
//构建的本地密钥对
//实例化密钥生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
//初始化密钥生成器
kpg.initialize(512);
//生成密钥对
KeyPair keyPair = kpg.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
byte[] encoded = publicKey.getEncoded();
//创建套接字
ServerSocket serverSocket = new ServerSocket(8000);
Socket socket = serverSocket.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
byte[] spkb = new byte[dis.readInt()];
dis.readFully(spkb);//readFully适合用在socket通信中
//将公钥发给客户端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(encoded.length);
dos.write(encoded);
//转换公钥材料
X509EncodedKeySpec x509spc = new X509EncodedKeySpec(spkb);
//实例化密钥工厂
KeyFactory kf = KeyFactory.getInstance("DH");
PublicKey sereverPublicKey = kf.generatePublic(x509spc);
//开始使用客户端的私钥和服务端的公钥来实现密钥协商
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(clientPublicKey,true);
//从服务器端接受IV
byte[] iv = new byte[8];
Random random = new Random();
random.nextBytes(iv);
dos.write(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
//从DH中产生的共享密钥中产生DES密钥
byte[] sessionKeyBytes = ka.generateSecret();
ClientKeyFactory ckf = SecretKeyFactory.getInstance("DESede");
KeySpec keySpec = new DESedeKeySpec(sessionKeyBytes);
SecretKey sessionKey = ckf.generateSecret(keySpec);
//创建cipher对象
Cipher cipher = Cipher.getInstance("DESede/CFB8/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,sessionKey,spec);
byte[] buffer = new byte[dis.readInt()];
dis.readFully(buffer);
byte[] encoded1 = cipher.doFinal(buffer);
System.out.println(new String(encoded1));
dis.close();
dos.close();
socket.close();
serverSocket.close();
}
}
client端
import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
public class DHDemoClient {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
//构建的本地密钥对
//实例化密钥生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
//初始化密钥生成器
kpg.initialize(512);
//生成密钥对
KeyPair keyPair = kpg.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
byte[] encoded = publicKey.getEncoded();
//建立socket通信
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8000);
//将公钥发送给服务端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(encoded.length);
dos.write(encoded);
//接受服务端传来的公钥
DataInputStream dis = new DataInputStream(socket.getInputStream());
byte[] spkb = new byte[dis.readInt()];
dis.readFully(spkb);//readFully适合用在socket通信中
//转换公钥材料
X509EncodedKeySpec x509spc = new X509EncodedKeySpec(spkb);
//实例化密钥工厂
KeyFactory kf = KeyFactory.getInstance("DH");
PublicKey sereverPublicKey = kf.generatePublic(x509spc);
//开始使用客户端的私钥和服务端的公钥来实现密钥协商
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(sereverPublicKey,true);
//从服务器端接受IV
byte[] iv = new byte[8];
dis.readFully(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
//从DH中产生的共享密钥中产生DES密钥
byte[] sessionKeyBytes = ka.generateSecret();
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
KeySpec keySpec = new DESedeKeySpec(sessionKeyBytes);
SecretKey sessionKey = skf.generateSecret(keySpec);
//创建cipher对象
Cipher cipher = Cipher.getInstance("DESede/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE,sessionKey,spec);
//要传输的信息,当然你也可以传输文件,就改一下就好了
String text ="大菜比,好好学习,坚持,刻苦,努力,奋斗";
byte[] encoded1 = cipher.doFinal(text.getBytes());
dos.writeInt(encoded1.length);
dos.write(encoded1);
dos.close();
dis.close();
socket.close();
}
}
(最后最后小声bb,我很菜的,如果有问题可以指出来,谢谢!! ^_^)