两个问题决定 VPN 私钥该放在哪里:密钥材料能否被复制出主机,以及进程能否在无人值守的情况下使用它。存储方案的营销名称对这两个问题都不作答。重启后必须恢复的隧道守护进程需要无人值守使用,这就排除了任何依赖人工输入 PIN 的方案。

在复制和使用之间做选择

复制和使用以不同方式失败,因此要把它们当成两项要求而不是一项。能读取密钥字节的攻击者可以把身份带到另一台主机,而原机器毫无察觉。读不到字节但能触达签名过程的攻击者,只要那种访问还在,就仍能完成认证。

文件形式的密钥极易复制,这也是它最容易备份和恢复的原因。软件密钥存储改变的是 API 而不是边界:提供方会拒绝自己的导出调用,但安装密钥的同一个管理员上下文通常还有别的路径拿到材料。软件提供方所谓的不可导出只是对支持操作的陈述,不是硬件墙的证明。把它当成防止随手复制的减速带,而不是防备已经掌握你会话的人。

TPM 或智能卡在硬件内部执行签名操作,只返回结果。这关上了复制路径,却把使用路径留给了任何能请求签名的东西。

让存储与隧道守护进程匹配

守护进程比安全策略限制得更死。WireGuard 只从文件或 wg set 输入读取 base64 私钥,没有通往证书存储的路径,因此 TPM 支撑的接口密钥根本不成立。OpenVPN 若针对 OpenSSL 3 构建,可以通过提供方 URI 加载密钥;strongSwan 除 PKCS#11 支持外还附带 TPM 插件,但两者都取决于你的软件包如何编译。

守护进程够不到所选存储,硬件边界就只是装饰。于是有人把密钥导出一份留在文件里,剩下的设计就悄悄跑在那份副本上。

续期是匹配的另一半,也是多数设计崩掉的地方。首次注册能用、但每次重签发都要控制台登录的存储,会在证书于不合适的时间到期时第一次被绕开。趁当前密钥还有几个月,先把重签发路径测一遍。

搬动之前先盘点密钥

在改变密钥存放位置之前,先记录它是什么,因为丢失 subject alternative names 的迁移会以看似路由故障的方式打断隧道。导出证书,确认守护进程期望的位置能读到私钥,并检查这对密钥确实匹配。

openssl x509 -in vpn.crt -noout -subject -ext subjectAltName
openssl pkey -in vpn.key -check -noout
openssl x509 -in vpn.crt -noout -pubkey | openssl sha256
openssl pkey -in vpn.key -pubout | openssl sha256

最后两个摘要必须完全一致。若不一致,守护进程会完成配置,然后在每次尝试时以完全不指向真实原因的证书或签名错误失败。WireGuard 的对应做法更短,因为公钥直接由私钥导出:wg pubkey < /etc/wireguard/wg0.key 必须打印出服务器已存档的对端密钥。

权限也属于同一份清单。stat -c '%a %U:%G' /etc/wireguard/wg0.key 应返回 600 root:root;任何组或全局可读都意味着密钥早已暴露给你从未设想的进程。

检查存储和使用路径

验证分三部分,第三部分最常被跳过。确认密钥存在于所选存储,确认守护进程加载的是这把密钥而不是遗留副本,再确认导出产物消失后它仍能启动。

certutil -store -user My
certutil -csp "Microsoft Platform Crypto Provider" -key -user
certutil -verifykeys -user My
ipsec listcerts

在 Windows 上,第一条命令列出个人存储并在每个条目旁打印密钥提供方,那里就会出现看似硬件支撑、实际位于软件提供方的证书。第二条列出平台加密提供方持有的密钥,所以证书还在而 VPN 身份不在,说明私有的那一半住在别处。在 Linux 网关上,ipsec listcerts 报告 strongSwan 加载了什么,包括来自 TPM 插件的密钥。用指纹或公钥哈希与清单比对,而不是相信条目数量。

负向测试才能证明设计。把导出的密钥移出守护进程的配置目录,重启服务,然后读日志看它出示哪个身份。隧道仍能起来,说明它用的是存储;启动失败,说明设计其实一直在跑副本。

写下续期和回滚

两个家族的回滚差别很大,这个差别属于设计笔记而不是事故电话。文件密钥可以加密备份并恢复,所以失败的迁移可以回退。不可导出的密钥根本无法备份,设备一旦损坏,重新签发和重新登记就是唯一的恢复路径。

在过期变成故障之前检查:当证书在三十天内到期时,openssl x509 -in vpn.crt -noout -checkend 2592000 以非零码退出,因此可以直接放进监控检查。每周跑这一行,就能抓住没人安排过的续期。

移除在服务端发生:删除对端或吊销序列号,然后确认隧道失败,因为持有缓存会话的客户端可能把半完成的吊销遮掩一阵。吊销还必须到达集中器的 CRL 或 OCSP 路径,而不只是 CA 数据库。证书是公开文档,RFC 5280 配置规定了如何验证它;这些验证都保护不了在轮换前被复制的私钥。

把决定写在一处:哪个存储持有密钥,哪个进程可以使用,谁能重新签发,回滚是什么。审计人员问的就是这一段,人员更替后留下来的也是它。

相关文章