Diffie-Hellman密钥协商算法探究

news2024/11/24 1:22:25

在这里插入图片描述

作者 | 魔王赵二狗

导读

隐私计算(Privacy-preserving computation)是指在保证数据提供方不泄露原始数据的前提下,对数据进行分析计算的一系列信息技术,保障数据在流通与融合过程中的可用不可见。而Diffie–Hellman密钥协商是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道创建起一个密钥。这个密钥可以在后续的通讯中作为对称秘钥讯内容。

全文6088字,预计阅读时间16分钟。

01 什么是DH密钥协商算法

1.1 DH由来

DH密钥协商是Whitefield与Martin Hellman在1976年提出了一个的密钥交换协议。

1.2 解决什么问题

首先我们先看一个场景:

小明要在网络上给小红发一篇情书,小明呢,比较害羞,不想让其他人知道情书的内容。

那么显而易见,小明必须要对内容进行加密,这个时候就需要选择加密的方式,我们知道对于非对称加密对内容的长度是有限制的,而小明写的情书内容又非常多,那只好选用AES对称加密。

图片

我们知道,AES对称加密和解密是需要密钥key的,那么我们假定小明和小红之间需要传递密钥,那么如何保证密钥key的安全性?这时候你可能会说,把密钥key用RSA非对称加密不就好了(数字信封的概念),但我们是否有其他更好的方式解决问题?

这时候,DH密钥协商算法就应运而生,他解决的就是对称加密的密钥无需进行传输,并使小明、小红使用的AES密钥是一致的,那么这是如何实现的呢。

1.3 实现原理

DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。

我们来看DH算法交换密钥的步骤。假设小明、小红双方需要传递密钥,他们之间可以这么做:

图片

  1. 小明首选选择一个素数 p p p,例如:97,底数 g g g p p p的一个原根,例如:5,随机数,例如:123,然后计算 A = g a A=g^a A=ga ,然后,小红发送 p = 97 p=97 p=97 g = 5 g=5 g=5 A = 34 A=34 A=34给小红;

  2. 小红收到后,也选择一个随机数 b b b,例如:456,然后计算 B = g a m o d p = 75 B=g^a mod p=75 B=gamodp=75 ,小红再同时计算 s 2 = A b m o d p = 22 s2=A^b mod p=22 s2=Abmodp=22

  3. 小红把计算的 B = 75 B=75 B=75发给小明,小明计算 s 1 = B a m o d p = 22 s1=B^a mod p=22 s1=Bamodp=22,计算结果与乙算出的结果一样,都是22。

所以最终双方协商出的密钥 s = 22 s=22 s=22。注意到这个密钥 s s s并没有在网络上传输。而通过网络传输的 p p p, g g g A A A B B B是无法推算出 s s s的,因为实际算法选择的素数是非常大的。

所以,更确切地说,DH算法是一个密钥协商算法,双方最终协商出一个共同的密钥,而这个密钥不会通过网络传输,来保障了密钥的安全性。此时,小明和小红都露出了开心的笑容。

02 公式推导

本着严谨的态度,现在我们对 s 1 s1 s1 s 2 s2 s2是否恒等做公式推导。一般来说,书上是如下解释:

图片

这里是运用了求余的运算规则,也就是说以下等式默认是成立的:

图片

其实当成定理记住也就OK的,但是想要证明这个公式也很简单:将求余运算转换为加减乘除运算,然后利用二项式展开公式便可以得到答案。

推导过程:

图片

根据①②可得

图片

图片

将③带入上式,可得

图片

使用二项式展开公式将展开,则

图片

从这个表达式可以看出,前项每一项都是的整数倍,因此 运算必定为0,因此:

图片

所以

图片

成立。

03 应用实现

注:本示例以Java服务端作为小明,Android客户端作为小红,下图为执行顺序。

图片

1.客户端发起请求

获取服务端的p,g,serverNum

 // 获取服务器的p,g,serverNum
Request request = new Request.Builder()
        .get()
        .url("https://xxxxx/dh/getdhbasedata")
        .build();
Call call = mHttpClient.newCall(request);
Response res = call.execute();

2. 服务端创建信息

创建DHServer类

public class DHServer {
    /** 用来生成大素数p */
    private static final String SOURCE = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
    /** 大素数p */
    private BigInteger mP;
    /** 原根g */
    private BigInteger mG;
    /** 服务端随机数值 */
    private int mServerNum
}

