java-信息安全(八)-迪菲-赫尔曼(DH)密钥交换
概述
信息安全基本概念:
- DH(Diffie–Hellman key exchange,迪菲-赫尔曼密钥交换)
DH
是一种安全协议,,一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一个组成部分。
这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥。然后可以用这个密钥进行加密和解密。但是注意,这个密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实际加密和解密消息。
Oakley算法是对Diffie-Hellman密钥交换算法的优化,它保留了后者的优点,同时克服了其弱点. Oakley算法具有五个重要特征: 它采用称为cookie程序的机制来对抗阻塞攻击. 它使得双方能够协商一个全局参数集合. 它使用了现时来保证抵抗重演攻击. 它能够交换Diffie-Hellman公开密钥. 它对Diffie-Hellman交换进行鉴别以对抗中间人的攻击.
流程分析
1.甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。

2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。

3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。

代码实现

package com.jd.order.util.encryption; import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
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 org.apache.commons.codec.binary.Base64; /**
* DH密码交换协议
*
* @author 木子旭
* @since 2017年3月17日上午9:18:14
* @version %I%,%G%
*/
public class DHCoder {
public static final String ALGORITHM = "DH"; /**
* 默认密钥字节数
*
* <pre>
* DH
* Default Keysize 1024
* Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
* </pre>
*/
private static final int KEY_SIZE = 1024; /**
* DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。
*/
public static final String SECRET_ALGORITHM = "DES";
private static final String PUBLIC_KEY = "DHPublicKey";
private static final String PRIVATE_KEY = "DHPrivateKey"; /**
* 初始化甲方密钥
*
* @return
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 甲方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); // 甲方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
} /**
* 初始化乙方密钥
*
* @param key
* 甲方公钥
* @return
* @throws Exception
*/
public static Map<String, Object> initKey(String key) throws Exception {
// 解析甲方公钥
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); // 由甲方公钥构建乙方密钥
DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams(); KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance(keyFactory.getAlgorithm());
keyPairGenerator.initialize(dhParamSpec); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 乙方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); // 乙方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey); return keyMap;
} /**
* 加密<br>
*
* @param data
* 待加密数据
* @param publicKey
* 甲方公钥
* @param privateKey
* 乙方私钥
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String publicKey,
String privateKey) throws Exception { // 生成本地密钥
SecretKey secretKey = getSecretKey(publicKey, privateKey); // 数据加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data);
} /**
* 解密<br>
*
* @param data
* 待解密数据
* @param publicKey
* 乙方公钥
* @param privateKey
* 乙方私钥
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String publicKey,
String privateKey) throws Exception { // 生成本地密钥
SecretKey secretKey = getSecretKey(publicKey, privateKey);
// 数据解密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data);
} /**
* 构建密钥
*
* @param publicKey
* 公钥
* @param privateKey
* 私钥
* @return
* @throws Exception
*/
private static SecretKey getSecretKey(String publicKey, String privateKey)
throws Exception {
// 初始化公钥
byte[] pubKeyBytes = decryptBASE64(publicKey); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); // 初始化私钥
byte[] priKeyBytes = decryptBASE64(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
Key priKey = keyFactory.generatePrivate(pkcs8KeySpec); KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
.getAlgorithm());
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true); // 生成本地密钥
SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM); return secretKey;
} /**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded());
} /**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded());
} public static byte[] decryptBASE64(String data) {
return Base64.decodeBase64(data);
} public static String encryptBASE64(byte[] data) {
return new String(Base64.encodeBase64(data));
}
}

测试代码

package com.jd.order.util.encryption;
import static org.junit.Assert.assertEquals;
import java.util.Map;
import org.junit.Test;
public class DHCoderTest {
    @Test
    public void test() throws Exception {
        // 生成甲方密钥对儿
        Map<String, Object> aKeyMap = DHCoder.initKey();
        String aPublicKey = DHCoder.getPublicKey(aKeyMap);
        String aPrivateKey = DHCoder.getPrivateKey(aKeyMap);
        System.err.println("甲方公钥:\r" + aPublicKey);
        System.err.println("甲方私钥:\r" + aPrivateKey);
        // 由甲方公钥产生本地密钥对儿
        Map<String, Object> bKeyMap = DHCoder.initKey(aPublicKey);
        String bPublicKey = DHCoder.getPublicKey(bKeyMap);
        String bPrivateKey = DHCoder.getPrivateKey(bKeyMap);
        System.err.println("乙方公钥:\r" + bPublicKey);
        System.err.println("乙方私钥:\r" + bPrivateKey);
        System.err.println("乙方构建加密,甲方解密 ");
        String aInput = "abc ";
        System.err.println("原文: " + aInput);
        // 乙方构建密钥消息,使用甲方公钥,乙方私钥构建密文
        byte[] aCode = DHCoder.encrypt(aInput.getBytes(), aPublicKey,
                bPrivateKey);
        // 甲方解密乙方加密消息,使用乙方公钥,甲方私钥解密
        byte[] aDecode = DHCoder.decrypt(aCode, bPublicKey, aPrivateKey);
        String aOutput = (new String(aDecode));
        System.err.println("解密: " + aOutput);
        assertEquals(aInput, aOutput);
        System.err.println(" ===============反过来加密解密================== ");
        System.err.println("甲方构建加密,乙方解密 ");
        String bInput = "def ";
        System.err.println("原文: " + bInput);
        // 甲方构建密钥消息,由乙方公钥,甲方私钥构建密文
        byte[] bCode = DHCoder.encrypt(bInput.getBytes(), bPublicKey,
                aPrivateKey);
        // 乙方解密甲方加密消息,使用甲方公钥,乙方私钥解密
        byte[] bDecode = DHCoder.decrypt(bCode, aPublicKey, bPrivateKey);
        String bOutput = (new String(bDecode));
        System.err.println("解密: " + bOutput);
        assertEquals(bInput, bOutput);
    }
}

结果输出

甲方公钥:
MIIBpjCCARsGCSqGSIb3DQEDATCCAQwCgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoCAgIAA4GEAAKBgCI1eq1qourLxvb5kEdUTM2sSuZcGpgUtGOuZdyH4iOzRwyMAD+Ae5TXastomBXdGxVyOVgNhOgj915rYsYIRMdzAxP/RADeTQ2scdcFRM6PzoTihrTLL5LDseZe6G67P9hfEuR+M1FKwuXlEi712LWblLD+404NNKPKRj5be/kO
甲方私钥:
MIIBZwIBADCCARsGCSqGSIb3DQEDATCCAQwCgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoCAgIABEMCQQCVmbxJKuqyTjgewwYYU29VwZEysIun5/lwIir5dV3b4MJ2m+i2FJ+wFY2dJPPH2s+tAFiK6QTVtlfdFcm+6P8E
乙方公钥:
MIIBpjCCARsGCSqGSIb3DQEDATCCAQwCgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoCAgIAA4GEAAKBgA5xV90r65kHFOCgKqE+6XCLx+Zxu+g4sg+A3bReyp+IgsIOQQhMXS1Lh3cCQMEMni1jqQ0c5hT3rBV0SIDaVD/57A62pevjdt1oKq4AQBabXNxXj9alENMozud1PVbEgq8+n89mt3s2QMOKpOCFMfjTIEi3GJRMa+MI7B28S1gS
乙方私钥:
MIIBZwIBADCCARsGCSqGSIb3DQEDATCCAQwCgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoCAgIABEMCQQCp+LEg3WYOYkFP8Cyl05O5y69ilJ99KO8NpdIjnqoD0uK9gl6GzWr2HVtXHs6KDRSJQFcKaOvldhfp9rVPQdiA
乙方构建加密,甲方解密
原文: abc
解密: abc
===============反过来加密解密==================
甲方构建加密,乙方解密
原文: def
解密: def

参考文章
百度百科,维基百科
http://snowolf.iteye.com/blog/382422,等
java-信息安全(八)-迪菲-赫尔曼(DH)密钥交换的更多相关文章
- java-信息安全(八)-迪菲-赫尔曼(DH)密钥交换【不推荐,推荐Oakley】
		概述 信息安全基本概念: DH(Diffie–Hellman key exchange,迪菲-赫尔曼密钥交换) DH 是一种安全协议,,一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一 ... 
- RSA算法二:迪菲赫尔曼公式变形
- 《Python3 标准库》作者  道格.赫尔曼
		Doug Hellmann目前是Racemi公司的一位高级开发人员,也是Python Software Foundation的信息交流主管.从1.4版开始他就一直在做Python编程,曾在大量UNIX ... 
- 赫夫曼树JAVA实现及分析
		一,介绍 1)构造赫夫曼树的算法是一个贪心算法,贪心的地方在于:总是选取当前频率(权值)最低的两个结点来进行合并,构造新结点. 2)使用最小堆来选取频率最小的节点,有助于提高算法效率,因为要选频率最低 ... 
- Java数据结构和算法(四)赫夫曼树
		Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ... 
- javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题
		赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支 ... 
- 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)
		参考资料 <算法(java)> — — Robert Sedgewick, Kevin Wayne <数据结构> ... 
- Android版数据结构与算法(七):赫夫曼树
		版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ... 
- 赫夫曼解码(day17)
		思路: 传入map(字节与对应字节出现的次数)和最后生成的要传送的字节.将他们先转换成对应的二进制字节,再转换成原来的字符串. 代码: 12345678910111213141516171819202 ... 
随机推荐
- 洛谷.3224.[HNOI2012]永无乡(Splay启发式合并)
			题目链接 查找排名为k的数用平衡树 合并时用启发式合并,把size小的树上的所有节点插入到size大的树中,每个节点最多需要O(logn)时间 并查集维护连通关系即可 O(nlogn*insert t ... 
- 洛谷.3391.文艺平衡树(fhq Traep)
			题目链接 //注意反转时先分裂r,因为l,r是针对整棵树的排名 #include<cstdio> #include<cctype> #include<algorithm& ... 
- [CC-PERMUTE]Just Some Permutations 3
			[CC-PERMUTE]Just Some Permutations 3 题目大意: \(T(T\le10^5)\)次询问,每次询问有多少长度为\(n(n\le10^6)\)的排列,满足任意相邻两个数 ... 
- Android getWidth和getMeasuredWidth
			1. 在一个类初始化时,即在构造函数当中我们是得不到View的实际大小的.感兴趣的朋友可以试一下,getWidth()和getMeasuredWidth()得到的结果都是0.但是我们可以从onDraw ... 
- 47. 全排列 II
			47. 全排列 II 题意 给定一个可包含重复数字的序列,返回所有不重复的全排列. 示例: 输入: [1,1,2]输出:[ [1,1,2], [1,2,1], [2,1,1]] 解题思路 去重的全排列 ... 
- innodb文件
			参数文件 日志文件 socket文件 pid文件 mysql表结构文件 存储引擎文件 1. 错误日志 启用错误日志方法 /etc/init.d/mysql启动文件中 /usr/bin/mysqld_s ... 
- RxJava2学习笔记(3)
			接上回继续,今天来学习下zip(打包)操作 一.zip操作 @Test public void zipTest() { Observable.zip(Observable.create(emitter ... 
- 2016年Godaddy最新域名转出教程
			2016年Godaddy最新域名转出教程 http://godaddy.idcspy.com/godaddy-newest-domain-zhuanchu HostEase注册的域名转入和转出 htt ... 
- 在AngularJS中使用谷歌地图把当前位置显示出来
			如何使用谷歌地图把当前位置显示出来呢? --在html5中,为我们提供了navigator.geolocation.getCurrentPosition(f1, f2)函数,f1是定位成功调用的函数, ... 
- spring-data-jpa中findOne与getOne的区别  getOne没数据 findOne有数据
			项目中用到了spring-data-jpa,今天在写一个update方法的时候报了个空指针,看了看是因为一个对象中的关联没有取出来,我用的是getOne取得这个对象,加断点看以一下这个对象是个hibe ... 
