Java 加解密技术系列之 DH

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

概念

DH,全称为“Diffie-Hellman”,他是一种确保共享KEY安全穿越不安全网络的方法,也就是常说的密钥一致协议。由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。

简单的说就是允许两名用户在公开媒体上交换信息以生成“一致”的、可以共享的密钥。也就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。

以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!

原理

  • 通信方A和通信方B约定一个初始数g,g是公开的,如g=5;
  • A生成一个随机数a,a是保密的,如a=6;
  • A计算g^a发送给B,g^a=5^6;
  • B生成一个随机数b,b是保密的,如b=15;
  • B计算g^b发送给A,g^b=5^15;
  • A接收到g^b后,再使用保密的a,计算(g^b)^a=g^(a*b)=5^(6*15) ;
  • B接收到g^a后,再使用保密的b,计算(g^a)^b=g^(a*b)=5^(6*15);
  • 这样通信方A和B得到一个相同的“密钥”g^(a*b)=5^(6*15)。

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

  • 通信方A和通信方B约定一个初始数g,如g=5,一个质数p,如p=23,g和p是公开的 ;
  • 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的生成算法也必需注意,应使结果尽可能随机,不能出现可预测的规律,否则会使破解变的容易。

代码实现

<span style="font-family:Comic Sans MS;font-size:12px;">package com.test.dh;

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));

    }
}</span><span style="font-family: 微软雅黑; font-size: 14px;">
</span>

结果

结束语

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

时间: 2024-12-13 12:25:35

Java 加解密技术系列之 DH的相关文章

10.Java 加解密技术系列之 DH

Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然,可能有很多人对这种加密算法并不是很熟悉,不过没关系,希望今天这篇文章能帮助你熟悉他. 原理 整个通信过程中g.g^a.g^b是公开的,但由于g.a.b都是整数,通过g和g^a得到a还是比较容易的,b也是如此,所以最终的“密钥”g^(a*b)还是可以被计算出来的.所以实际的过程还需要在基本原理上加入

Java 加解密技术系列文章

Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列之 3DES Java 加解密技术系列之 DES Java 加解密技术系列之 HMAC Java 加解密技术系列之 SHA Java 加解密技术系列之 MD5 Java 加解密技术系列之 BASE64

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

Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的保护.同时,由于水平的限制,打算这个系列就到此为止了,这篇文章就算是一个总结吧,回顾一下这几个月来都写了些什么. 背景 其 实,在开始写这个系列之前,我对于 Java 的加解密也并不是那么了解.之所以要写这些文章,还主要是由于工作的原因.记得几个月以前,当时项目要做一个数字证书,证书的生成.存储.传

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

Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项目太紧,具体的就不多说了,想听我吐槽的小伙伴, 可以私信给我(*^__^*) .上一篇文章,已经把对称加密的算法讲完了.从今天开始,要说说非对称加密了.因为,非对称加密真的是太重要了,我们的日常生活中,都离不开非对称加密. 概念 在说 RSA 之前,首先聊聊什么是非对称加密.在讲对称加密的时候,就曾

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

Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认知来说,并没有 DES.3DES.AES 那么流行,也不尽然,其实是我之前并没有这方面的需求,当然接触他的机会也就很少了,因此,可想而知,没听过显然在正常不过了. 概念 PBE,全称为“Password Base Encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用

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

Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES — — Triple DES. 背景 至于 3DES 为什么会出现呢?其实,这个不难想到.由于 DES 是一种非常简便的加密算法,但是密钥长度比较短,计算量比较小,相对来说,比较容易被破解.因此,在 DES 的基础上,使用三重数据加密算法,对数据进行加密,这样来说,破解的概率就小了很多. 概念 3D

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

Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比较常见的加解密算法.这篇文章,以及后面几篇,打算介绍几个对称加密算法,比如:DES.3DES(TripleDES).AES 等.那么,这篇文章主要是对 DES 大概讲一下. 背景 在 讨论 DES 之前,首先了解一下什么是对称加密算法吧.对于对称加密算法,他应用的时间比较早,技术相对来说比较成熟,在

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

java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原先的 DES 标准.现在来说,AES 的用途还是非常广泛的. 概念 AES, 全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准.AES 加密算法作为新一代的数据加密标准汇聚了强安

1.Java 加解密技术系列之 BASE64

Java 加解密技术系列之 BASE64 序号 背景 正文 总结 序 这段时间,工作中 用到了 Java 的加解密技术,本着学习的态度,打算从这篇文章开始,详细的研究一番 Java 在加解密技术上有什么与众不同,同时,也想为大家或者自己留下点什么,一块分享这其中的“精髓”.需要说明的是,这个系列可能要持续一段时间,因为,加解 密的相关技术太多太多了,要搞明白这些着实不是一件容易的事. 背景 说到这个加解密技 术,之前一直没有机会研究这个东西,这次公司里的项目需要加解密的支持,因此有机会能够好好研