Java 加解密技术系列之 DH

  • 概念
  • 原理
  • 代码实现
  • 结果
  • 结束语

上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH。当然,可能有很多人对这种加密算法并不是很熟悉,不过没关系,希望今天这篇文章能帮助你熟悉他。

原理

  • 整个通信过程中g、g^a、g^b是公开的,但由于g、a、b都是整数,通过g和g^a得到a还是比较容易的,b也是如此,所以最终的“密钥”g^(a*b)还是可以被计算出来的。所以实际的过程还需要在基本原理上加入新的计算——模运算。

    A生成一个随机数a,a是保密的,如a=6 ;

  • A计算g^a%p发送给B,g^a%p=5^6%23=8 ;
  • B生成一个随机数b,b是保密的,如b=15 ;
  • B计算g^b%p发送给A,g^b%p=5^15%23=19 ;
  • A接收到g^b%p后,再使用保密的a,计算(g^b%p)^a%p=19^6%23=2 ;
  • B接收到g^a%p后,再使用保密的b,计算(g^a%p)^b%p=8^15%23=2 ;
  • 这样通信方A和B得到一个相同的密钥:2。
(g^b%p)^a%p=(g^a%p)^b%p 证明:
如果a=2:

    (g^b%p)^a%p=(g^b%p)^2%p=(g^b-n*p)^2%p=(g^(2*b)-2*g^b*n*p+(n*p)^2)%p=g^(2*b)%p ;
    可以看出(g^b-n*p)^2展开后除g^(2*b)外,其它都是p的倍数,所以整个算式的结果是g^(2*b)%p ;

  • 同理对(g^b-n*p)^a展开后除g^(a*b)外,其它都是p的倍数,所以整个算式的结果是g^(a*b)%p ;
  • 同样可以得出(g^a%p)^b%p=g^(a*b)%p ;
  • 所以(g^b%p)^a%p=(g^a%p)^b%p 。
整个通信过程中g、p、g^a%p、g^b%p是公开的,这时通过g、p、g^a%p得到a比较难,同样通过g、p、g^b%p得到b比较难,所以最终的密钥是比较安全的。
以g=5、p=23、g^a%p=8计算a为例,a=log(5, (8+23*n)),这个只能将n的可能值逐个带入公式试验才能得到a的值。如果a、p是比较大的数那么计算更加困难。
需要注意的是,为了防止应用优化算法计算上述问题,质数p不是随便选择的,需要符合一定的条件。随机数a、b的生成算法也必需注意,应使结果尽可能随机,不能出现可预测的规律,否则会使破解变的容易。

代码实现

