当前位置: 首页 > news >正文

Diffie-hellman 密匙交换

新的需求

在之前的章节中,Alice 和 Bob 在进行加密通信之前,他们首先需要面对面的共享那个用于加解密的密匙。但是,如果 Alice 和 Bob 无法面对面的交换密匙,将会面临什么问题?

随着计算机网络的普及,人们之间的交流不再受到的地理位置的限制。如果 Alice 和 Bob 之间需要通过计算机网络去进行一些私密消息的传递时,他们就需要将信息加密。于是他们就会在网络通信时首先向对方发送本次通信的密匙,然后发送加密的内容。但是,他们之间总是存在一个窃听者 Eve,他将 Alice 和 Bob 之间的网络通信截取并拷贝下来,然后原封不动的发给另一方。通过这中方式,Eve 就会得到 Alice 和 Bob 之间的密匙,以及全部的加密消息,当然因为有了密匙,他也轻而易举的窃取了全部的通信内容。那么,有没有一种密匙交换方式,可以使得 Alice 和 Bob 在 Eve 的窃听下协商出一个不被 Eve 获知的通信密匙呢?

Diffie-hellman 密匙交换

在 1976 年,Whitfield Diffie 和 Martin Hellman 一起发明了一个让人叫绝的方式,被称为 “迪菲-赫尔曼密钥交换 Diffie–Hellman key exchange 简称 D-H”。D-H 是基于模运算的一个算法。所以首先我们看下什么是模运算。

比如,现在我们有一个圆形的时钟,我们注意到时钟上面的小时刻度将这个时钟的周长分成了 12 个等份,我们简称这个等份为 小时刻度周长等份。然后我们手上有一根绳子,绳子的长度为 15 倍于小时刻度周长等份。现在我们做一件事,将绳子的一头放在时钟的 0 刻度位置(或者 12 刻度),然后顺时针的将绳子沿着时钟的边缘进行一圈一圈的环绕。最后我们会发现绳子的另一头停在了刻度 3 上。简化成一个数学式子就是 15 mod 12 = 3 ,它表示 15 对 12 取模后剩余 3。

我们发现模运算有一个特点,就是对一个数求模,可以容易的得到其求模后的结果。但是如果反过来想获取原本的数却很难。比如 2187 mod 7 = 31594323 mod 7 = 3 等等,如果知道了结果 3,和模 7,无法准确得知原本的数。

接下来,我们看看在使用 D-H 密匙交换方式时,Alice 和 Bob 之间是如何协商出通信密匙的:

  1. 首先 Alice 先要随机选择两个数 a,n 作为模运算的基础,然后再次随机出一个数 x
  2. 然后 Alice 通过一个运算生成一个数 p,p 的运算方式为:
p = a^x mod n
复制代码
  1. 然后 Alice 把 p、a、和 n 告诉了 Bob,当然,Eve 也知道了这些内容
  2. 接着, Bob 随机选择一个数 y,并通过下面的运算生成一个数 q:
q = a^y mod n
复制代码

在 q 生成了之后,Bob 将其告诉了 Alice,当然,Eve 也得知了 q 5. 现在,Alice 和 Bob 开始生成密匙

// Alice
key = q^x mod n

// Bob
key = p^y mod n 
复制代码

通过上面的运算,Alice 和 Bob 生成了他们之间的密匙。

现在,你可能很疑惑,那就是为什么经过这一系列的运算后,Alice 和 Bob 之间生成了一把 Eve 不知道的密匙。

我们先来看看 Alice,她接收到了 Bob 传过来的公开信息 q:

q = a^y mod n               1)
复制代码

之后,为了得到密匙,她进行了下面的操作:

q^x mod n                   2)
复制代码

那么将 1) 中的 q 带入到 2) 中的式子,替换 2) 中的 q,Alice 生成密匙的操作等价于:

(a^y mod n)^x mod n         3)
复制代码

再来看看 Bob,他接收到了 Alice 传来的公开信息 p,为了生成密匙,他做了下面的操作:

p^y mod n              4)
复制代码

那么我们看看 p 是什么,它是由 Alice 这样生成的:

p = a^x mod n          5)
复制代码

这次,我们将 5) 带入到 4) 中,替换 4) 中的 p,然后我们发现 Bob 的操作等价于

(a^x mod n)^y mod n    6)
复制代码

现在,我们如果可以证明 3) 和 6) 是等价的话,就可以说明这种方式是可以工作的。

开始求证

上文中,我们通过使用一个绳子沿着时钟的边缘绕圈的例子,对求模运算有了一个形象的认识。现在,如果需要操作一根长度为 136 倍于小时刻度周长等份的绳子的话,你已经知道该如何做了 - 就是将绳子的一头放在 0 刻度,沿着时钟边缘不断地绕圈,看绳子另一头最终落在哪个刻度上。

现在,尝试以相似的方式去理解运算(100+36) mod 12 。你可以想象成,在你准备对一个长度是 136 倍于小时刻度周长等份的绳子进行绕圈之前,绳子不小心断成了两段,一段长度为 100 倍于小时刻度周长等份、另一段长度为 36 倍于小时刻度周长等份(下面就简称 “100 长度”“36 长度”)。

