【Java小工匠聊密码学】--非对称加密--DH密钥交换算法

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 1、DH密钥交换概述Diffie-Hellman由Whitfield Diffie和Martin Hellman在1976年公布的一种密钥一致性算法。Diffie-Hellman是一种建立密钥的方法,而不是加密方法。

1、DH密钥交换概述

Diffie-Hellman由Whitfield Diffie和Martin Hellman在1976年公布的一种密钥一致性算法。Diffie-Hellman是一种建立密钥的方法,而不是加密方法。然而,它所产生的密钥可用于加密、进一步的密钥管理或任何其它的加密方式。Diffie-Hellman密钥交换算法及其优化首次发表的公开密钥算法出现在Diffie和Hellman的论文中,这篇影响深远的论文奠定了公开密钥密码编码学。

2、DH密钥交换算法原理

2.1、使用颜色形象描述

设想这样一个场景,Alice(A)和Bob(B),他们想在不见面的情况下秘密约定出一种颜色,但他们互相沟通的信息都会被公开,应该怎么办呢?


DH密钥交换算法

秘密在于,颜色混合是一种“不可逆”的操作,当双方交换颜色时,尽管我们知道他们交换的颜色都是由一份黄色和另一份其他颜色混合得到的,但我们还是无法或者很难得到他们的私密颜色。

2.2、数学算法

2.2.1 算法背景

乘方得逆运算称为对数运算,比如已知 7^x = 49 那么可知 x=log(7,49)=2。 对数运算非常容易,即使在数字很大的时候是,但如果是下面的情况 7^xmod13=8 。 求X的过程称为“离散对数”,就不那么容易了,在数字很大时几乎是一个不可能的运算,而DH秘钥交换就是利用了这种离散对数计算非常困难的特性来设计的。

2.2.2 取模运算规律

公式里的mod是取模运算,取模运算有几条基本的定律如下:
(a+b) mod P =(a mod P+b mod P) mod P
(a∗b) mod P = (a mod P∗b mod P) mod P
(a^b) mod P = (a mod P)^b) mod P

2.2.3 密钥交换流程

根据上面的公式,可以推导出一个非常重要的公式。
(G^(a∗b)) mod P=(G^a mod P)^b mod P=(G^b mod P)^a mod P
根据这个公式,我们可以向上面交换颜色那样设计出一个秘密交换数字的流程出来。


交换流程

最终两个人得到的秘密数字都是g^(ab) mod p,而窃听者仅从p,g,A,B四个公开信息,是无法得到这个秘密数字的!

2.2.4举例说明

第1步.爱丽丝与鲍伯协定使用p=23以及g=5.
第2步.爱丽丝选择一个秘密整数a=6, 计算A = g^a mod p并发送给鲍伯。 A = 5^6 mod 23 = 8.
第3步.鲍伯选择一个秘密整数b=15, 计算B = g^b mod p并发送给爱丽丝。
B = 5^15 mod 23 = 19.
第4步.爱丽丝计算
s = B a mod p
19^6 mod 23 = 2.
第5步.鲍伯计算s = A b mod p
8^15 mod 23 = 2.

3、DH密钥交换算用途

可以用作对称加密算法中,双方约定的加密准则的交换(对方的公钥和自己的私钥计算的到秘密整数,可以作为双方的加密准则)。交换双方可以在不共享任何秘密的情况下协商出一个密钥。

4、中间人攻击

由于密钥交换本身并没有提供通讯双方的身份验证服务,因此它很容易受到中间人攻击。 一个中间人“丙”在信道的中央进行两次迪菲-赫尔曼密钥交换,一次和甲,另一次和乙,就能够成功的向甲假装自己是乙,反之亦然。而攻击者可以解密(读取和存储)任何一个人的信息并重新加密信息,然后传递给另一个人。因此通常都需要一个能够验证通讯双方身份的机制来防止这类攻击。

5、算法实现

