IKE 已经启动,两端都声明支持 AES,隧道却仍然以 NO_PROPOSAL_CHOSEN 失败。日志给出了提案的名字,却没有指出哪个字段没有匹配上。提案是一份完整的要约,一个不兼容的字段就会让整个组合失效,而不只是那一行。把它当成一堆彼此独立的算法选择来读,正是这种不匹配长期隐形的原因。

两套各自独立提案列表的协商

IKEv2 会建立两个安全关联。第一个是 IKE SA,用来保护控制交换本身;第二个是 CHILD SA,用来通过 ESP 承载你的流量。两者各有自己的提案列表,对端可以接受其中一个而拒绝另一个。

这种分离解释了一个常见困惑:能正常工作的 IKE SA 只能证明控制通道的算法匹配。如果 CHILD 提案失败,即便第一阶段已经完成、日志顶部看起来健康,隧道也不会承载任何数据包。

每个列表内部的顺序同样重要。响应方从发起方按序排列的要约中挑选,因此把兼容面更宽的条目挪到较窄的条目之前,就会改变最终安装哪套算法,而任何一个算法名称都没有变。请在配置中固定这个顺序,不要依赖默认值。稳定的顺序也让不同站点之间的日志对比有意义。

哪些字段属于哪份提案

字段集合不大,但在两套协商中并不完全相同。要按行读表,因为一列中的字段可能在另一列里没有。语法因实现而异;strongSwan 的提案文档就是一个具体的参考。

FieldIKE SA proposalCHILD SA proposal
EncryptionRequiredRequired
IntegrityRequired unless AEADRequired unless AEAD
PRFRequiredNot present
DH groupRequiredOnly for PFS
ESNNot presentOptional

加密字段指出算法,在多数语法里还包括密钥长度。完整性字段指出用于校验密文的哈希,PRF 则指出为 IKE SA 派生密钥材料的函数。DH 组负责建立新的共享秘密材料,因此它出现在两侧,但目的并不相同。

扩展序列号只属于数据路径。它扩大了序列空间,并作为 CHILD 提案的一部分进行协商,所以这里出现不匹配只会拒绝 CHILD 要约,IKE SA 不受影响。

一份完整要约是如何被选中的

每份提案都是一个集合,响应方寻找的是一个所有必填字段都可接受的集合。两个列表都可能包含 AES 和 SHA-256,却仍然没有兼容条目,因为密钥长度、PRF 或 DH 组不同。比较是按提案进行的,不是按算法。

当没有任何匹配时,响应方会连同失败的 SA 类型一起发送 NO_PROPOSAL_CHOSEN。修改配置之前,先读清这条通知指的是哪份提案列表;IKE 提案被拒时去改 ESP 算法,只会浪费一轮排查。

调整配置档时,值得了解选择行为。有些实现偏好发起方的顺序,另一些会按本地策略重新排序,所以在两个厂商之间能跑通的算法套件可能消失,只要你把发起连接的一侧对调。

AEAD 是删掉一个字段,而不是增加一个

AES-GCM 与 ChaCha20-Poly1305 在一次运算中同时完成认证与加密。当其中任一被选用于 ESP 时,完整性算法不会单独协商,因为 AEAD 构造已经覆盖了它。因此把 AES-GCM 与 SHA-256 配在一起的提案,最好情况是冗余,最坏情况是被拒绝。

PRF 在 IKE 侧仍然是独立的,因为它为控制通道派生密钥,而不是保护载荷。正因如此,IKE 提案可以合法地把 AEAD 算法与 PRF 和 DH 组组合在一起,而对应的 ESP 提案只列出 AEAD 算法和可选的 PFS 组。

密钥长度是加密令牌的一部分,所以 AES-128-GCM 与 AES-256-GCM 是不同的要约。从假定使用 256-bit 密钥的教程里复制来的配置档,无法与配成 128-bit 的端点匹配,尽管两行都写着 AES-GCM。两端可以就算法族达成一致,却仍会因为连字符后面那个数字而互相拒绝。

要读已安装的 SA,而不是配置

配置好的提案列表是一份要约,已安装的 SA 才是结果。在 strongSwan 和 Linux 内核上,两种视图都可用:

sudo swanctl --list-sas
sudo ip xfrm state
sudo ip xfrm policy

swanctl --list-sas会打印每个 SA 协商出的算法,包括 CHILD 是否使用 AEAD。 ip xfrm state 则显示内核实际安装了什么,而这里正是确认重协商没有退回与你记录不同的套件的地方。

修复之后,把这段输出保存在配置旁边。当客户端更新改变了默认值时,记录下来的套件就是对照的基线,而对比会直接指向变动的字段,不必从零开始调查。

相关文章