OpenConnect 需要哪种 PKCS#11 URI?
OpenConnect 要的是标明令牌证书与私钥的 PKCS#11 URI,而不是导出的 PEM。找到与注册相符的对象并用引号括起来。
已经存有正确密钥的智能卡,也必须被告知在卡上使用哪个对象。这种情况下 OpenConnect 不要复制的 .key 文件,而要一个 PKCS#11 URI,用它标出证书;当自动搜索无法完成工作时,还要标出旁边的私钥。麻烦之处在于,语法合法的 URI 什么也证明不了。它必须写明网关已经信任的那项注册,而不只是卡愿意打开的某个对象。
客户端想要的不是导出的密钥
-c 选项通常接受文件路径,但当值被解析为 PKCS#11 URI 时,OpenConnect 会把它当作对令牌内部对象的引用,而不是磁盘上的文件。-k 选项对私钥也是如此。什么都不会导出,密钥从不离开卡提供的保护。客户端需要你给出的,是令牌模块、证书存储和网关注册三方面都认可的名字。
大多数尝试卡在最后一个条件上。URI 可以完全合法,却仍然选中网关会拒绝的证书,因为有效性和相关性是两个不同的问题。值得做的是与注册记录比对,而不是扫描第一个碰巧能打开的对象。
两个 URI,两项不同的工作
证书 URI 回答你在主张哪个身份。它指向一个证书对象,其公钥和主题行与 VPN 管理员签发的账户或设备配置相符。同一张证书通常附带存放在旁边的私钥对象,而 TLS 握手真正需要的正是这一对。
密钥 URI 回答当 OpenConnect 无法推断时,匹配的私密材料在哪里。只设置 -c 时,客户端会根据证书属性构建密钥搜索;再给出 -k 就是用显式对象引用取代猜测。令牌的 PIN 是第三个、互不相关的输入:它为这次操作解锁卡片,从不标识任何对象。
openconnect --protocol=anyconnect \
-c 'pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=1a2b3c4d;id=%01' \
vpn.example.net
那个带引号的字符串里的一切都是数据。分号分隔 URI 属性,百分号转义对否则会破坏解析的字符进行编码,所以整个参数必须完整地穿过 shell。
找到与你的注册相符的 URI
GnuTLS 自带 p11tool,它会列出当前用户通过 p11-kit 实际能访问到什么。先用 p11tool --list-tokens 开始,再列出你关心的令牌上的证书对象。如果对象只有在输入 PIN 后才出现,就加上 --login,并把主题和签发者与注册记录比对,而不是直接挑第一个出现的。
如果令牌根本不出现,问题在 OpenConnect 之下:p11-kit 没有为该中间件注册模块,或者模块注册在另一个账户下。系统级注册位于 /etc/pkcs11/modules 之下,而用户级目录可以保存同样的内容,不必改动系统副本。检查模块文件里的提供者路径是否指向读卡器软件确实安装的库。
从列表里复制 URI 时,要保留它外面的引号。没有引号的分号会在第一个属性处截断参数,客户端随后报告找不到对象。
当自动密钥查找放弃时
OpenConnect 可以从证书 URI 推导出密钥查找,但它执行的确切匹配取决于版本,对象类型和标签的处理方式随时间也变过。所谓先缩短 URI 直到能用,说的是旧的变通办法,不是要求。若推导出的查找失败,就用 -k 明确点名密钥,而不是把证书 URI 改得更含糊。
在责怪客户端之前,先用 p11tool --list-keys 确认密钥存在且可见。接着确认该对象被标记为可用于签名或解密,因为属性错误的密钥无论 URI 多正确都无法完成握手。不要把 OpenVPN 配置里序列化的 pkcs11-id 值搬进 OpenConnect 选项;两个客户端即便面对同一张卡,标识符格式也不同。
如果 URI 能解析、PIN 也被接受,但认证仍然失败,就别再调 URI 了。去读网关自己的错误,因为下一个失败在令牌之上。
只是看起来像 URI 问题的失败
讲另一种协议的网关,无论你的 URI 对错都会一样失败。OpenConnect 替换旧版 vpnc 客户端是有原因的,支持智能卡并不会让不兼容的网关协议变得兼容。在又花一小时研究对象属性之前,先确认服务器期望的协议和认证安排。
服务器身份校验也是独立的一层。客户端会用信任库校验网关证书,而这项检查在智能卡完成自己的部分之后才运行。读卡器权限和停止的 pcscd 服务会产生同样的外在症状,即令牌无法读取,但修起来简单得多。
从对象向外排查:p11tool 看得到令牌吗,看得到证书和密钥吗,OpenConnect 接受这个 URI 吗,然后才问网关是否接受该身份。每一步都有自己的错误信息,而且每一步都比后一步更省事。