OpenVPN 的 EC 与 ED:为何这些名字不是一条强度阶梯
EC、ED、ECDHE 与 auth 回答的是不同的问题。在比较或改动任何东西之前,先按每个名字所承担的工作把 OpenVPN 配置归位。
把 ec、ed、ecdh、ecdsa、auth 和一串 TLS 密码套件并排放在同一份配置里,文件看上去就像一只带六个档位的强度旋钮。事实并非如此。每个名字回答的是不同的问题,两个只在其中一项上不同的配置,并不必然一个比另一个更强或更弱。按名字各自承担的工作把它们归位,大约只要五分钟,却能挡掉人们对一份本来能用的配置做出的大多数糟糕改动。
下文从混淆本身入手,再向外扩展到配置中真正决定什么在保护你流量的那些部分。每一节处理一种误读,并说明那个名字实际上挂靠在什么之上。
两个字母,两个不同的问题
争论最多的就是 EC 与 ED 这一对,而争辩通常假定它们是对同一个问题的两种竞争答案。它们甚至不在同一条轴上。EC 描述的是一类数学:椭圆曲线密码学,其安全性来自在曲线上求解离散对数的困难,而不是在某个大有限域里求解的困难。
ED 描述的是这一族之中的一种曲线形状。扭曲爱德华兹曲线是一种为速度和抗若干实现错误而选用的表示法,Ed25519 则是人们通常所指的那个名字。这种曲线并不是椭圆曲线密码学的竞争者;它只是书写一条椭圆曲线的一种方式。把 ED 读成“更新、更强的 EC”是第一个错误的转弯,此后的种种往往都朝同一个方向错下去。
签名与密钥交换是两份不同的工作
家族问题一旦厘清,剩下的名字就分成两列,彼此从不互相替代。一列负责签名,另一列负责就一个共享密钥达成一致。
| Name in the profile | Job it does | What it is not |
|---|---|---|
ecdsa | Signature scheme using elliptic-curve keys | Not a key-exchange method |
eddsa, Ed25519 | Signature scheme for twisted Edwards curves | Not interchangeable with ECDSA |
ecdh, X25519 | Ephemeral key agreement | Not a signature and not a cipher |
auth | HMAC digest for packet authentication | Not the data cipher |
EdDSA 与 ECDSA 都做签名,但它们是不同的方案,密钥格式也不同,因此 为其中一种签发的证书不能当作另一种使用。EdDSA 只为一小部分曲线定义;P-256 不在其中,这就是为什么 P-256 证书只能用 ECDSA 验证,永远无法用 Ed25519 验证。
交换那一列没有叫 EdDH 的对应物。在 Curve25519 密钥上进行的临时协商是 X25519,而它就是采用某条特定曲线的 ECDH,与在 P-256 上完全一样。Ed25519 严格说来只指签名方案,不过由于两条曲线彼此相关,这个名字也常被宽松地用于交换:签名所用的爱德华兹形式与 X25519 所用的蒙哥马利形式描述的是同一个底层群。
为什么 EC 配置没有 dh 文件
一份 dh 文件里装着为有限域 Diffie-Hellman 预先生成的参数,之所以需要这些参数,是因为大素数和生成元总得有个来源。椭圆曲线交换不需要它们:具名曲线本身就是参数集,双方早已知道。这正是 EC 配置可以省掉该指令的全部原因。
有两种情况仍会在一份看上去很现代的配置里留下 dh 行。第一种是第二个监听器,或一个确实协商有限域群的较旧对端。第二种是从 RSA 时代的模板里抄来、却始终没有删掉的残留文字。一份为交换指定了曲线、却仍然加载 2048 位 dh 文件的配置,并不会因此更安全;它只是带着一件用不上的旧物。
TLS 1.3 把密钥交换从名称里拿掉了
把 TLS_AES_256_GCM_SHA384 与 TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 放在一起比较,短的那个名字看上去像是一次降级。它只是一次改名,原因在于结构:TLS 1.3 移除了静态 RSA 和有限域 Diffie-Hellman 密钥交换,因此临时椭圆曲线交换成了仅剩的非 PSK 选项。若在每个套件名里都写上它,同一件事就要重复一打遍。
证书类型从名称中消失,原因与此相关。在 1.3 里,证书认证的是握手签名,而不是密钥协商,因此它不再决定套件。这也正是配置中要为 1.3 单独准备一份密码列表的原因:套件是被改名和重组了,而不是被削弱了。
有一个后果值得直说。ECDHE 是一种密钥交换算法,从来不是密码算法。一个被描述为“椭圆曲线”的套件,说明的是两端如何得到一个共享密钥,对于此后用什么加密载荷则只字未提。
auth 认证的是数据包,不是载荷
指令 auth 选择的是用来认证数据包的 HMAC 摘要,它的历史默认值是 SHA1。在使用非 AEAD 数据密码时,OpenVPN 会对密文计算摘要并附加在其后,这种先加密再 MAC 的安排,正是让填充操纵变得不切实际的原因。
这里的迷思在于:把 auth 提到 SHA512 就能加固隧道。它并不会提高加密强度;而如果协商出的数据密码是 AES-256-GCM 这类 AEAD 模式,摘要根本不会被施加到数据包上。GCM 标签本身已经在认证数据包,auth 的取值只留给控制通道的 HMAC,以及任何仍在使用的非 AEAD 密码。
所以这个指令确实重要,但它起作用的位置比它在文件中占的位置要窄。查清实际协商的是哪种密码,才能知道它到底有没有生效。
按列读配置,而不是按等级读
三个问题能把这几层干净地分开,每个问题由协商会话中不同的部分回答。哪种密码在保护载荷?每个数据包由哪个标签或摘要作保,而所用的密码究竟需不需要这样一个标签?谁的证书结束了握手,控制通道是否还有 tls-auth 或 tls-crypt 额外把守?
EC 或 ED 这个标签本身定不了这三者中的任何一个,单看一个套件名同样一个也定不了。唯有协商结果才是证据,所以去读它,而不是从词汇里推断强度:
openssl x509 -in client.crt -noout -text | grep -E 'Signature Algorithm|Public Key Algorithm'
grep -i 'control channel' /var/log/openvpn.log
第一条命令告诉你,你的证书实际用的是哪种签名方案,这是判断自己究竟在跑 ECDSA 还是 Ed25519 的唯一可靠办法。第二条会显示客户端为这次会话记录的控制通道行,其中包含 TLS 版本和最终选定的套件。如果一次配置改动无法由这两条输出中的任何一条来支撑,那它多半只是改了个名字,而不是改了某项属性。