如何从信任存储中删除过期的 VPN 证书
删除 VPN 配置文件后,证书仍留在操作系统、浏览器或应用商店中。按指纹匹配,删除范围最窄的副本,然后验证证书链。
VPN 客户端已卸载,配置文件已从“设置”里消失,但隧道仍会用旧凭据建立。或者你删除了它,重启后第二天早上又发现它回来了。这都不是删除按钮坏了。配置文件只是配置;证书是密钥材料加上记录在某个存储中的信任决定,而配置文件只是指向那个存储。你手里拿的是哪一样,决定了“删除”必须意味着什么。
配置文件不是证书
在 VPN 配置中,有三种不同的对象都被称为“证书”。客户端身份是一张带私钥的叶证书,网关用它判断你是谁。服务器自己的叶证书及其中间证书,是你的客户端在连接时检查的内容。信任锚通常是根证书或私有 CA 证书,正是它让整条链可被验证。 RFC 5280 将它们称为信任锚、中间证书和终端实体,并且只有当每一环签署其下一环时,链才有效。
删除身份,一台设备就失去访问权。删除锚, 每个依据该存储进行验证的应用都会失去检查其下所签发任何内容的能力,包括与 VPN 毫无关系的连接。这种不对称正是这里的全部风险:为一个网关安装的根,往往也是签署内部门户、邮件服务器或软件包镜像的同一个根。记住你手里拿的是两者中的哪一个。
所有权决定删除是否保持删除状态。在你自己的笔记本上,只有你能把证书放回去。在受管理的设备上,配置描述文件或 MDM 载荷可能在下次签入时重新安装它,这就是它“回来”的原因,也是更用力删除没有用的原因。
找出它还在哪个存储里
人们从“设置”应用开始,而那是证书唯一不会被存放的地方。真正的位置是操作系统信任存储、浏览器自己的存储以及应用的私有存储。
| Store | What deleting from it affects |
|---|---|
| Windows CurrentUser\My | your login session only |
| Windows LocalMachine Root | every user and service on the machine |
| macOS login or System keychain | one account, or all of them with sudo |
| Linux ca-certificates bundle | curl, git, Python, anything using the system bundle |
| Firefox cert9.db | Firefox only; it does not read the OS store |
按序列号或指纹匹配,绝不要按显示名称。显示名称由签发者设定并被重复使用:“Example VPN”可能是三张使用三把不同密钥的证书,其中一张仍在日常使用。
certutil -user -store My
certutil -store Root
openssl x509 -in vpn-client.pem -noout -subject -issuer -serial -fingerprint -sha256
在 macOS 上,security 工具给出同样的事实,而 find-identity 会显示哪些证书仍附有私钥:
security find-certificate -a -c "Example VPN" -Z /Library/Keychains/System.keychain
security find-identity -v -p ssl-client
删除最窄的对象,保留共享的签发者
删除前先导出。 没有备份,唯一的恢复方式就是重新签发,在私有 CA 上这意味着回去找管理 PKI 的人。导出的 PFX 也是你在删除被证明是错误决定时交出的东西。
certutil -user -exportPFX -p backup My <serial> C:\temp\vpn-id.pfx
certutil -user -delstore My <serial>
Remove-Item Cert:\CurrentUser\My<thumbprint>
这个 -user 标志是人们常因疏忽而漏掉的。没有它,certutil 会改为操作计算机存储,这意味着提升权限的 shell、另一组证书,以及现在包含机器上所有其他账户的影响范围。
在 macOS 和 Linux 上形状相同:识别、导出,然后从能解释症状的最窄存储中删除。删除 /usr/local/share/ca-certificates 下的文件并重新运行 update-ca-certificates 就足够了; --fresh 只有在 /etc/ssl/certs 中的符号链接已经不一致时才有帮助。RHEL 系列系统把锚放在 /etc/pki/ca-trust/source/anchors中,在那里 trust anchor --remove 完成同样的工作。
sudo security delete-certificate -Z <sha1> /Library/Keychains/System.keychain
sudo rm /usr/local/share/ca-certificates/example-vpn.crt
sudo update-ca-certificates
sudo trust anchor --remove /etc/pki/ca-trust/source/anchors/example-vpn.crt
在删除 CA 证书之前,检查还有什么会链到它。列出捆绑包中的主体是一个两行检查,它回答了该锚是否还在做别的工作:
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \
| openssl pkcs7 -print_certs -noout | grep -i example
删除文件不等于吊销身份
本地删除只移除一个副本。私钥仍可能留在附加到旧工单的 p12 中、备份文件夹里,或同时设置的第二台笔记本上。如果这是客户端身份,在身份被服务器端拒绝之前,网关会继续接受该密钥的任何副本。
对于 OpenVPN PKI,这意味着吊销证书,并确保 crl-verify 所引用的 CRL 是最新的; easyrsa revoke 然后 easyrsa gen-crl 就是简短版本。对于绑定到身份提供商的网关,要在那里禁用用户或移除设备,因为证书只是其中一项凭据。在账户仍处于活动状态时删除本地副本看起来像清理,其实不是。
事件处理有不同的顺序:先保留证书、其序列号和吊销记录,然后再删除和吊销。这些记录不花成本,也不需要把可用的凭据留在原处。
用一次握手和第二个应用来验证
从外部检查链,而不是从你刚编辑过的存储列表。存储显示你的编辑;握手显示结果。
openssl s_client -connect vpn.example.com:443 -servername vpn.example.com \
-CAfile /etc/ssl/certs/ca-certificates.crt -verify_return_error < /dev/null
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt vpn-gateway.pem
返回码 0 意味着剩余的锚仍能验证网关。移除锚之后出现链错误是预期结果,它确认你命中的是正在使用的锚,而不是客户端从不读取的存储中的重复项。如果连接仍然可用,就去找第二个副本、应用内置的存储或固定的证书,这些在自带信任列表的客户端中都很常见。
然后测试一些无关的东西:内部站点、邮件客户端、软件包镜像。这能在变更窗口关闭前廉价地衡量影响范围。
- 已将角色识别为身份、中间证书或信任锚
- 已记录序列号和指纹,并将副本导出到批准的位置
- 在能解释症状的最窄范围内完成删除
- 当人员或设备离开时,已撤销服务器端授权
- 已解释握手,并重新测试了一个无关应用
- 已更新 MDM 载荷或管理描述文件,否则证书会回来
如果重启后证书又回来了,工作就没有完成:有东西拥有它并重新安装它。在再次删除之前找到那个所有者,因为第三次删除的行为会与前两次完全一样。