两端都写着 AES-128,隧道却依然无法建立。CBC 与 GCM 是两种不同的变换,只是恰好共用同一密钥长度;只提供其中一种的客户端,无法满足只接受另一种的网关。AES 这个词描述的是一族算法,而不是一份约定。

还有第二条轴线,而制造那些令人困惑的错误的正是它。

两个层次,两份独立的列表

IKE 保护协商本身,而子关联保护你真正关心的流量。Windows 把前者暴露为加密方法和完整性方法,把后者暴露为密码与认证变换常量。这些字段并不是彼此的别名,在一个字段中有效的值,在另一个字段中可能毫无意义甚至是错的。带着这种划分去读配置文件,大部分困惑就会消失。

实际后果是:你完全可能拥有一份无可挑剔的密钥交换策略,以及一份对端不会接受的数据通道策略。一份列表负责协商,另一份负责保护数据包。改动前者的值不会影响后者,无论两者在配置对话框里看起来多么相似。

在拿两份列表与网关比对之前,先把它们写下来。凭记忆比对,结果往往是一个字段被改了两遍,另一个从未被碰过。把网关控制台开在配置文件旁边。

把错误放在提议旁边读

失败的阶段会告诉你该检查哪一份列表。 初始密钥交换阶段的拒绝,属于 IKE 提议问题,而且它发生在任何子关联存在之前。受保护流量关联协商期间的失败,属于 ESP 提议问题,此时密钥交换已经成功。客户端给出的通用策略不匹配消息不会指明属于哪个阶段,所以真正有用的是网关自己的视角。

swanctl --list-conns
swanctl --list-sas
journalctl -u strongswan -n 60 --no-pager

对比收到的提议、配置的提议与最终选中的提议 针对那一次尝试。这里有三份不同的列表,只有最后一份描述实际使用的内容。在工单里请逐字引用它,而不是概述。

把 CBC 与 GCM 保留为彼此独立的备选

若网关被有意允许接受任一算法族,它需要两条提议,而不是一条。在 strongSwan 中,逗号分隔备选,连字符连接单个套件的组成部分。用连字符把两个算法族拼在一起,得到的是一条不可能的提议,而不是两条可接受的提议。

proposals = aes128gcm16-prfsha256-ecp256, aes128-sha256-ecp256
esp_proposals = aes128gcm16-ecp256

第一条备选带有认证加密,却仍然要写明伪随机函数,因为即使完整性由密码提供,密钥派生也需要它。第二条备选是经典加密,另配独立的完整性算法。两者都仍受限于已加载的密码实现所支持的内容。

ESP 提议不是改名后的 IKE 提议

密码令牌在两份列表里看起来一模一样,其余几乎什么都无法照搬。ESP 提议没有密钥交换伪随机函数,它的完整性与前向保密要求是针对对端的 Windows 策略论证出来的,而不是从 IKE 那一行复制过来的。把一份列表机械地翻译成另一份,只会得到能通过校验、却永远谈不成的配置。

选定一个方向并坚持下去。要么网关保留仅 GCM 的策略,由客户端部署一份完整兼容的策略;要么网关接受两个算法族,客户端保留现有套件。启用安全策略明令禁止的回退方案并不是修复,无论它多快让错误消失。

分别验证两套套件,再做一次密钥重协商

分别确认选中的密钥交换套件与选中的流量套件,因为它们来自不同的列表,可以各自独立成功。然后让一次密钥重协商发生,并同样加以确认。首次握手成功而重协商失败,说明这套提议只被部分接受。

把这份证据与变更一起留存。反复断线需要它自己的失败交换记录,之后才轮到怀疑计时器或平台缺陷。

相关文章