5.1、JDK算法实现

package lzf.cipher;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;

import org.apache.commons.codec.binary.Hex;

/**
 * @author java小工匠
 */
public class DHUtils {

    // 甲方初始化密钥对
    public static KeyPair initKey() {
        try {
            // 实例化密钥对生成器
            KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
            // 初始化密钥对生成器参数 默认1024 512-1024之间64的倍数
            generator.initialize(1024);
            // 产生密钥对
            KeyPair keyPair = generator.genKeyPair();
            return keyPair;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    // 乙方初始化密钥生成对
    public static KeyPair initKey(byte[] key) {
        try {
            // 公钥从字节数组转换为PublicKey
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
            // 实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance("DH");
            // 还原甲方的公钥
            DHPublicKey dhPublicKey = (DHPublicKey) keyFactory.generatePublic(keySpec);
            // 剖析甲方公钥,得到其参数
            DHParameterSpec dhParameterSpec = dhPublicKey.getParams();
            // 实例化密钥对生成器
            KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
            // 使用得到的参数,初始化密钥生成器
            generator.initialize(dhParameterSpec);
            return generator.generateKeyPair();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    // 获取公钥
    public static byte[] getPublicKey(KeyPair keyPair) {
        byte[] bytes = keyPair.getPublic().getEncoded();
        return bytes;
    }

    // 获取公钥
    public static String getPublicKeyStr(KeyPair keyPair) {
        byte[] bytes = keyPair.getPublic().getEncoded();
        return Hex.encodeHexString(bytes);
    }

    // 获取私钥
    public static byte[] getPrivateKey(KeyPair keyPair) {
        byte[] bytes = keyPair.getPrivate().getEncoded();
        return bytes;
    }

    // 获取公钥
    public static String getPrivateKeyStr(KeyPair keyPair) {
        byte[] bytes = keyPair.getPrivate().getEncoded();
        return Hex.encodeHexString(bytes);
    }

    // 生成本地密钥
    public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) {
        try {
            // 实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance("DH");
            // 将公钥从字节数组转换为PublicKey
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            // 将私钥从字节数组转换为PrivateKey
            PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKey);
            PrivateKey priKey = keyFactory.generatePrivate(privateSpec);
            // 先实例化KeyAgreement
            KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
            // 用自己的私钥初始化keyAgreement
            keyAgreement.init(priKey);
            // 结合对方的公钥进行运算
            keyAgreement.doPhase(pubKey, true);
            // 开始生成本地密钥SecretKey 密钥算法为对称密码算法
            SecretKey key = keyAgreement.generateSecret("AES");
            return key.getEncoded();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public static void main(String[] args) {
        KeyPair keyPair1 = initKey();
        byte[] publicKey1 = getPublicKey(keyPair1);
        String publicKeyStr1 = Hex.encodeHexString(publicKey1);
        byte[] privateKey1 = getPrivateKey(keyPair1);
        String privateKeyStr1 = Hex.encodeHexString(privateKey1);
        System.out.println("甲方公钥:" + publicKeyStr1);
        System.out.println("甲方私钥:" + privateKeyStr1);
        KeyPair keyPair2 = initKey(publicKey1);
        byte[] publicKey2 = getPublicKey(keyPair2);
        String publicKeyStr2 = Hex.encodeHexString(publicKey2);
        byte[] privateKey2 = getPrivateKey(keyPair2);
        String privateKeyStr2 = Hex.encodeHexString(privateKey2);
        System.out.println("乙方公钥:" + publicKeyStr2);
        System.out.println("乙方私钥:" + privateKeyStr2);
        byte[] secrectKey1 = getSecretKey(publicKey2, privateKey1);
        byte[] secrectKey2 = getSecretKey(publicKey1, privateKey2);
        String secrectKeyStr1 = Hex.encodeHexString(secrectKey1);
        String secrectKeyStr2 = Hex.encodeHexString(secrectKey2);
        System.out.println("甲方协议密钥:"+secrectKeyStr1);
        System.out.println("乙方协议密钥:"+secrectKeyStr2);
        System.out.println("甲=乙:"+secrectKeyStr1.equals(secrectKeyStr2));

    }
}

如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】

个人公众号,欢迎关注,查阅更多精彩历史!

image
相关文章
|
1月前
|
存储 安全 算法
密码学基础:加密技术如何保护我们的在线生活
密码学基础:加密技术如何保护我们的在线生活
50 7
|
2月前
|
算法 安全 量子技术
量子计算与密码学:加密技术的新挑战
【10月更文挑战第29天】本文探讨了量子计算对密码学的影响,分析了现有加密技术面临的挑战,并展望了未来的发展趋势。量子计算的快速发展对传统公钥加密算法(如RSA和ECC)构成巨大威胁,同时也催生了后量子加密算法和量子密钥分发等新技术。文章强调了多元化、标准化和国际合作在构建量子安全加密体系中的重要性。
|
2月前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
90 2
|
2月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
174 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
4月前
|
存储 算法 Java
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
这篇文章详细介绍了在Java项目中如何使用MD5算法对用户密码进行加密存储和登录验证,包括加入依赖、编写MD5工具类、注册时的密码加密和登录时的密码验证等步骤,并通过示例代码和数据库存储信息展示了测试效果。
在Java中使用MD5对用户输入密码进行加密存储、同时登录验证。
|
3月前
|
安全 Java 数据安全/隐私保护
- 代码加密混淆工具-Java 编程安全性
在Java编程领域,保护代码安全与知识产权至关重要。本文探讨了代码加密混淆工具的重要性,并介绍了五款流行工具:ProGuard、DexGuard、Jscrambler、DashO 和 Ipa Guard。这些工具通过压缩、优化、混淆和加密等手段,提升代码安全性,保护知识产权。ProGuard 是开源工具,用于压缩和混淆Java代码;DexGuard 专为Android应用程序设计,提供强大加密功能;Jscrambler 基于云,保护Web和移动应用的JavaScript及HTML5代码;DashO 支持多种Java平台和
243 1
|
4月前
|
安全 Java 应用服务中间件
网络安全的护城河:漏洞防御与加密技术深入浅出Java并发编程
【8月更文挑战第31天】在数字世界的棋盘上,每一次点击都可能是一步棋。网络安全的战场无声却激烈,漏洞如同裂缝中的风,悄无声息地侵袭着数据的堡垒。本文将揭示网络漏洞的隐蔽角落,探讨如何通过加密技术筑起防线,同时提升个人和组织的安全意识,共同守护我们的数字家园。
|
4月前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
5月前
|
存储 安全 Java
Java中使用加密盐
摘要(Markdown格式): 本文介绍了密码安全性中的加盐(Salt)技术,以对抗彩虹表攻击。彩虹表是预先计算的哈希值集合,能威胁到仅使用MD5等简单哈希的密码。加盐是在密码中加入随机字符串,提高破解难度。文章展示了Java代码示例,说明如何生成和验证加盐后的密码。使用Spring Security的BCryptPasswordEncoder也作为例子给出,它提供了内置的加盐和加密功能。即使密码相同,每次加盐后生成的密文都不同,增强了密码的安全性。
114 1
Java中使用加密盐
|
5月前
|
缓存 网络协议 算法
(二)Java网络编程之爆肝HTTP、HTTPS、TLS协议及对称与非对称加密原理!
作为一名程序员,尤其是Java程序员,那必须得了解并掌握HTTP/HTTPS相关知识。因为在如今计算机网络通信中,HTTP协议的作用功不可没,无论是日常上网追剧、冲���、亦或是接口开发、调用等,必然存在HTTP的“影子”在内。尤其对于WEB开发者而言,HTTP几乎是每天会打交道的东西。
100 10