1.简述
1976年,W.Diffie和M.Hellman在发表的论文中提出了公钥加密算法思想,但当时并没有给出具体的实施方案,原因在于没有找到单向函数(也就是消息摘要算法),但在该论文中给出了通信双方通过信息交换协商密钥的算法,即Diffie-Hellman密钥交换算法(简称为DH算法)。该算法的目的在于让消息的收发双方可以在安全的条件下交换密钥,以备后续加密/解密使用。因此,DH算法是第一个密钥协商算法,但仅能用于密钥分配,不能用于加密或者解密消息。
DH密钥交换算法的安全性基于有限域上的离散对数难题。基于这种安全性,通过DH算法进行密钥分配,使得消息的收发双方可以安全地交换一个密钥,再通过这个密钥对数据进行加密和解密处理。
2.模型分析
我们以消息传递模型为例,甲方作为发送者,乙方作为接受者,分述甲乙双方如何构建密钥、交互密钥和加密数据。
首先,甲乙双方需要在收发消息前构建自己的密钥对,如图1所示。
甲乙双方构建密钥需要经过以下几个步骤:
1)由消息发送的一方构建密钥,这里由甲方构建密钥。
2)由构建密钥的一方向对方公布其公钥,这里由甲方向乙方发布公钥。
3)由消息接收的一方通过对方公钥构建自身密钥,这里由乙方使用甲方公钥构建乙方密钥。
4)由消息接收的一方向对方公布其公钥,这里由乙方向甲方公布公钥。
这里要注意的是,乙方构建自己密钥对的时候需要使用甲方公钥作为参数这是很关键的一点,如果缺少了这一环节则无法确保甲乙双方获得同一个密钥,消息加密更无从谈起。
其次,假设甲乙双方事先约定好了用于数据加密的对称加密算法(如AES算法),并构建本地密钥(即对称加密算法中的密钥),如图2所示。
甲方需要使用自己的私钥和乙方的公钥才能构建自己的本地密钥,乙方需要使用自己的私钥和甲方的公钥才能构建自己的本地密钥。
虽然甲乙双方使用了不同的密钥来构建本地密钥,但是甲乙两方得到的密钥其实是一致的,后面的demo可以证明,也正是基于此,甲乙双方才能顺利地进行加密消息的传送。
最后,甲乙双方构建了本地密钥后,可按照基于对称加密算法的消息传递模型完成消息传递。如图4所示。
作为对称加密体制向非对称加密体制的一种过渡,DH算法仅仅比一般的对称加密算法多了密钥对的构建和本地密钥的构建这两项操作,而真正的数据加密/解密操作仍由对称加密算法完成。
3.实现
1)DH算法实现(DHCoder.java)
package dh; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.interfaces.DHPrivateKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class DHCoder { /** * 非对称加密密钥算法 */ private static final String KEY_ALGORITHM = "DH"; /** * 本地密钥算法,即对称加密密钥算法 * 可选DES、DESede或者AES */ private static final String SELECT_ALGORITHM = "AES"; /** * 密钥长度 */ private static final int KEY_SIZE = 512; //公钥 private static final String PUBLIC_KEY = "DHPublicKey"; //私钥 private static final String PRIVATE_KEY = "DHPrivateKey"; /** * 初始化甲方密钥 * @return Map 甲方密钥Map * @throws Exception */ public static MapinitKey() throws Exception{ //实例化密钥对生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); //初始化密钥对生成器 keyPairGenerator.initialize(KEY_SIZE); //生成密钥对 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //甲方公钥 DHPublicKey publicKey = (DHPublicKey)keyPair.getPublic(); //甲方私钥 DHPrivateKey privateKey = (DHPrivateKey)keyPair.getPrivate(); //将密钥对存储在Map中 Map keyMap = new HashMap (2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 初始化乙方密钥 * @param key 甲方公钥 * @return Map 乙方密钥Map * @throws Exception */ public static Map initKey(byte[] key) throws Exception{ //解析甲方公钥 //转换公钥材料 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //由甲方公钥构建乙方密钥 DHParameterSpec dhParameterSpec = ((DHPublicKey)pubKey).getParams(); //实例化密钥对生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); //初始化密钥对生成器 keyPairGenerator.initialize(KEY_SIZE); //产生密钥对 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //乙方公钥 DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); //乙方私约 DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); //将密钥对存储在Map中 Map keyMap = new HashMap (2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 加密 * @param data 待加密数据 * @param key 密钥 * @return byte[] 加密数据 * @throws Exception */ public static byte[] encrypt(byte[] data, byte[] key) throws Exception{ //生成本地密钥 SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM); //数据加密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 解密 * @param data 待解密数据 * @param key 密钥 * @return byte[] 解密数据 * @throws Exception */ public static byte[] decrypt(byte[] data, byte[] key) throws Exception{ //生成本地密钥 SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM); //数据揭秘 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 构建密钥 * @param publicKey 公钥 * @param privateKey 私钥 * @return byte[] 本地密钥 * @throws Exception */ public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception{ //实例化密钥工厂 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //初始化公钥 //密钥材料转换 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey); //产生公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); //初始化私钥 //密钥材料转换 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey); //产生私钥 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); //实例化 KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm()); //初始化 keyAgree.init(priKey); keyAgree.doPhase(pubKey, true); //生成本地密钥 SecretKey secretKey = keyAgree.generateSecret(SELECT_ALGORITHM); return secretKey.getEncoded(); } /** * 取得私钥 * @param keyMap 密钥Map * @return byte[] 私钥 * @throws Exception */ public static byte[] getPrivateKey(Map keyMap) throws Exception{ Key key = (Key) keyMap.get(PRIVATE_KEY); return key.getEncoded(); } /** * 取得公钥 * @param keyMap 密钥Map * @return byte[] 公钥 * @throws Exception */ public static byte[] getPublicKey(Map keyMap) throws Exception{ Key key = (Key) keyMap.get(PUBLIC_KEY); return key.getEncoded(); } }
2)DH算法测试(DHTest.java)
package dh; import java.util.Map; import org.apache.commons.codec.binary.Base64; public class DHTest { //甲方公钥 private static byte[] publicKey1; //甲方私钥 private static byte[] privateKey1; //甲方本地密钥 private static byte[] key1; //乙方公钥 private static byte[] publicKey2; //乙方私钥 private static byte[] privateKey2; //乙方本地密钥 private static byte[] key2; /** * 初始化密钥 * @throws Exception */ public static final void initKey() throws Exception{ //生成甲方密钥对 MapkeyMap1 = DHCoder.initKey(); publicKey1 = DHCoder.getPublicKey(keyMap1); privateKey1 = DHCoder.getPrivateKey(keyMap1); System.out.println("甲方公钥:\n" + Base64.encodeBase64String(publicKey1)); System.out.println("甲方私钥:\n" + Base64.encodeBase64String(privateKey1)); //由甲方公钥产生本地密钥对 Map keyMap2 = DHCoder.initKey(publicKey1); publicKey2 = DHCoder.getPublicKey(keyMap2); privateKey2 = DHCoder.getPrivateKey(keyMap2); System.out.println("乙方公钥:\n" + Base64.encodeBase64String(publicKey2)); System.out.println("乙方私钥:\n" + Base64.encodeBase64String(privateKey2)); key1 = DHCoder.getSecretKey(publicKey2, privateKey1); System.out.println("甲方本地密钥:\n" + Base64.encodeBase64String(key1)); key2 = DHCoder.getSecretKey(publicKey1, privateKey2); System.out.println("乙方本地密钥:\n" + Base64.encodeBase64String(key2)); } /** * 主方法 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub initKey(); System.out.println(); System.out.println("===甲方向乙方发送加密数据==="); String input1 = "求知若饥,虚心若愚。"; System.out.println("原文:\n" + input1); System.out.println("---使用甲方本地密钥对数据进行加密---"); //使用甲方本地密钥对数据加密 byte[] encode1 = DHCoder.encrypt(input1.getBytes(), key1); System.out.println("加密:\n" + Base64.encodeBase64String(encode1)); System.out.println("---使用乙方本地密钥对数据库进行解密---"); //使用乙方本地密钥对数据进行解密 byte[] decode1 = DHCoder.decrypt(encode1, key2); String output1 = new String(decode1); System.out.println("解密:\n" + output1); System.out.println("/~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~..~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~/"); initKey(); System.out.println("===乙方向甲方发送加密数据==="); String input2 = "好好学习,天天向上。"; System.out.println("原文:\n" + input2); System.out.println("---使用乙方本地密钥对数据进行加密---"); //使用乙方本地密钥对数据进行加密 byte[] encode2 = DHCoder.encrypt(input2.getBytes(), key2); System.out.println("加密:\n" + Base64.encodeBase64String(encode2)); System.out.println("---使用甲方本地密钥对数据进行解密---"); //使用甲方本地密钥对数据进行解密 byte[] decode2 = DHCoder.decrypt(encode2, key1); String output2 = new String(decode2); System.out.println("解密:\n" + output2); } }
4.测试结果