DHServer中增加初始化方法:

 /**
 * 初始化p g serverNum 以及计算服务端的processedServerNum
 * @return HashMap<String, String> 返回初始化创建好的p g serverNum 以及计算服务端的processedServerNum
 */
public HashMap<String, String> init() {
    generateBaseInfo();
    HashMap<String, String> baseData = new HashMap<>();
    baseData.put("p", mP.toString());
    baseData.put("g", mG.toString());
    baseData.put("serverNumr", mServerNum + "");
    baseData.put("processedServerNum", processServerKey());
    return baseData;
}

DHServer中增加创建基础信息方法:

/**
 * 生成基础信息,p,g,服务端随机数serverNum
 */
private void generateBaseInfo () {
    // 第一步:根据pSource生成服务器当前固定的p
    BigInteger p = new BigInteger(SOURCE, 16);
    BigInteger tempP;
    BigInteger g;
    BigInteger gFlag;
    while (true) {
        tempP = p.subtract(new BigInteger("1"));
        // 取一个2-p中间的随机数
        g = getBigIntegerRandomRange(new BigInteger("2"), tempP);
        gFlag = g.modPow(tempP, p);
        if (gFlag.toString().equals("1")) {
            break;
        }
    }
    Random serverNumRd = new Random();
    this.mServerNum = serverNumRd.nextInt(100000) + 100;
    this.mG = g;
    this.mP = p;
}

DHServer中计算服务端的数值processedServerNum

/**
 * 返回已处理的服务端processedServerNum
 * @return processedServerNum
 */
private String processServerKey() {
    return mG.modPow((new BigInteger(mServerNum + "")), mP).toString();
}

3. 客户端收到信息后进行计算

接收服务端数据

JSONObject data = new JSONObject(res.body().string());
String p = data.getString("p");
String g = data.getString("g");
String processedServerNum = data.getString("processedServerNum");

创建DHClient类并在构造方法中生成客户端随机数mClientNum

public class TiDHClient {
    private final int mClientNum;
    private BigInteger mP;
    private BigInteger mG;
    private BigInteger mProcessedServerNum;
    private BigInteger mProcessedClientNum;
    private BigInteger mKey;
    public TiDHClient() {
        mClientNum = new Random().nextInt(99999 - 10000) + 10000;
    }
}

DHClient增加计算方法,计算出密钥key与客户端计算值mProcessedClientNum

 /**
 * 通过服务端获取的 p, g 和processedServerNum计算密钥key.
 * @param p 通过服务端获取的 p
 * @param g 通过服务端获取的 g
 * @param serverNum 通过服务端获取的 server number
 * @return 密钥字符串
 */
public String processKey(String p, String g, String processedServerNum) {
    mP = new BigInteger(p);
    mG = new BigInteger(g);
    mProcessedServerNum = new BigInteger(processedServerNum);
    mProcessedClientNum = mG.modPow(new BigInteger(String.valueOf(mClientNum)), mP);
    // 计算密钥key
    mPublicKey = mServerNumber.modPow(new BigInteger(String.valueOf(mClientNum)), mP);
    return mPublicKey.toString();
}

DHClient中添加get方法

/**
 * 获取 processedClientNum. 用于发送给服务端.
 * 如果未调用 processKey 将返回空字符串.
 * @return processedClientNum.
 */
public String getProcessedClientNum() {
    if (mProcessedClientNum == null) {
        return "";
    }
    return  mProcessedClientNum.toString();
}

/**
 * 返回密钥字符串.
 * 如果未调用processKey 将返回空字符串
 * @return public key
 */
public String getKey() {
    if (mKey == null) {
        return "";
    }
    return mKey.toString();
}

4.客户端将processedClientNum计算结果给服务端

// 根据processedServerNum,processedClientNum和p 计算出密钥K
TiDHClient dhClient = new TiDHClient();
mClientKey = dhClient.processKey(p, g, serverNumber);
// 将计算过后的processedClientNum发送给服务器
FormBody formBody = new FormBody
        .Builder()
        .add("processedClientNum",dhClient.getProcessedClientNum())
        .build();
request = new Request.Builder()
        .post(formBody)
        .url("https://xxxxxxxxxx/dh/postdhclientdata")
        .build();
call = mHttpClient.newCall(request);
res = call.execute();
data = new JSONObject(res.body().string());

5.服务端计算密钥key

DHServer中添加计算方法

 /**
 * 根据客户端传过来的processedClientNum 计算出key
 * @param processedClientNum 客户端传过来的processedClientNum
 * @param serverNum 上一次请求随机生成的serverNum
 * @param p 上一次请求的 p
 * @return String 密钥key
 */
