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. 关于JavaScript的模块化

    为什么需要模块化 最近在学习网易微专业的<前端系统架构>课程,里面讲到了关于JavaScript的模块化问题.具体指的是当随着Web系统不断强大起来,需要在客户端进行的操作就多了起来(比如 ...

  2. Windows入门基础:1.关于CreateWindow()函数使用中遇到的问题

    我在实现显示窗口的程序中,遇到一个问题:首先程序没有任何语法错误,编译能够通过,但是就是不能弹出窗口. 后来在MSDN中查询CreateWindow()函数,发现了下面这句话: "If lp ...

  3. MFC使用SQLite 学习系列 一: SQLITE_MISUSE错误

    一 为什么要选择SQLite 由于使用文本文件来记录测试数据,速度越来越慢的问题,经过园友推荐,使用了SQLite来进行数据的存储,再次感谢园友@LightSmaile. 关于这个问题,可以参考一下上 ...

  4. SQL入门之条件表达式

    where子句和having子句主要是用来筛选符合条件的元组,其后紧跟的即为条件表达式. 0.and, or条件的连接 用法和一般编程语言一样,主要用于条件的拼接.and两边都为真,则结果为真.or两 ...

  5. 图解CSS选择器之nth家族

    1 nth-last-of-type  顾名思义从最后开始的元素开始选取可接受 数字 关键词 公式  比如4n+0就是每隔四个 odd even关键词表示奇偶数 .classify-item:nth- ...

  6. jquery如何设置html众标签中的值

    $("img").attr("src",some_url);//jquery设置img标签中的src值 $("#user").val(&qu ...

  7. 浅谈MVC页面之间参数传递

    关于MVC页面之间的传值,有多种方式,下面,我们就Html.RenderAction 方式 和 Html.RenderPartial 方式 来给大家分享一下有什么不同. 一.Html.RenderAc ...

  8. 用Web抓包分析工具Livepool 实现本地替换开发

    这是官方的介绍: LivePool 是一个基于 NodeJS,类似 Fiddler 支持抓包和本地替换的 Web 开发调试工具,是 Tencent AlloyTeam 在开发实践过程总结出的一套的便捷 ...

  9. 通过bootloader向内核传输启动参数

    作者:Younger Liu,本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可. Linux提供了一种通过bootloader向其传输启动参数的功能,内核开发者 ...

  10. apache用户认证,ssl双向认证配置

    安装环境: OS:contos 6.4 httpd:httpd-2.2.15-59.el6.centos.i686.rpm openssl:openssl-1.0.1e-57.el6.i686.rpm ...