import com.google.common.collect.Maps;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder; import javax.crypto.*;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map; /**
* Created by xiang.li on 2015/3/4.
* DH 加解密工具类
*/
public class DH {
/**
* 定义加密方式
*/
private static final String KEY_DH = "DH";
/**
* 默认密钥字节数
*/
private static final int KEY_SIZE = 1024;
/**
* DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法
*/
private static final String KEY_DH_DES = "DES";
private static final String KEY_DH_PUBLICKEY = "DHPublicKey";
private static final String KEY_DH_PRIVATEKEY = "DHPrivateKey"; /**
* 初始化甲方密钥
* @return
*/
public static Map<String, Object> init() {
Map<String, Object> map = null;
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_DH);
generator.initialize(KEY_SIZE);
KeyPair keyPair = generator.generateKeyPair();
// 甲方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 甲方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
map = Maps.newHashMap();
map.put(KEY_DH_PUBLICKEY, publicKey);
map.put(KEY_DH_PRIVATEKEY, privateKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return map;
} /**
* 初始化乙方密钥
* @param key 甲方密钥
* @return
*/
public static Map<String, Object> init(String key) {
Map<String, Object> map = null;
try {
// 解析甲方密钥
byte[] bytes = decryptBase64(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance(KEY_DH);
PublicKey publicKey = factory.generatePublic(keySpec); // 由甲方公钥构建乙方密钥
DHParameterSpec spec = ((DHPublicKey) publicKey).getParams();
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_DH);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
// 乙方公钥
DHPublicKey dhPublicKey = (DHPublicKey) keyPair.getPublic();
// 乙方私钥
DHPrivateKey dhPrivateKey = (DHPrivateKey) keyPair.getPrivate();
map = Maps.newHashMap();
map.put(KEY_DH_PUBLICKEY, dhPublicKey);
map.put(KEY_DH_PRIVATEKEY, dhPrivateKey);
} catch (Exception e) {
e.printStackTrace();
}
return map;
} /**
* DH 加密
* @param data 带加密数据
* @param publicKey 甲方公钥
* @param privateKey 乙方私钥
* @return
*/
public static byte[] encryptDH(byte[] data, String publicKey, String privateKey) {
byte[] bytes = null;
try {
// 生成本地密钥
SecretKey secretKey = getSecretKey(publicKey, privateKey);
// 数据加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
bytes = cipher.doFinal(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return bytes;
} /**
* DH 解密
* @param data 待解密数据
* @param publicKey 乙方公钥
* @param privateKey 甲方私钥
* @return
*/
public static byte[] decryptDH(byte[] data, String publicKey, String privateKey) {
byte[] bytes = null;
try {
// 生成本地密钥
SecretKey secretKey = getSecretKey(publicKey, privateKey);
// 数据解密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
bytes = cipher.doFinal(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return bytes;
} /**
* 取得私钥
* @param map
* @return
*/
public static String getPrivateKey(Map<String, Object> map) {
String str = "";
try {
Key key = (Key) map.get(KEY_DH_PRIVATEKEY);
str = encryptBase64(key.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
return str;
} /**
* 取得公钥
* @param map
* @return
*/
public static String getPublicKey(Map<String, Object> map) {
String str = "";
try {
Key key = (Key) map.get(KEY_DH_PUBLICKEY);
str = encryptBase64(key.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
return str;
} /**
* 构建本地密钥
* @param publicKey 公钥
* @param privateKey 私钥
* @return
*/
private static SecretKey getSecretKey(String publicKey, String privateKey) {
SecretKey secretKey = null;
try {
// 初始化公钥
byte[] publicBytes = decryptBase64(publicKey);
KeyFactory factory = KeyFactory.getInstance(KEY_DH);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
PublicKey localPublicKey = factory.generatePublic(keySpec); // 初始化私钥
byte[] privateBytes = decryptBase64(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateBytes);
PrivateKey localPrivateKey = factory.generatePrivate(spec); KeyAgreement agreement = KeyAgreement.getInstance(factory.getAlgorithm());
agreement.init(localPrivateKey);
agreement.doPhase(localPublicKey, true); // 生成本地密钥
secretKey = agreement.generateSecret(KEY_DH_DES);
} catch (Exception e) {
e.printStackTrace();
}
return secretKey;
} /**
* BASE64 解密
* @param key 需要解密的字符串
* @return 字节数组
* @throws Exception
*/
public static byte[] decryptBase64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
} /**
* BASE64 加密
* @param key 需要加密的字节数组
* @return 字符串
* @throws Exception
*/
public static String encryptBase64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
} /**
* 测试方法
* @param args
*/
public static void main(String[] args) {
// 生成甲方密钥对
Map<String, Object> mapA = init();
String publicKeyA = getPublicKey(mapA);
String privateKeyA = getPrivateKey(mapA);
System.out.println("甲方公钥:\n" + publicKeyA);
System.out.println("甲方私钥:\n" + privateKeyA); // 由甲方公钥产生本地密钥对
Map<String, Object> mapB = init(publicKeyA);
String publicKeyB = getPublicKey(mapB);
String privateKeyB = getPrivateKey(mapB);
System.out.println("乙方公钥:\n" + publicKeyB);
System.out.println("乙方私钥:\n" + privateKeyB); String word = "abc";
System.out.println("原文: " + word); // 由甲方公钥,乙方私钥构建密文
byte[] encWord = encryptDH(word.getBytes(), publicKeyA, privateKeyB); // 由乙方公钥,甲方私钥解密
byte[] decWord = decryptDH(encWord, publicKeyB, privateKeyA);
System.out.println("解密: " + new String(decWord)); }
}

结果

结束语

这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥。然后可以用这个密钥进行加密和解密。但是注意,这个密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实际加密和解密消息。

10.Java 加解密技术系列之 DH的更多相关文章

  1. Java 加解密技术系列文章

    Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列 ...

  2. 11.Java 加解密技术系列之 总结

    Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的 ...

  3. 9.Java 加解密技术系列之 RSA

    Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项 ...

  4. 5.Java 加解密技术系列之 DES

    Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比 ...

  5. 2.Java 加解密技术系列之 MD5

    Java 加解密技术系列之 MD5 序 背景 正文 结束语 序 上一篇文章中,介绍了最基础的编码方式 — — BASE64,也简单的提了一下编码的原理.这篇文章继续加解密的系列,当然也是介绍比较基础的 ...

  6. 8.Java 加解密技术系列之 PBE

    Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认 ...

  7. 7.java 加解密技术系列之 AES

    java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原 ...

  8. 6. Java 加解密技术系列之 3DES

    Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES ...

  9. 4.Java 加解密技术系列之 HMAC

    Java 加解密技术系列之 HMAC 序 背景 正文 代码 结束语 序 上一篇文章中简单的介绍了第二种单向加密算法 — —SHA,同时也给出了 SHA-1 的 Java 代码.有这方面需求的童鞋可以去 ...

随机推荐

  1. sublime Text3+emmet(快速开发)

    sublime软件使用Emmet插件快速编写CSS样式                    基本的CSS样式编写时,很多样式只需输入首字母即可得到不带属性值的CSS样式,像上面说到的margin.而 ...

  2. Mybatis基础学习(一)—初识MyBatis

    一.MyBatis是什么?      MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google co ...

  3. 对quartz定时任务的初步认识

    已经好久没有写技术博文了,今天就谈一谈我前两天自学的quartz定时任务吧,我对quartz定时任务的理解,就是可以设定一个时间,然后呢,在这个时间到的时候,去执行业务逻辑,这是我的简单理解,接下来看 ...

  4. Java设计模式:工厂模式

    问题提出 Java的工厂模式与现实生活中的工厂的模型是很相似的.工厂是用来做什么?当然是用来生成产品.因此在Java的工厂模式的关键点就是如何描述好产品和工厂这2个角色之间的关系. 下面来仔细描述一下 ...

  5. HYML / CSS和Javascript 部分

    1  CSS实现垂直水平居中 HTML结构: <div class="wrapper"> <div class="content">&l ...

  6. Linux学习(一)

    Linux系统 1.组成部分 1.1内核负责的功能 1.1.1:系统内存管理 内存管理即管理物理内存和虚拟内存 (通过硬盘实现的,即swap space),长时间为被访问的内存块会被放到虚拟内存中,当 ...

  7. Block Token 原理分析

    介绍 文件权限检查由NameNode执行,而不是DataNode执行. 默认情况下,任何客户端都可以访问只有其块ID的任何块. 为了解决这个问题,Hadoop引入了块访问令牌的概念. 块访问令牌由Na ...

  8. [第一阶段] Python学习

    首先声明一下,我这个学习计划是关于学习Python的. 先说一下起因:我自己接触Python算是很久了,目前仍没学会,很失败,很惭愧.所以这次一方面简单分析一下自学会碰到的问题:另一方便,我想到了一种 ...

  9. webpack学习(三)之web-dev-server不能自动刷新问题

    使用webpack-dev-server中遇到不能浏览器无法自动刷新的问题:寻找多方答案后明白了一些: 下面有一些需要注意的点: 1.webpack-dev-server并不能读取你的webpack. ...

  10. 七牛整合 ueditor (拦住那头牛,七牛又如何)

    最近遇到个项目,要求所有图片都必须整合到七牛上,看了把你谈文档踩在前辈们的基础上终于把他完成了,恰巧本屌丝最近刚好有时间,本着天下屌丝是一家的原则,和小朋友们一同学习 闲话少说入正题. 第一 :下载编 ...