public String computeShareKey (String processedClientNum, String serverNumber, String p) {
    BigInteger BigClientNumber = new BigInteger(processedClientNum);
    return BigClientNumber.modPow(new BigInteger(serverNumber + ""), new BigInteger(p)).toString();
}

怎么样,看到这里是否感觉这像是握手的过程,其实HTTPS的TLS1.3版本也引入了DH的概念来保证安全性。此外,p,g的生成还可以用RSA的公钥和私钥,这时候就会演变成DH-RSA算法。同时p,g的生成是放在服务端还是放在客户端其实各有优缺点,大家可以考虑下。

学会这些,我们也可以在业务中仿写数据传输的工具SDK,只要在初始化阶段进行协商,那么就能得到一个无法被抓包和破解的加密key,希望对大家的实践有所帮助。

——END——

推荐阅读:

贴吧低代码高性能规则引擎设计

浅谈权限系统在多利熊业务应用

分布式系统关键路径延迟分析实践

百度工程师教你玩转设计模式(装饰器模式)

百度工程师带你体验引擎中的nodejs

揭秘百度智能测试在测试定位领域实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/154757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023-01-10 mysql列存储引擎-聚合多线程并行扫表-VCPackGuardian策略LOCK_ALL-概要设计

摘要: 当前的pack淘汰策略为LOCK_ONE, 在多线程切换时导致pack地址丢失。 新设计LOCK_ALL策略以保证多线程聚合正常工作。 设计思想: 多线程聚合运算期间, 对持有的pack不做淘汰业务中对pack的读取和释放保持原有逻辑架构设计: 静态结构: 动态结构: 上层业务通过VirtualCol…

【学习笔记之Linux】工具之vim基本介绍

vim基本认识 vim是一种多模式的编辑器&#xff0c;它是vi的升级版本&#xff0c;它兼容vi所有的指令并加入了一些新的特性在里面。vi是一个老式的文本编辑器&#xff0c;功能相当齐全&#xff0c;vim则是在vi之上更进了一步&#xff0c;拥有代码补全、编译及错误跳转等功能&…

pytorch OutOfMemoryError

torch.cuda.OutOfMemoryError before: self.memory deque(maxlen50000) after: self.memory deque(maxlen500) ok.... pytorch模型提示超出内存cuda runtime error(2): out of memory - pytorch中文网 看到这个提示&#xff0c;表示您的GPU内存不足。由于我们经常在PyTo…

做外贸有没有好的软件?

在外贸电商行业中&#xff0c;邮件营销是非常重要的一种营销方式之一。除了性价比高&#xff0c;他还能对目标客户进行精准营销。但是&#xff0c;对于刚开始做的公司来讲&#xff0c;不注意方法和细节也难收获到理想的营销效果。 一、问题 1&#xff09;不管理邮箱联系人 只…

13.Isaac教程--模型制作

模型制作 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 该软件包演示了具有软件定义装配工作流程的工厂场景。 在模拟工厂环境中&#xff0c;多个 AMR 在装配站之间运输材料&#xff0c;而每个装配站的机械臂拾取所需材料并将其放置在对接的 …

自定义el-pagination分页

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; vue项目使用el-ui库&#xff0c;由于原本的el-pagination显示字段和样式无法满足其他项目的设计要求&#xff0c;需要进行改动 el-ui官网&#xff1a; 改动后&#xff1a; 解决方案&#xff1a; 1…

golang字符串常见功能

文章目录1. 获取字符串长度2. 是否一xx开头3. 是否以xx结尾4. 是否包含5. 变大写6. 变小写7. 去两边8. 替换9. 分割10. 拼接11. string转换为int12. int转换为string13. 字符串和字节切片14. 字符串和rune切片15. string和字符1. 获取字符串长度 2. 是否一xx开头 3. 是否以xx结…

抖音seo优化排名

武汉微驱动科技有限公司 你有没有想过&#xff0c;同样是运营抖音&#xff0c;为什么别人的视频总是排在你的前面&#xff1f;你死磕创意&#xff0c;拍摄、剪辑&#xff0c;甚至比同行更投入&#xff0c;为什么他的收益总是高于你&#xff1f; 当下抖音搜索引擎的用户数量已经…

Nginx与LUA(1)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;HTTP服务器是相对于HTTP客户端来说的——HTTP客户端就是各种常用的「浏览器」&#xff0c;如IE、chrome、微信浏览器。当浏览器通过URL地址栏访问一个Web页面时&a…