为了完成工作,你先将 100 长度的绳子的一头放在 0 刻度,然后开始沿着时钟边缘进行绕圈,在这个 100 长度的绳子绕完之后,绳子的另一头应该落在某个刻度上,我们使用 (100 mod 12) 去表示这个刻度。然后,你拿起另外一段 36 长度的绳子,也将它的一头放在 0 刻度,开始对时钟进行绕圈,绕完之后,绳子的另一头也落在了某个刻度上,我们使用 (36 mod 12) 去表示这个刻度。最后你将 100 mod 12 加上了 36 mod 12

为什么可以相加? 之前,我们将 36 长度的绳子的一头放在的 0 刻度,通过让绳子沿着时钟边缘不断地顺时针绕圈,使得最终绳子的另一头会落在时钟上的某一个刻度上,我使用 36 mod 12 去表示这个刻度。我们换一个角度,36 mod 12 其实也表示了一个顺时针的偏移量,你可以想象一下,不论起始刻度选在哪里,最终绳子另一头落下的刻度和起始刻度之间总会有一个 36 mod 12 的偏移量。当我们绕完了 100 长度的绳子时,我们知道了它的终点落在了 100 mod 12 这个刻度上,于是,我们拿起 36 长度的绳子,接着 100 长度的绳子的终点继续绕圈。站在偏移量的角度,“将 36 长度的绳子的一头放在 100 长度绳子结束的刻度上,然后让它继续沿着时钟边缘绕圈” 就可以看成是 “在 100 长度绳子结束的刻度上又顺时针偏移了 36 mod 12

所以我们可以让 100 mod 12 加上 36 mod 12 。不过还有一点需要注意,就是在 100 mod 12 的刻度上再顺时针偏移 36 mod 12 的话,可能会超过 0 刻度又开始新的一个绕圈。所以,100 mod 12 加上 36 mod 12 的结果还需要再 mod 12。所以完整的表示就是 (100 mod 12 + 36 mod 12) mod 12

通过这个形象的例子,我们得出一个模运算的加法规律:

(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) mod p, a、b、p ∈ 正整数

∵  对于正整数 a,等式 a = k1*p + r1 一定成立
    对于正整数 b,等式 b = k2*p + r2 一定成立

∴	a*b = (k1*p + r1 ) * (k2*p + r2)
		= k1*k2*p^2 + k1*p*r2 + r1*k2*p + r1*r2

∵  (a+b) mod p = (a mod p + b mod p) mod p

∴  (a*b) mod p = (k1*k2*p^2 + k1*p*r2 + r1*k2*p + r1*r2) mod p

	= (k1*k2*p^2 mod p + k1*p*r2 mod p + r1*k2*p mod p + r1*r2 mod p) mod p
	= (0 + 0 + 0 + r1*r2 mod p) mod p
	= (r1*r2 mod p) mod p
	= r1*r2 mod p

∵  r1 = a mod p, r2 = b mod p

∴ (a*b) mod p = (a mod p * b mod p) mod p

得证
复制代码

那么,(a^b) mod p 有什么变换规律呢?因为 a^b = a*a*a*a...*a 一共是 ba 相乘。那么根据上面的乘法规律 (a^b) mod p 其实就是:

(a*a*a*a...*a) mod p = (a mod p * a mod p * a mod p ... * a mod p) mod p
复制代码

于是就得出了乘方的规律:

(a^b) mod p = (a mod p)^b mod p
复制代码

那么我就开始证明,为什么 Alice 生成密匙的操作与 Bob 生成密匙的操作会得到相同的结果,其实也就是证明上面的 3) 与 6) 等价,即:

(a^y mod n)^x mod n = (a^x mod n)^y mod n   10)
复制代码

首先我们结合刚得到的乘方公式,看下 10) 等式的左边:

rule: (a^b) mod p = (a mod p)^b mod p
left:             (a^y mod n)^x mod n
通过这样对齐,我们可以将 left 中的 a^y 看做一个整体,于是 left 就可以变换为:
      a^y*x mod n
复制代码

同样的,对于 10) 等式的右边:

a^x*y mod n
复制代码

于是我们发现,左右两边的等式是相同,因而 Alice 和 Bob 可以得到相同的结果。

繁琐的回报

到目前为止 Alice 和 Bob 之间为了协商出一个不被 Eve 获知的密匙可谓是费尽周折。那么,这么做有什么用吗?

首先,我们看看一直在窃听的 Eve 手上有了哪些信息:p,q,a,n 。

然后我们知道,对于密匙生成方式实际就是 a^x*y mod n。在这个生成密匙的式子中,Eve 只直接知道了 a 和 n,对于 x 和 y 他并没有直接获取到。当然,他也获取了 x 和 y 的相关信息,就是 p = a^x mod nq = a^y mod n。于是他只有通过两条式子去逆推出 x 和 y。

如何逆推呢?根据 p = a^x mod n,可以转换成 a^x = k*n + p,已知 a,n,p,通过不断的调整 k 算出可能的 x,同理再不断的算出可能的 y,将它们相乘再配合实际的密匙生成式子 key = a^x*y mod n 猜测所有可能的 key。

