之前的章节讲到了对称加密算法AES,发送方和接收方需要使用相同的密钥进行通讯,但是发送方怎么将密钥安全的发送给接收方?这是一个问题。
对称加密算法中,为了将密钥安全的发送给对端,通常有以下四种方法:
事先共享密钥
事先双方约定好密钥的数值,或者使用其他安全的手段例如派特工押送等,将密钥安全的送到对端,这种方式虽然简单,但是局限性很大,另外通讯量增大以后,密钥个数也会相应增加,这在高数量级的设备通讯上并不现实。
密钥分配中心
为了解决事先共享密钥的密钥增多的问题,也可以使用密钥分配中心来解决。每次加密的密钥由密钥中心进行分配,每个人只要和密钥中心事先共享密钥就可以了。虽然这个方法解决了密钥增多的问题,但是密钥中心存储和记录了所有的密钥,一旦它出现故障或者被攻击破坏,那么所有的加密都会瘫痪,这也是集中式管理的缺点。
DH 密钥交换
为了解决集中式管理的缺点,有人想出了 Diffie-Hellman 密钥交换的方法。在 Diffie-Hellman 密钥交换中,加密通信双方需要交换一些信息,而这个过程是可以防止窃听者攻击的。根据交换的信息,双方各自生成相同的密钥。而窃听者无法生成相同的密钥。 将DH算法与ECC公钥算法结合形成ECDH算法,这是一种解决密钥交换问题的一种很常见的解决方案,这个下面也会详细讲解。
非对称加密
非对称加密双方分别有一个公钥和一个私钥。公钥可以互相发给对方,用来加密数据,私钥则由自己保存。公钥甚至在网上传播,被窃听者拿到也没有关系,由于没有私钥,他也无法解开密文。私钥只要掌握在接收者手上就不会导致密文被窃听,这样就解决了密钥传输的问题。
虽然非对称加密解决了密钥配送的问题,但是非对称密钥仍然存在一些问题,比如:
非对称加密算法可以说是密码学历史上最伟大的发明,每个公钥算法的底层原理都是非常巧妙的。常用的非对称加密算法有RSA、ECC算法等, 在物联网领域常用的是ECC加密算法。
我们知道非对称加密可以防止窃听者攻击,也就是说双方进行公钥分享、协商、计算的时候就算是旁边有窃听者,将双方的数据都获取到,也无法计算出双方协商的密钥。 对于初学者可能很难理解这件事情,双方的数据都可以窃听到,为什么通讯双方可以计算出密钥,而窃听者却无法计算出呢?
对于RSA算法是基于大素数相乘后结果很容易计算,但是通过结果反推出两个素数却很困难,目前只能使用穷举法。
ECC算法是基于椭圆曲线的离散对数问题上的密码体制,给定椭圆曲线上的一个点G,并选取一个整数k,求解K=kG很容易,但反过来计算确很困难。
所以非对称加密的本质就是利用正向运算比较简单,但可逆运算比较困难的单向函数。 而防止窃听者攻击也并不是窃听者完全无法破解出密钥,而是因为窃听者只能使用穷举法破解,可能破解的时间是正向运算的几万倍,就近似的认为该算法是可以防止窃听者攻击的。
由于 ECC 密钥具有很短的长度,所以运算速度比较快。到目前为止,对于 ECC 进行逆操作还是很难的,数学上证明不可破解,ECC 算法的优势就是性能和安全性高。实际应用可以结合其他的公开密钥算法形成更快、更安全的公开密钥算法,比如结合 DH 密钥形成 ECDH 密钥协商算法,结合数字签名 DSA 算法组成 ECDSA 数字签名算法。
ECDH算法常常用来进行密钥的协商,协商好密钥后,用来解决上面的密钥分配问题,将对称加密的密钥安全的传到对端设备。
算法 | 加密/解密 | 数字签名 | 密钥交换 |
---|---|---|---|
RSA | ✅ | ✅ | ✅ |
Diffie-Hellman | ❌ | ❌ | ✅ |
DSS | ❌ | ✅ | ❌ |
椭圆曲线 ECC | ✅ | ✅ | ✅ |
如上表,椭圆曲线可以用于 3 个方面:
椭圆曲线源自于求椭圆弧长的椭圆积分的反函数。一般情况下,椭圆曲线可以用下面的方程式来表示,其中 a,b,c,d 为系数:
E: y^2 = ax^3 + bx^2 + cx+ d
举个例子,
上面是一条椭圆曲线,但是它的样子也并不是一个椭圆。
加法运算:椭圆曲线上的两点 A 和 B,构成的直线与椭圆曲线的交点,与 X 轴的对称点,定义为 A+B。如下图:
当然也存在两个点重合的情况,这种情况下,就相当于寻找 2 倍点的问题。在椭圆曲线上的一点 A,做一条切线,与椭圆曲线的另外一交点,相对于 X 轴的对称点成为 2 倍点。这种运算成为 2 倍计算,如下图:
点 A 相对于 X 轴的对称位置的点成为 -A。这个运算成为椭圆曲线的正负取反运算。如下图:
椭圆曲线加密的实质是利用椭圆曲线上离散对数问题(Elliptic Curve Discrete Logarithm Problem, ECDLP),实质是已知点 xG 求数 x 的问题。
已知:
椭圆曲线 E
椭圆曲线 E 上的一点 G (基点)
椭圆曲线 E 上的一点 xG (G 的 x 倍)求:
数 x
椭圆曲线在实数域上是连续的,曲线也是一条光滑的曲线。假设椭圆曲线为 E2:y^2 = x^3 + x + 1,如下图:
如果位于有限域 F23 上,那么 E2:y^2 ≡ x^3 + x + 1 (mod 23)。此时椭圆曲线就不是一个连续的曲线了,而是一堆离散的点。如下图:
上图中每个点的 y 坐标对 23 求余,都等于 x^3 + x + 1 对 23 求余。如果我们把 E2 上的点 G=(0,1) 作为基点,那么按照椭圆曲线的计算规则计算 2G、3G、4G、5G、……。
这就是“已知 G 和 xG 求 x 的问题”。这个问题在 p 相当大的时候,很难求解。椭圆曲线无法被破解的原因是:求解椭圆曲线上的离散对数问题是非常困难的。
但是椭圆曲线加密算法并非在实数域 R 上,而是在有限域 F§ 上。有限域 F§ 指的是对于某个给定的质数 p,由 0,1,2,……,p-1 共 p 个元素所组成的整数集合中定义的加减乘除运算。
椭圆曲线加密算法除了可以使用特征数为质数 p 的素域 GF§,椭圆曲线还可以使用特征数为 2^m 的扩张域 GF(2^m)。
非椭圆曲线的 Diffie-Hellman 密钥交换利用的是:
以 p 为模,已知 G 和 G^x mod p 求 x 的复杂度(有限域上的离散对数问题)
椭圆曲线的 Diffie-Hellman 密钥交换利用的是:
在椭圆曲线上,已知 G 和 xG 求 x 的复杂度(椭圆曲线上的离散对数问题)
DH 算法和 RSA 算法最大的不同在于:
举个例子来说明 ECDH 算法是如何生成共享密钥的。
窃听者一共可以拿到 3 个有效信息:G、aG、bG。但是由于“已知 G 和 xG 求 x 非常难”,导致已知 G 和 aG 无法求解出 a,已知 G 和 bG 无法求解出 b。所以最终也就无法求解出私钥 abG。
严格的来说,椭圆曲线上的离散对数问题的复杂度只能证明 “已知 G,aG,bG 难以求出 a,b”,但无法证明“已知 G,aG,bG 难以求出 abG”,后者需要另外证明。具体证明这里省略。
如果采用静态的 DH 算法和 ECC 结合就是 ECDH 算法。这种方式每次都使用的相同的 G 基点,它的优点在于可以避免每次在初始化连接时服务器频繁生成 G。这个过程比较消耗 CPU。但是它带来的缺点是,一旦随机数 a、b 被泄露了,那么在这之前的所有会话都将会被解密。
为了解决这个问题,于是出现了 DHE 算法(Diffie-Hellman Ephemeral ,短暂临时的 DH 算法),结合 ECC 后形成了 ECDHE 算法。它可以保证每次通信使用的共享密钥都是不同的,DH 密钥对仅仅保存在内存中,不像 RSA 的私钥保存在磁盘上,攻击者即使从内存中破解了私钥,也仅仅影响本次通信,所以无需担心在此之前的通信内容会被解密,这样的特征成为前向安全性(Forward Secrecy,FS)或者完全前向安全性(Perfect Forward Secrecy,PFS)。更安全的是,协商出会话密钥后,a 和 b 两个私钥可以丢弃,进一步提升了安全性,在有限的时间、有效的空间生成了密钥对。在 TLS 握手中使用的 ECDHE_ECDSA 和 ECDHE_RSA 密钥交换算法。
通过使用对称加密可以解决信息安全的问题,但是使用对称加密,必须要解决密钥配送的问题。
使用公钥密码可以解决密钥配送的问题,但是公钥密码又暴露了 2 个新的问题。
第一点缺点可以用这一章节讲的混合加密系统来解决。第二点缺点需要用到下一篇文章中的认证相关的算法来解决。
混合密码系统 (hybrid crytosystem) 是将对称密码和公钥密码的优势相结合的方法。用快速的对称密码对明文进行加密,用公钥密码对对称密码的密钥进行加密。由于对称密钥一般都比消息本身要短,这样公钥密码速度慢的问题也可以忽略。
会话密钥 (session key) 是指为本次通信而生成的临时密钥。它一般都是通过伪随机数生成器产生的。伪随机生成器所产生的会话密钥同时也会作为对称密码的密钥使用。会话密钥是对称密码的密钥,同时也是公钥密码的明文。
下图是混合密码系统中的加密流程:
值得说明的是,公钥密码的强度应该高于对称密码。因为对称密码会话密钥被破译只影响本地通信内容,但是公钥密钥被破译就会影响到过去所有通信内容,以及未来所有通信内容。
下图是混合密码系统中的解密流程:
与 RSA 密钥对生成方式不一样,DH 密钥对生成方式分为 2 步,第一步先生成参数文件,第二步再根据参数文件生成密钥对,同一个参数文件可以生成无数多且不重复的密钥对。
第一步,生成 DH 密钥对
// 生成 2048 比特的参数文件
$ openssl dhparam -out dhparam.pem -2 2048
$ openssl dhparam -in dhparam.pem -noout -C
输出
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
......................+..........................................................................+..............................................................................................................................................................................................+.......................................................+
#ifndef HEADER_DH_H
# include
#endif
DH *get_dh2048()
{
static unsigned char dhp_2048[] = {
0xBD, 0x70, 0xDB, 0x2E, 0xF9, 0x0B, 0x89, 0x37, 0xC4, 0x31,
0x93, 0x48, 0x47, 0xEF, 0xD5, 0xEA, 0x7E, 0xBC, 0xDA, 0xC8,
0x14, 0x0E, 0x82, 0xDD, 0xF6, 0xC6, 0x07, 0x2A, 0xD4, 0x97,
0xC3, 0x02, 0xA1, 0x9B, 0x02, 0xCB, 0xE4, 0xC0, 0xB9, 0x33,
0xD1, 0xBB, 0x69, 0xF0, 0xBA, 0x8C, 0x7A, 0x57, 0x1F, 0xDF,
0xD3, 0xB5, 0x2F, 0x87, 0x1E, 0xA8, 0x35, 0xE4, 0xC0, 0x94,
0xED, 0x20, 0x04, 0x26, 0x58, 0x50, 0x27, 0xFC, 0xF6, 0xE1,
0xBE, 0xC7, 0xB8, 0x7A, 0x14, 0xC1, 0x08, 0x16, 0x06, 0xC6,
0xB8, 0x09, 0xDC, 0x34, 0xEA, 0xA0, 0xD1, 0x3E, 0x88, 0xBD,
0xB3, 0xBB, 0x05, 0xFE, 0x4D, 0xCB, 0x62, 0x05, 0x9A, 0xC7,
0x00, 0xA2, 0x0B, 0x73, 0xAD, 0xDD, 0x39, 0x18, 0x9A, 0xD8,
0x2A, 0x95, 0xCE, 0xF4, 0x10, 0x6A, 0xB2, 0x5C, 0x0F, 0x9E,
0x99, 0xE5, 0xE6, 0x0D, 0x6C, 0x19, 0xF5, 0xF5, 0xDC, 0x07,
0x2D, 0xF0, 0xDE, 0xB5, 0x58, 0xEC, 0x35, 0x33, 0xEF, 0x65,
0x70, 0xC3, 0x8C, 0xBF, 0x14, 0x40, 0x4C, 0xC3, 0x47, 0x77,
0xE0, 0x5F, 0xF6, 0x61, 0x5F, 0x49, 0x35, 0xCC, 0x39, 0x75,
0x8E, 0x31, 0xA3, 0x99, 0x43, 0x61, 0xD7, 0xE3, 0xB7, 0xB8,
0x0F, 0x79, 0xF3, 0x66, 0x50, 0x95, 0x0D, 0x95, 0xB2, 0x5F,
0x1B, 0x2C, 0x9D, 0x64, 0xE0, 0x54, 0xCB, 0x27, 0xE9, 0x4A,
0x23, 0x96, 0xA0, 0x7E, 0x55, 0xD4, 0xA9, 0x93, 0x43, 0xB0,
0x69, 0xB2, 0xF5, 0xB4, 0x9D, 0x41, 0x5B, 0xD3, 0x5D, 0x4B,
0x91, 0x48, 0x9F, 0xAA, 0x54, 0xA4, 0xA1, 0x81, 0xF1, 0x59,
0xF6, 0xB3, 0x44, 0x8C, 0xE7, 0x83, 0x8F, 0x57, 0x63, 0x52,
0x58, 0x70, 0x2F, 0x0A, 0xC1, 0xC0, 0xE5, 0xDD, 0x6F, 0x4D,
0xFD, 0xC1, 0x5D, 0x61, 0x6E, 0xD0, 0xA4, 0xBD, 0x15, 0xA1,
0xA6, 0xDE, 0x61, 0xC9, 0xF4, 0x4B
};
static unsigned char dhg_2048[] = {
0x02
};
DH *dh = DH_new();
BIGNUM *dhp_bn, *dhg_bn;
if (dh == NULL)
return NULL;
dhp_bn = BN_bin2bn(dhp_2048, sizeof(dhp_2048), NULL);
dhg_bn = BN_bin2bn(dhg_2048, sizeof(dhg_2048), NULL);
if (dhp_bn == NULL || dhg_bn == NULL
|| !DH_set0_pqg(dh, dhp_bn, NULL, dhg_bn)) {
DH_free(dh);
BN_free(dhp_bn);
BN_free(dhg_bn);
return NULL;
}
return dh;
}
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAvXDbLvkLiTfEMZNIR+/V6n682sgUDoLd9sYHKtSXwwKhmwLL5MC5
M9G7afC6jHpXH9/TtS+HHqg15MCU7SAEJlhQJ/z24b7HuHoUwQgWBsa4Cdw06qDR
Poi9s7sF/k3LYgWaxwCiC3Ot3TkYmtgqlc70EGqyXA+emeXmDWwZ9fXcBy3w3rVY
7DUz72Vww4y/FEBMw0d34F/2YV9JNcw5dY4xo5lDYdfjt7gPefNmUJUNlbJfGyyd
ZOBUyyfpSiOWoH5V1KmTQ7BpsvW0nUFb011LkUifqlSkoYHxWfazRIzng49XY1JY
cC8KwcDl3W9N/cFdYW7QpL0VoabeYcn0SwIBAg==
-----END DH PARAMETERS-----
第二步,基于参数文件生成密钥对
$ openssl genpkey -paramfile dhparam.pem -out dhkey.pem
// 查看密钥对文件内容
$ openssl pkey -in dhkey.pem -text -noout
输出
DH Private-Key: (2048 bit)
private-key:
53:7c:d5:88:80:13:49:b0:c0:00:79:62:fd:4b:8d:
54:c6:b2:f3:49:45:fa:3c:f7:d8:49:cf:b2:b2:cd:
b6:45:be:b8:de:54:9b:07:7e:dc:e5:f1:92:0c:7c:
7d:b9:6e:aa:db:3e:a0:b0:e1:e8:aa:b6:6e:71:c8:
59:30:b7:75:cb:79:c8:44:25:18:36:2f:22:c2:6b:
fb:19:f6:31:7e:c8:6d:df:5f:46:cf:b9:df:41:53:
1c:5e:42:9f:46:77:29:c0:09:a0:a2:4d:e8:f7:44:
43:1a:f8:43:18:d1:2d:7f:56:75:dc:62:4f:84:93:
26:c6:fc:ca:a6:5f:50:73:d8:bc:ee:2b:1c:65:22:
4c:ab:0b:a3:a0:ce:d3:cd:1d:d9:21:3f:b3:32:f0:
74:fd:3d:98:1d:43:3b:a4:ed:e0:d2:7e:eb:27:64:
c7:64:54:e7:12:19:5d:f8:f0:a5:fc:15:50:ee:20:
8f:81:84:b5:e6:ba:64:9a:e0:36:bc:61:09:b3:b3:
ea:24:63:92:66:13:50:2d:5b:f5:db:2f:15:3d:6b:
e3:63:99:61:70:8e:b1:12:a1:c1:bb:c0:fa:57:90:
ae:47:7a:78:35:ce:dc:47:2a:63:b4:30:4a:9d:49:
4f:2b:ac:a0:8e:47:44:dc:a5:29:68:cd:c6:e0:74:
50
public-key:
00:81:0a:3e:3b:29:8a:e9:1e:26:47:e0:cc:a0:40:
e9:2d:03:c4:55:64:b0:73:2c:6e:39:6f:5a:95:de:
a3:44:6a:d1:20:f4:69:68:3c:58:8f:f5:86:94:39:
13:bf:c7:66:1f:af:7b:1e:0e:15:cd:0e:f9:65:d4:
d4:70:37:63:50:f7:7b:93:6c:bf:e2:9a:de:fa:41:
13:20:8b:23:0c:f7:b3:94:00:19:6d:65:f2:30:6a:
50:88:8f:5e:7e:40:79:fa:14:0f:73:3b:51:ed:24:
06:5d:29:f9:6e:34:7a:21:6e:b0:cf:6a:7e:c3:26:
31:3f:f5:42:47:f6:2f:45:c4:89:67:90:b4:69:0e:
73:92:57:90:32:63:be:77:9f:f1:a8:c5:e3:cd:4d:
b8:2f:13:4f:88:78:9a:09:1b:04:fa:ab:6d:0c:a7:
b8:b4:6e:76:05:42:d2:50:b7:a3:6b:0c:94:53:9f:
fa:eb:9e:20:df:e7:91:a3:08:fa:a8:be:85:6d:75:
41:89:e9:ae:1d:e4:e1:65:bc:17:f3:b3:a2:ab:45:
31:eb:9e:21:dc:45:66:0d:8b:2c:70:ea:c2:1d:ac:
27:9d:0d:d4:06:9c:b0:3d:0b:34:9e:05:2d:9c:55:
28:f4:63:7f:61:96:da:e7:09:0b:40:b4:d6:0a:5c:
aa:df
prime:
00:bd:70:db:2e:f9:0b:89:37:c4:31:93:48:47:ef:
d5:ea:7e:bc:da:c8:14:0e:82:dd:f6:c6:07:2a:d4:
97:c3:02:a1:9b:02:cb:e4:c0:b9:33:d1:bb:69:f0:
ba:8c:7a:57:1f:df:d3:b5:2f:87:1e:a8:35:e4:c0:
94:ed:20:04:26:58:50:27:fc:f6:e1:be:c7:b8:7a:
14:c1:08:16:06:c6:b8:09:dc:34:ea:a0:d1:3e:88:
bd:b3:bb:05:fe:4d:cb:62:05:9a:c7:00:a2:0b:73:
ad:dd:39:18:9a:d8:2a:95:ce:f4:10:6a:b2:5c:0f:
9e:99:e5:e6:0d:6c:19:f5:f5:dc:07:2d:f0:de:b5:
58:ec:35:33:ef:65:70:c3:8c:bf:14:40:4c:c3:47:
77:e0:5f:f6:61:5f:49:35:cc:39:75:8e:31:a3:99:
43:61:d7:e3:b7:b8:0f:79:f3:66:50:95:0d:95:b2:
5f:1b:2c:9d:64:e0:54:cb:27:e9:4a:23:96:a0:7e:
55:d4:a9:93:43:b0:69:b2:f5:b4:9d:41:5b:d3:5d:
4b:91:48:9f:aa:54:a4:a1:81:f1:59:f6:b3:44:8c:
e7:83:8f:57:63:52:58:70:2f:0a:c1:c0:e5:dd:6f:
4d:fd:c1:5d:61:6e:d0:a4:bd:15:a1:a6:de:61:c9:
f4:4b
generator: 2 (0x2)
prime 是一个大质数,generator 是生成元。
接下来举一个 DH 密钥协商的完整例子。
// 通讯一方生成 DH 参数文件,可以对外公开
$ openssl genpkey -genparam -algorithm DH -out dhp.pem
// 查看参数文件内容,包括 p 和 g 参数
$ openssl pkeyparam -in dhp.pem -text
输出
-----BEGIN DH PARAMETERS-----
MIGHAoGBAO6kIxno65uejqlc0Q+SwnpIQjc9A8DMr1r1xNk9d/qTGczGi52olR96
XfKHZMKCRih4mvFglAfisykwLGdVViiqOQrLmG0uI2WviyPKG4eHqu7gID/6+pil
RBHo6m9McsnlrcGLgV2d3a3IczNcH/g2NnVoKN6Q9+tGiZKhowZDAgEC
-----END DH PARAMETERS-----
DH Parameters: (1024 bit)
prime:
00:ee:a4:23:19:e8:eb:9b:9e:8e:a9:5c:d1:0f:92:
c2:7a:48:42:37:3d:03:c0:cc:af:5a:f5:c4:d9:3d:
77:fa:93:19:cc:c6:8b:9d:a8:95:1f:7a:5d:f2:87:
64:c2:82:46:28:78:9a:f1:60:94:07:e2:b3:29:30:
2c:67:55:56:28:aa:39:0a:cb:98:6d:2e:23:65:af:
8b:23:ca:1b:87:87:aa:ee:e0:20:3f:fa:fa:98:a5:
44:11:e8:ea:6f:4c:72:c9:e5:ad:c1:8b:81:5d:9d:
dd:ad:c8:73:33:5c:1f:f8:36:36:75:68:28:de:90:
f7:eb:46:89:92:a1:a3:06:43
generator: 2 (0x2)
发送方 Alice 基于参数文件生成一个密钥对
$ openssl genpkey -paramfile dhp.pem -out akey.pem
// 查看密钥对内容
$ openssl pkey -in akey.pem -text -noout
输出
DH Private-Key: (1024 bit)
private-key:
79:1f:07:19:83:8d:34:db:4a:ea:d6:7b:16:4f:7e:
11:cd:39:78:75:ea:4f:c3:f7:09:48:91:3c:96:cf:
6b:8d:85:56:48:9e:1a:83:fb:13:0a:28:c5:f6:eb:
74:13:ff:a4:2e:ae:29:df:b5:92:2a:af:25:11:cf:
02:b6:09:d5:c8:a1:71:78:86:dd:26:6a:d1:55:52:
33:ce:65:57:22:3f:f4:df:a6:df:80:c2:cc:c6:98:
de:67:09:05:e2:68:09:d1:43:7f:42:03:37:99:88:
cc:5f:78:e6:22:b0:fa:5c:c9:5f:0f:2d:de:b6:33:
42:05:f4:45:58:b8:fb:93
public-key:
4d:90:d3:5f:df:13:fb:17:32:55:6c:3b:17:67:cb:
51:8f:42:1c:88:10:ef:7b:90:69:2c:e6:97:92:10:
92:4f:16:8d:d5:9b:4f:d5:97:fa:71:3b:28:12:99:
1f:86:0d:ee:b2:ca:7e:4f:96:ae:46:73:1d:14:b5:
0e:1a:0c:e6:41:fa:97:d4:0c:50:a2:17:5d:a9:e8:
7e:c0:33:fd:dc:59:ca:13:90:f7:1b:79:ee:88:5e:
39:70:4f:c0:a8:a7:0b:0e:6e:e8:27:29:43:1b:af:
56:72:0a:aa:a1:06:1d:0d:78:f8:6d:1a:1e:bd:b5:
3b:20:55:76:bc:9c:1a:d3
prime:
00:ee:a4:23:19:e8:eb:9b:9e:8e:a9:5c:d1:0f:92:
c2:7a:48:42:37:3d:03:c0:cc:af:5a:f5:c4:d9:3d:
77:fa:93:19:cc:c6:8b:9d:a8:95:1f:7a:5d:f2:87:
64:c2:82:46:28:78:9a:f1:60:94:07:e2:b3:29:30:
2c:67:55:56:28:aa:39:0a:cb:98:6d:2e:23:65:af:
8b:23:ca:1b:87:87:aa:ee:e0:20:3f:fa:fa:98:a5:
44:11:e8:ea:6f:4c:72:c9:e5:ad:c1:8b:81:5d:9d:
dd:ad:c8:73:33:5c:1f:f8:36:36:75:68:28:de:90:
f7:eb:46:89:92:a1:a3:06:43
generator: 2 (0x2)
接收方 Bob 也基于参数文件生成一个密钥对
$ openssl genpkey -paramfile dhp.pem -out bkey.pem
// 查看密钥对内容
$ openssl pkey -in bkey.pem -text -noout
输出
PKCS#3 DH Private-Key: (1024 bit)
private-key:
7b:00:1e:c9:12:62:3a:82:69:58:03:5c:f5:23:ef:
c2:20:10:27:f5:a7:e3:c9:14:57:76:ea:94:4c:68:
e5:17:c2:38:36:e1:82:9d:fb:d2:97:04:44:4a:0c:
9f:81:d3:4a:33:c8:8f:64:50:79:6c:cd:a6:3a:26:
d0:2b:55:7d:78:b0:e7:1e:12:d8:a3:89:da:61:53:
1e:d0:9a:9f:f6:c0:ce:7d:c9:25:50:01:cf:c2:cb:
0d:e2:74:78:3c:58:28:71:38:f0:d3:d2:91:f4:28:
ec:da:f2:f9:75:e6:c4:13:8c:97:2a:f9:a3:fd:c0:
0e:cb:d0:17:7d:62:01:c4
public-key:
00:8c:ad:e1:2f:42:85:4f:f8:07:7e:15:d0:f9:c5:
84:b4:a3:be:9d:b2:fb:57:86:b8:c7:0c:d3:d5:92:
c1:7b:db:e6:e6:b2:7e:ff:db:95:d9:8b:10:1a:10:
4d:59:ac:b2:00:43:6f:23:f6:aa:13:b6:e4:f4:04:
92:f1:23:88:13:21:c9:f7:d2:f3:dc:0e:a6:75:20:
ed:4c:61:29:55:a2:36:55:d3:04:9f:7d:c3:96:7d:
be:62:44:83:bf:9e:b3:f8:e7:ac:88:c5:3e:9b:b7:
01:52:63:1a:30:5f:5d:90:46:5b:06:06:f3:b4:f8:
c4:4b:16:45:43:da:d5:6d:1e
prime:
00:ee:a4:23:19:e8:eb:9b:9e:8e:a9:5c:d1:0f:92:
c2:7a:48:42:37:3d:03:c0:cc:af:5a:f5:c4:d9:3d:
77:fa:93:19:cc:c6:8b:9d:a8:95:1f:7a:5d:f2:87:
64:c2:82:46:28:78:9a:f1:60:94:07:e2:b3:29:30:
2c:67:55:56:28:aa:39:0a:cb:98:6d:2e:23:65:af:
8b:23:ca:1b:87:87:aa:ee:e0:20:3f:fa:fa:98:a5:
44:11:e8:ea:6f:4c:72:c9:e5:ad:c1:8b:81:5d:9d:
dd:ad:c8:73:33:5c:1f:f8:36:36:75:68:28:de:90:
f7:eb:46:89:92:a1:a3:06:43
generator: 2 (0x2)
Alice 拆出公钥文件 akey_pub.pem,私钥自己保存
$ openssl pkey -in akey.pem -pubout -out akey_pub.pem
输出
-----BEGIN PUBLIC KEY-----
MIIBHzCBlQYJKoZIhvcNAQMBMIGHAoGBAO6kIxno65uejqlc0Q+SwnpIQjc9A8DM
r1r1xNk9d/qTGczGi52olR96XfKHZMKCRih4mvFglAfisykwLGdVViiqOQrLmG0u
I2WviyPKG4eHqu7gID/6+pilRBHo6m9McsnlrcGLgV2d3a3IczNcH/g2NnVoKN6Q
9+tGiZKhowZDAgECA4GEAAKBgE2Q01/fE/sXMlVsOxdny1GPQhyIEO97kGks5peS
EJJPFo3Vm0/Vl/pxOygSmR+GDe6yyn5Plq5Gcx0UtQ4aDOZB+pfUDFCiF12p6H7A
M/3cWcoTkPcbee6IXjlwT8CopwsObugnKUMbr1ZyCqqhBh0NePhtGh69tTsgVXa8
nBrT
-----END PUBLIC KEY-----
Bob 拆出公钥文件 bkey_pub.pem,私钥自己保存
$ openssl pkey -in bkey.pem -pubout -out bkey_pub.pem
输出
-----BEGIN PUBLIC KEY-----
MIIBIDCBlQYJKoZIhvcNAQMBMIGHAoGBAO6kIxno65uejqlc0Q+SwnpIQjc9A8DM
r1r1xNk9d/qTGczGi52olR96XfKHZMKCRih4mvFglAfisykwLGdVViiqOQrLmG0u
I2WviyPKG4eHqu7gID/6+pilRBHo6m9McsnlrcGLgV2d3a3IczNcH/g2NnVoKN6Q
9+tGiZKhowZDAgECA4GFAAKBgQCMreEvQoVP+Ad+FdD5xYS0o76dsvtXhrjHDNPV
ksF72+bmsn7/25XZixAaEE1ZrLIAQ28j9qoTtuT0BJLxI4gTIcn30vPcDqZ1IO1M
YSlVojZV0wSffcOWfb5iRIO/nrP456yIxT6btwFSYxowX12QRlsGBvO0+MRLFkVD
2tVtHg==
-----END PUBLIC KEY-----
Alice 收到 Bob 他的公钥,并将协商出来的密钥保存到 data_a.txt 中
$ openssl pkeyutl -derive -inkey akey.pem -peerkey bkey_pub.pem -out data_a.txt
输出
b;[nQ?�!zX^m��4���Է�M<{|1X?�M3�NT�Łm�Ϧ\��X+<����(L�(�F��l5Yl��N���/o���v�#7���J`>�?R�,���!>G������
��>B�����$�-��Y
对应二进制
$ od -d data_a.txt
0000000 25098 23355 20846 16134 8602 32634 24152 59757
0000020 59416 57652 52209 47060 19942 31548 12668 16216
0000040 19901 56883 21582 50641 28033 53186 23718 55494
0000060 11096 35644 61385 10435 33612 54568 51014 27841
0000100 22837 44908 5587 49486 61576 12198 57967 64511
0000120 60534 56867 14088 38019 19100 15968 16283 57938
0000140 60972 57071 15905 583 49404 47506 61861 47628
0000160 5292 16958 38029 62234 57799 39460 43821 38391
0000200
Bob 收到 Alice 他的公钥,并将协商出来的密钥保存到 data_b.txt 中
$ openssl pkeyutl -derive -inkey bkey.pem -peerkey akey_pub.pem -out data_b.txt
输出
b;[nQ?�!zX^m��4���Է�M<{|1X?�M3�NT�Łm�Ϧ\��X+<����(L�(�F��l5Yl��N���/o���v�#7���J`>�?R�,���!>G������
��>B�����$�-��Y
对应二进制
$ od -d data_b.txt
0000000 25098 23355 20846 16134 8602 32634 24152 59757
0000020 59416 57652 52209 47060 19942 31548 12668 16216
0000040 19901 56883 21582 50641 28033 53186 23718 55494
0000060 11096 35644 61385 10435 33612 54568 51014 27841
0000100 22837 44908 5587 49486 61576 12198 57967 64511
0000120 60534 56867 14088 38019 19100 15968 16283 57938
0000140 60972 57071 15905 583 49404 47506 61861 47628
0000160 5292 16958 38029 62234 57799 39460 43821 38391
0000200
可以发现两者最后协商出来的密钥是完全一致的。
OpenSSL 中支持很多命名的曲线,先来看看支持的曲线有哪些。
$ openssl ecparam -list_curves
输出
secp112r1 : SECG/WTLS curve over a 112 bit prime field
secp112r2 : SECG curve over a 112 bit prime field
secp128r1 : SECG curve over a 128 bit prime field
secp128r2 : SECG curve over a 128 bit prime field
secp160k1 : SECG curve over a 160 bit prime field
secp160r1 : SECG curve over a 160 bit prime field
secp160r2 : SECG/WTLS curve over a 160 bit prime field
secp192k1 : SECG curve over a 192 bit prime field
secp224k1 : SECG curve over a 224 bit prime field
secp224r1 : NIST/SECG curve over a 224 bit prime field
secp256k1 : SECG curve over a 256 bit prime field
secp384r1 : NIST/SECG curve over a 384 bit prime field
secp521r1 : NIST/SECG curve over a 521 bit prime field
prime192v1: NIST/X9.62/SECG curve over a 192 bit prime field
prime192v2: X9.62 curve over a 192 bit prime field
prime192v3: X9.62 curve over a 192 bit prime field
prime239v1: X9.62 curve over a 239 bit prime field
prime239v2: X9.62 curve over a 239 bit prime field
prime239v3: X9.62 curve over a 239 bit prime field
prime256v1: X9.62/SECG curve over a 256 bit prime field
sect113r1 : SECG curve over a 113 bit binary field
sect113r2 : SECG curve over a 113 bit binary field
sect131r1 : SECG/WTLS curve over a 131 bit binary field
sect131r2 : SECG curve over a 131 bit binary field
sect163k1 : NIST/SECG/WTLS curve over a 163 bit binary field
sect163r1 : SECG curve over a 163 bit binary field
sect163r2 : NIST/SECG curve over a 163 bit binary field
sect193r1 : SECG curve over a 193 bit binary field
sect193r2 : SECG curve over a 193 bit binary field
sect233k1 : NIST/SECG/WTLS curve over a 233 bit binary field
sect233r1 : NIST/SECG/WTLS curve over a 233 bit binary field
sect239k1 : SECG curve over a 239 bit binary field
sect283k1 : NIST/SECG curve over a 283 bit binary field
sect283r1 : NIST/SECG curve over a 283 bit binary field
sect409k1 : NIST/SECG curve over a 409 bit binary field
sect409r1 : NIST/SECG curve over a 409 bit binary field
sect571k1 : NIST/SECG curve over a 571 bit binary field
sect571r1 : NIST/SECG curve over a 571 bit binary field
c2pnb163v1: X9.62 curve over a 163 bit binary field
c2pnb163v2: X9.62 curve over a 163 bit binary field
c2pnb163v3: X9.62 curve over a 163 bit binary field
c2pnb176v1: X9.62 curve over a 176 bit binary field
c2tnb191v1: X9.62 curve over a 191 bit binary field
c2tnb191v2: X9.62 curve over a 191 bit binary field
c2tnb191v3: X9.62 curve over a 191 bit binary field
c2pnb208w1: X9.62 curve over a 208 bit binary field
c2tnb239v1: X9.62 curve over a 239 bit binary field
c2tnb239v2: X9.62 curve over a 239 bit binary field
c2tnb239v3: X9.62 curve over a 239 bit binary field
c2pnb272w1: X9.62 curve over a 272 bit binary field
c2pnb304w1: X9.62 curve over a 304 bit binary field
c2tnb359v1: X9.62 curve over a 359 bit binary field
c2pnb368w1: X9.62 curve over a 368 bit binary field
c2tnb431r1: X9.62 curve over a 431 bit binary field
wap-wsg-idm-ecid-wtls1: WTLS curve over a 113 bit binary field
wap-wsg-idm-ecid-wtls3: NIST/SECG/WTLS curve over a 163 bit binary field
wap-wsg-idm-ecid-wtls4: SECG curve over a 113 bit binary field
wap-wsg-idm-ecid-wtls5: X9.62 curve over a 163 bit binary field
wap-wsg-idm-ecid-wtls6: SECG/WTLS curve over a 112 bit prime field
wap-wsg-idm-ecid-wtls7: SECG/WTLS curve over a 160 bit prime field
wap-wsg-idm-ecid-wtls8: WTLS curve over a 112 bit prime field
wap-wsg-idm-ecid-wtls9: WTLS curve over a 160 bit prime field
wap-wsg-idm-ecid-wtls10: NIST/SECG/WTLS curve over a 233 bit binary field
wap-wsg-idm-ecid-wtls11: NIST/SECG/WTLS curve over a 233 bit binary field
wap-wsg-idm-ecid-wtls12: WTLS curve over a 224 bit prime field
Oakley-EC2N-3:
IPSec/IKE/Oakley curve #3 over a 155 bit binary field.
Not suitable for ECDSA.
Questionable extension field!
Oakley-EC2N-4:
IPSec/IKE/Oakley curve #4 over a 185 bit binary field.
Not suitable for ECDSA.
Questionable extension field!
brainpoolP160r1: RFC 5639 curve over a 160 bit prime field
brainpoolP160t1: RFC 5639 curve over a 160 bit prime field
brainpoolP192r1: RFC 5639 curve over a 192 bit prime field
brainpoolP192t1: RFC 5639 curve over a 192 bit prime field
brainpoolP224r1: RFC 5639 curve over a 224 bit prime field
brainpoolP224t1: RFC 5639 curve over a 224 bit prime field
brainpoolP256r1: RFC 5639 curve over a 256 bit prime field
brainpoolP256t1: RFC 5639 curve over a 256 bit prime field
brainpoolP320r1: RFC 5639 curve over a 320 bit prime field
brainpoolP320t1: RFC 5639 curve over a 320 bit prime field
brainpoolP384r1: RFC 5639 curve over a 384 bit prime field
brainpoolP384t1: RFC 5639 curve over a 384 bit prime field
brainpoolP512r1: RFC 5639 curve over a 512 bit prime field
brainpoolP512t1: RFC 5639 curve over a 512 bit prime field
SM2 : SM2 curve over a 256 bit prime field
生成一个参数文件,通过 -name 指定命名曲线
$ openssl ecparam -name secp256k1 -out secp256k1.pem
// 查看参数文件,下面这句话默认只打印曲线名字
$ openssl ecparam -in secp256k1.pem -text -noout
输出
ASN1 OID: secp256k1
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
显示参数文件内具体参数
// 显示参数文件内具体参数
$ openssl ecparam -in secp256k1.pem -text -param_enc explicit -noout
输出
Field Type: prime-field
Prime:
00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff:
ff:fc:2f
A: 0
B: 7 (0x7)
Generator (uncompressed):
04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87:
0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16:
f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc:
0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0:
8f:fb:10:d4:b8
Order:
00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0:
36:41:41
Cofactor: 1 (0x1)
参数解释:
在 ECC 中,k 就是私钥,g 就是基点,kg 基于公式运算最终得到公钥,通过公钥很难计算出私钥 k,其背后有复杂的数学理论,即离散对数问题。
接下来举一个 ECDH 密钥协商的完整例子。
// 生成参数文件
$ openssl ecparam -name secp256k1 -out secp256k1.pem
输出
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
Alice 生成 EDCH 私钥对文件
$ openssl genpkey -paramfile secp256k1.pem -out akey.pem
输出
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgXVTzOp6aEkAeC2ICiPTi
bmAZkr1RYtzprh3Larg4n/yhRANCAATbDBxnW/pTc6fS1Rk/bJEZZLp0fBGfn+H8
hYJILjrQu7ZORRlQqe+o2le1+OV2t/l3bEGOYAI8nvHOhT6weN/e
-----END PRIVATE KEY-----
Bob 生成 EDCH 私钥对文件
$ openssl genpkey -paramfile secp256k1.pem -out bkey.pem
输出
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgjhUWgm7C3tcXTUcqX9RG
aHCJQ5BqF0OzCBYntGnhJwahRANCAARPI4E54N3VfbHTfcvoUXNGJrIcMHAEhj6t
TkKrpEYMCbPkM0g57C2DWcOIqvb9ImD8OYQ+N27LlVNhYt6ncD/v
-----END PRIVATE KEY-----
Alice 分解出公钥,将公钥发送给 Bob
$ openssl pkey -in akey.pem -pubout -out akey_pub.pem
输出
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2wwcZ1v6U3On0tUZP2yRGWS6dHwRn5/h
/IWCSC460Lu2TkUZUKnvqNpXtfjldrf5d2xBjmACPJ7xzoU+sHjf3g==
-----END PUBLIC KEY-----
Bob 分解出公钥,将公钥发送给 Alice
$ openssl pkey -in bkey.pem -pubout -out bkey_pub.pem
输出
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAETyOBOeDd1X2x033L6FFzRiayHDBwBIY+
rU5Cq6RGDAmz5DNIOewtg1nDiKr2/SJg/DmEPjduy5VTYWLep3A/7w==
-----END PUBLIC KEY-----
Alice 收到 Bob 他的公钥,并将协商出来的密钥保存到 data_a.txt 中
$ openssl pkeyutl -derive -inkey akey.pem -peerkey bkey_pub.pem -out data_a.txt
输出
��tR�"��g����_������]�Vn�Uɺ
对应二进制
$ od -d data_a.txt
0000000 38303 21108 8888 49924 26595 54407 63697 57439
0000020 61643 63942 37357 63837 28246 3817 5973 47817
0000040
Bob 收到 Alice 他的公钥,并将协商出来的密钥保存到 data_b.txt 中
$ openssl pkeyutl -derive -inkey bkey.pem -peerkey akey_pub.pem -out data_b.txt
输出
��tR�"��g����_������]�Vn�Uɺ
对应二进制
$ od -d data_a.txt
0000000 38303 21108 8888 49924 26595 54407 63697 57439
0000020 61643 63942 37357 63837 28246 3817 5973 47817
0000040
可以发现两者最后协商出来的密钥是完全一致的