【C++】STL六大组件之一——适配器(adapters)

目录1. 前言2. 初始适配器2.1 适配器的概念2.2 适配器的分类3. 容器适配器&#xff08;container adapters&#xff09;3.1 认识deque3.1.1 逻辑结构3.1.2 物理结构3.1.3 deque的迭代器3.1.4 选择deque做stack/queue底层容器的原因3.2 stack3.3 queue3.4 另一种容器适配器 ——…

阿里云计算巢 x GBase GCDW:自动化部署云原生数据仓库

近日&#xff0c;阿里云计算巢与天津南大通用数据技术股份有限公司&#xff08;以下简称&#xff1a;GBASE&#xff09;合作&#xff0c;双方融合各自技术优势&#xff0c;助力企业用户实现云上数据仓库的自动化部署&#xff0c;让用户在云端获取数据仓库服务“更简单”&#x…

【ESP32+freeRTOS学习笔记-(六)软件定时器】

目录1、软件定时器概念2、软件定时器的运行机制2.1 组成2.2 创建2.3 运行3、软件定时器的属性和状态3.1 定时器的周期3.2 定时器的类型3.3 定时器的状态4、软件定时器的回调函数原型5、定时器的使用5.1 创建定时器xTimeCreate()5.2 启动定时器xTimerStart()5.3 终止定时器xTime…

IPC进程间通信-system V 共享内存

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位大佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 文章目录一、共享内存原理二、共享内存的建立原理三、共享内存的创建四、共享内存的删除五、共享内存挂接到自己的地址空间六、从进程地址空间去掉与…

快过年了用Python抢红包

快过年了&#xff0c;刚刚收到了两个消息&#xff0c;一个好消息&#xff0c;一个坏消息。 先说好消息&#xff0c;好消息就是微信群里即将有人要发红包&#xff0c; 坏消息是我抢不上&#xff01; 难道就这么放弃了吗&#xff1f;那就只能试试能不能通过编程的方式实现自动化…

基于轻量级YOLOV5+BIFPN的苹果瑕疵检测识别分析系统

BIFPN是一种比较经典有效的特征融合手段&#xff0c;在很多检测模型中都有集成应用&#xff0c;实际表现也验证了BIFPN的有效性&#xff0c;这里并不是要探讨BIFPN的原理内容&#xff0c;而是想集成这项技术&#xff0c;提升原有模型的性能表现&#xff0c;在我之前的一些文章中…

排序算法之冒泡排序

一般学习过编程的人都知道&#xff0c;排序算法有很多种&#xff0c;包括直接选择排序、直接插入排序、计数排序、快速排序、归并排序、冒泡排序等&#xff0c;在我看来&#xff0c;以上六种排序算法是必须要掌握的&#xff0c;今天&#xff0c;我们先来讲解一下冒泡排序算法&a…

Java高手速成 | 新增类Record的工作实例

01、什么是Record? Record 是Java新增的库类&#xff0c;在Java 14和Java 15中以预览&#xff08;preview&#xff09;形式公布。Record类用来自动生成对定义数据进行创建、设置、访问以及比较等代码&#xff0c;所以又被称作数据类&#xff08;data class&#xff09;。在一…

初级开发者福音:手把手教你实现数字滚动效果~

文章目录一、前言二、背景知识三、实现方案Step 1&#xff1a;分析需求Step 2&#xff1a;实现单个数字的滚动效果Step 3&#xff1a;组件接口设计Step 4&#xff1a;完善组件一、前言 前端数字滚动显示的场景很多&#xff0c;比如抽奖的时候&#xff0c;营造一种马上公布中奖…

[MySQL从入门到实战环境部署](超详细版)

MySQL从入门到实战环境部署1.部署CentOS1.1部署CenOS虚拟机步骤&#xff08;1&#xff09;基于VirtualBox&#xff08;2&#xff09;下载CentOS1.2环境部署过程2.部署MySQL1.部署CentOS 1.1部署CenOS虚拟机步骤 &#xff08;1&#xff09;基于VirtualBox 下载网址&#xff1…

Docker Compose:Docker Compose部署nacos初始化MySQL

Docker Compose&#xff1a;Docker Compose部署nacos初始化MySQL找初始化sql文件nacos初始化mysql-schema.sql文件内容docker-compose.yml上传到挂载目录运行docker-compose.yml访问nacos找初始化sql文件 先去官网下载nacos安装包 官方github地址&#xff1a;https://github.…