使加密更加牢固

回顾一下 Alice 是怎么生成 p 的?

// Alice
p = a^x mod n
复制代码

我们选几组不同的 a 和 n ,然后不断的调整 x 的大小,看看 p 有怎样的变化:

// 在浏览器控制台中运行
var test = function (a, n, x) {
    var unique = {}, out = [], p;
    
    for (var i = 1; i < x; i++) {
        p = Math.pow(a, i) % n;
        out.push(p);
        if (unique[p] === true) continue;
        unique[p] = true;
    }
    
    console.log("Output: ", out.join(', '));
    console.log("count: ", out.length);
    console.log("%c------------------------------------------------", "color:blue");
    console.log("Unique:", Object.keys(unique));
    console.log("count: ", out.length);
    console.log("%c================================================", "color:red")
};
复制代码
// test(4, 10, 100);
Output:  4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4
count:  99
------------------------------------------------
Unique: ["4", "6"]
count:  99
================================================
复制代码
// test(3, 10, 100);
Output:  3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3, 8, 4, 6, 6, 0, 6, 8, 2, 6, 6, 4, 2, 4, 0, 8, 8, 4, 2, 0, 4, 8, 8, 0, 4, 2, 4, 0, 2, 4, 4, 0, 2, 8, 4, 6, 2, 8, 4, 0, 8, 6, 6, 0, 4, 8, 2, 8, 2, 8, 0, 6, 8, 2, 8, 4, 0, 6, 2, 6, 0, 0, 0, 2, 6, 4, 0
count:  99
------------------------------------------------
Unique: ["0", "1", "2", "3", "4", "6", "7", "8", "9"]
count:  99
================================================
复制代码
test(3, 11, 100);
Output:  3, 9, 5, 4, 1, 3, 9, 5, 4, 1, 3, 9, 5, 4, 1, 3, 9, 5, 4, 1, 3, 9, 5, 4, 1, 3, 9, 5, 4, 1, 3, 9, 5, 3, 9, 7, 0, 4, 4, 1, 5, 4, 8, 0, 0, 6, 5, 1, 9, 5, 4, 9, 6, 0, 9, 3, 0, 0, 6, 5, 7, 3, 2, 2, 1, 1, 3, 3, 0, 7, 10, 6, 4, 4, 2, 0, 8, 1, 5, 1, 10, 4, 9, 6, 7, 9, 4, 1, 10, 7, 0, 0, 8, 2, 6, 1, 3, 5, 1
count:  99
------------------------------------------------
Unique: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
count:  99
================================================
复制代码

在上面的输出中,我们发现对于式子 p = a^x mod n 来说,当 (a, n) = 1 (读作 a 与 n 互素)时,不断地调整 x,对应的 p 值是 0~n 之间的随机整数。所以说,为了确保 p 的密匙空间足够大,我们在随机选择 a 和 n 时,要确保它们是互素的。在实际操作中,为了简化操作,我们直接随机选择素数的 a 和 素数的 n,这样就可以省略判断 a 和 n 互素的步骤。

至此,我们对 D-H 方法的作用以及原理有了简单的认识。

相关文章:

  • 一段话系列-Spring cloud gateway都有哪些内置filter
  • 中俄蒙三国六方签订白鹤研究与保护合作备忘录
  • jS获取子、父、兄节点方法小结
  • quartz详细介绍
  • 回顾小程序2018年三足鼎立历程,2019年BAT火力全开
  • oschina
  • 深度学习入门:10门免费线上课程推荐
  • CF712E Memory and Casinos
  • 研究:印度气候变暖速度加剧 2040年或面临重灾
  • ImageLoader的优化写法
  • 七牛实时音视频云视频连线demo(web部分)
  • 支付宝支撑2135亿成交额的数据库架构原理
  • ZStack--工作流引擎
  • dmidecode查看linux硬件信息
  • Octave 入门
  • #Java异常处理
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 【css3】浏览器内核及其兼容性
  • co模块的前端实现
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • Java-详解HashMap
  • Netty源码解析1-Buffer
  • Promise初体验
  • spark本地环境的搭建到运行第一个spark程序
  • Spring声明式事务管理之一:五大属性分析
  • vuex 笔记整理
  • 初识 beanstalkd
  • 分布式熔断降级平台aegis
  • 缓存与缓冲
  • 回流、重绘及其优化
  • 扑朔迷离的属性和特性【彻底弄清】
  • 入口文件开始,分析Vue源码实现
  • 世界上最简单的无等待算法(getAndIncrement)
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 突破自己的技术思维
  • 线性表及其算法(java实现)
  • 小程序开发中的那些坑
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • #100天计划# 2013年9月29日
  • (1)常见O(n^2)排序算法解析
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (js)循环条件满足时终止循环
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (十六)串口UART
  • (十五)使用Nexus创建Maven私服
  • (四)鸿鹄云架构一服务注册中心
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)菜鸟学数据库(三)——存储过程
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .Net - 类的介绍
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .net 4.0发布后不能正常显示图片问题