介绍
在这个实验中,你将探索 Diffie-Hellman (DH) 密钥交换的基础知识,它是现代密码学的一个基石。DH 协议允许两个事先互不了解的通信方,在不安全的通信信道上共同建立一个共享密钥。这个共享密钥随后可用于使用对称密钥密码算法来加密后续的通信。
你将使用强大的 openssl 命令行工具来模拟整个 DH 过程。这包括生成公共参数、为两个独立方创建私钥和公钥,以及最后推导出并验证共享密钥。完成这个实验后,你将对这个基本的密码学握手是如何工作的有一个实际的理解。
迪菲 - 赫尔曼密钥交换原理
在这一步,我们将介绍 Diffie-Hellman 密钥交换的理论原理。此步骤中无需执行任何命令;目标是在实现之前理解这个概念。
该过程如下:
公共参数的协商: 两个通信方,我们称之为 Alice 和 Bob,首先协商两个公共数字:一个大素数 $p$(模数)和一个基数 $g$(生成元)。这些数字不是秘密,可以通过不安全的信道传输。
私钥生成:
- Alice 选择一个秘密的私有整数 $a$。她将此数字保密。
- Bob 选择一个秘密的私有整数 $b$。他将此数字保密。
公钥计算:
- Alice 使用公式 $A = g^a \mod p$ 计算她的公钥 $A$。她将此公钥 $A$ 发送给 Bob。
- Bob 使用公式 $B = g^b \mod p$ 计算他的公钥 $B$。他将此公钥 $B$ 发送给 Alice。
共享密钥推导:
- Alice 接收到 Bob 的公钥 $B$,并使用她自己的私钥 $a$ 计算共享密钥 $S$:$S = B^a \mod p$。
- Bob 接收到 Alice 的公钥 $A$,并使用他自己的私钥 $b$ 计算共享密钥 $S$:$S = A^b \mod p$。
由于模运算的特性,Alice 和 Bob 都会得到完全相同的 $S$ 值。
- Alice 的计算:$S = (g^b \mod p)^a \mod p = g^{ba} \mod p$
- Bob 的计算:$S = (g^a \mod p)^b \mod p = g^{ab} \mod p$
信道上的窃听者可以看到 $p$、$g$、$A$ 和 $B$,但无法轻易计算出私钥 $a$ 或 $b$。这种难度被称为离散对数问题 (discrete logarithm problem),它是 Diffie-Hellman 交换安全性的基础。
在接下来的步骤中,你将使用 openssl 来执行这些操作。
生成迪菲 - 赫尔曼参数
在这一步,你将生成双方都将使用的公共 DH 参数(p 和 g)。openssl 工具可以为你处理寻找合适的素数和生成元所需的复杂数学运算。本实验中的所有操作都将在默认的 ~/project 目录下执行。
使用 openssl dhparam 命令来生成这些参数。我们将指定 2048 位的密钥长度,这是此目的下常见且安全的长度。
在你的终端中执行以下命令:
openssl dhparam -out dhparam.pem 2048
我们来解析一下这个命令:
openssl dhparam: 这调用了 OpenSSL 中用于 DH 参数管理的工具。-out dhparam.pem: 此标志指定输出应保存到名为dhparam.pem的文件中。2048: 这是素数模数p所需的比特长度。
由于它需要搜索一个强素数,此命令可能需要一分钟才能完成。在它运行时,你将看到类似以下的输出:
Generating DH parameters, 2048 bit long safe prime
完成后,当前目录下会创建一个名为 dhparam.pem 的文件。该文件包含了双方在密钥交换中将使用的公共参数。
生成 A 方密钥
在这一步,你将模拟第一方 "A 方" 的操作。A 方将使用上一步中共享的 DH 参数来生成自己的私钥,然后推导出相应的公钥。
首先,生成 A 方的私钥。这个密钥是我们理论示例中的秘密数字 $a$。我们将使用 openssl genpkey 命令,这是一个通用的密钥生成工具。
执行以下命令生成 A 方的私钥:
openssl genpkey -paramfile dhparam.pem -out a_private_key.pem
genpkey: 用于生成私钥的命令。-paramfile dhparam.pem: 这告诉genpkey使用dhparam.pem文件中的参数来创建 DH 密钥。-out a_private_key.pem: 这会将生成的私钥保存到文件a_private_key.pem中。
接下来,A 方需要从私钥中推导出其公钥。公钥是 A 方将通过不安全信道发送给 B 方的内容。
执行此命令来提取公钥:
openssl pkey -in a_private_key.pem -pubout -out a_public_key.pem
pkey: 用于管理公钥和私钥的命令。-in a_private_key.pem: 指定输入的私钥。-pubout: 此标志指示命令输出密钥的公有部分。-out a_public_key.pem: 将结果公钥保存到a_public_key.pem。
现在你有两个新文件:A 方必须保密的 a_private_key.pem,以及 A 方将发送给 B 方的 a_public_key.pem。
生成 B 方密钥
在这一步,你将为第二方 "B 方" 执行相同的操作。B 方独立于 A 方运作,但使用来自 dhparam.pem 的相同公共 DH 参数。
首先,生成 B 方的私钥。这对应于我们理论示例中的秘密数字 $b$。
执行以下命令:
openssl genpkey -paramfile dhparam.pem -out b_private_key.pem
此命令的结构与 A 方的命令相同,但我们将输出保存到 b_private_key.pem 以作区分。
接下来,就像 A 方一样,B 方必须从其新生成的私钥中推导出其公钥。
执行此命令来提取 B 方的公钥:
openssl pkey -in b_private_key.pem -pubout -out b_public_key.pem
在真实世界的交换中,到此阶段,A 方将拥有 B 方的公钥(b_public_key.pem),而 B 方将拥有 A 方的公钥(a_public_key.pem)。双方都已将各自的私钥保密。你现在已经成功模拟了协议的密钥生成和交换部分。
计算共享密钥
这是最后也是最重要的一步。在这里,双方将使用自己的私钥和对方的公钥来独立计算共享密钥。如果协议成功,他们将得到完全相同的密钥值。
首先,我们从 A 方的角度计算共享密钥。A 方使用其私钥(a_private_key.pem)和 B 方的公钥(b_public_key.pem)。
运行以下命令:
openssl pkeyutl -derive -inkey a_private_key.pem -peerkey b_public_key.pem -out a_shared_secret.bin
pkeyutl: 用于执行公钥操作的工具。-derive: 此操作告诉工具派生一个共享密钥。-inkey a_private_key.pem: 指定 A 方自己的私钥。-peerkey b_public_key.pem: 指定另一方("对等方")的公钥。-out a_shared_secret.bin: 将结果二进制密钥保存到文件中。
接下来,从 B 方的角度计算共享密钥。B 方使用其私钥(b_private_key.pem)和 A 方的公钥(a_public_key.pem)。
运行以下命令:
openssl pkeyutl -derive -inkey b_private_key.pem -peerkey a_public_key.pem -out b_shared_secret.bin
现在,你有两个文件:a_shared_secret.bin 和 b_shared_secret.bin。为了验证密钥交换的成功,这两个文件必须完全相同。你可以使用 cmp(比较)命令来检查这一点。
cmp a_shared_secret.bin b_shared_secret.bin
如果文件相同,此命令将不产生任何输出并静默退出。这种静默表示成功!
为了获得更直观的确认,你也可以计算两个文件的加密哈希值。哈希值必须匹配。
sha256sum *.bin
你应该看到一个输出,其中两个文件的 SHA256 哈希值完全相同。实际的哈希值在每次运行时都会有所不同,但对于 a_shared_secret.bin 和 b_shared_secret.bin 来说,它们必须相同,以证明双方派生了相同的共享密钥:
e3705a4ab5ae5d86f59dfe968f0177b49d5144e2d731dbd8d41b2eda318412ec a_shared_secret.bin
e3705a4ab5ae5d86f59dfe968f0177b49d5144e2d731dbd8d41b2eda318412ec b_shared_secret.bin
恭喜你,你已经成功执行了一次 Diffie-Hellman 密钥交换!
总结
在这个实验中,你已经成功地使用 openssl 命令行工具模拟了一个完整的 Diffie-Hellman 密钥交换。你获得了关于这一关键加密协议基本步骤的实践经验。
你学会了如何:
- 生成共享的公共 DH 参数(
openssl dhparam)。 - 基于这些参数为两个独立方创建私钥和公钥对(
openssl genpkey和openssl pkey)。 - 从自己的私钥和对等方的公钥中派生共享密钥(
openssl pkeyutl -derive)。 - 验证双方是否独立计算出了完全相同的密钥,从而证明密钥交换的成功。
这个过程是像 TLS/SSL 这样的安全通信系统的基础构建模块,它们保护着互联网上的数据。你现在对双方如何在不安全的网络上建立安全的通信通道有了实际的理解。



