DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

curl 携带客户端证书仍返回 401,浏览器证书登录正常

使用 OpenSSL 后端的 curl 只发送客户端叶证书时,服务器可能无法建立身份链。区分服务器证书校验与客户端认证,为 --cert 提供有序 PEM 链并核对身份授权。

同一个接口在浏览器中选择客户端证书后能登录,curl 已经指定 --cert 和 --key,却仍收到 401 Unauthorized。诊断输出还显示 SSL certificate verify ok,看起来证书已经验证通过。

这里有两个不同方向的验证:curl 验证服务器身份,服务器验证客户端身份。前一项成功,不代表服务器已经认可 curl 发来的客户端证书。如果服务器需要客户端证书认证,而 curl 只发送了叶证书、缺少建立客户端身份链所需的中间证书,就可能无法完成身份识别。具体服务器可能在 TLS 阶段拒绝,也可能在握手后返回认证失败,不能仅凭 401 就断定缺链。

本文处理使用 OpenSSL TLS 后端、从 PEM 文件加载客户端证书的 curl。Schannel 证书存储、硬件令牌及其他 TLS 后端的加载方式不同,不应直接套用拼接文件的步骤。确认执行环境可查看:

curl --version

除 curl 版本外,还要核对实际使用的 TLS 库。支持多个后端的构建可能受 CURL_SSL_BACKEND 影响,不能只凭机器上安装了 OpenSSL 判断。

先确认缺的是客户端身份链

保留当前命令、客户端证书和私钥文件,不要先把接口改成免认证。检查服务端的认证诊断:有没有收到客户端证书、证书对应哪个身份、验证链是否缺少签发者,以及该身份是否被授权访问当前接口。

浏览器成功可以帮助对比,但还需排除已有登录 Cookie、另一张证书、不同代理或不同路径。只有确认浏览器也在用预期的客户端证书,才有条件比较两边提交的链。

curl 的详细输出里出现客户端 Certificate 消息,也不能单独证明所有中间证书都已发送。应结合服务端可用的证书链诊断,或者经授权取得的握手检查结果判断。不同 TLS 版本的抓包可见性不同,不要仅按数据包大小推测发送了几张证书。

若服务器已经接受客户端证书,后续 401 来自账号映射、权限或接口认证方式,补链不会解决它。WWW-Authenticate: Basic 表示响应提出了 Basic 认证挑战,也不足以独自证明该服务完全不支持证书认证。

给 --cert 提供有序的客户端证书链

从证书签发方或负责该接口的管理员取得与客户端叶证书匹配的中间证书链。不要下载一张名称相似的 CA 证书就拼进去;必须沿实际签发关系核对。

在支持该 PEM 加载方式的 OpenSSL 后端中,证书文件可以包含客户端叶证书及其后续中间证书。第一张必须是实际客户端证书,后面按签发顺序放中间证书。私钥可以继续独立保存。

假设 client-leaf.pem 仅包含客户端叶证书,client-intermediates.pem 已包含按顺序排列的所需中间证书,两者都是完整、以换行结尾的 PEM 文件。使用未被占用的新输出文件:

cat client-leaf.pem client-intermediates.pem > client-chain.pem

输出路径不能与任一输入路径相同,否则重定向可能先清空输入。不要把私钥或服务器证书链误拼进这里。保留原始文件,先将新文件用于一次受控调用:

curl --verbose --cert-type PEM \
  --cert ./client-chain.pem \
  --key ./client.key \
  'https://api.example.com/status'

域名、路径及文件名均为占位值,应使用允许诊断的接口。私钥必须与第一张客户端证书配对,限制其读取权限;若私钥加密,按当前 curl 的交互提示处理口令,不把明文口令直接写入共享脚本或诊断记录。

不要连续写两个 --cert 来提供两张链证书。它是单值选项,重复指定不是“追加一个中间证书”的接口。

不把客户端链和服务器信任库混为一谈

--cert 提供客户端身份材料;--key 提供对应私钥;--cacert 指定用于验证对端的 CA 文件。若服务器使用私有 CA,可以在保留客户端链参数的同时,按实际信任要求提供服务器 CA 文件:

curl --cert-type PEM \
  --cert ./client-chain.pem \
  --key ./client.key \
  --cacert ./server-trust.pem \
  'https://api.example.com/status'

server-trust.pem 应来自已确认的信任配置。服务器证书原本已能通过正常信任库校验时,不必为了客户端链问题替换这个文件。

OpenSSL 在构建发送的证书链时也可能从信任位置寻找补充证书,因此某些环境中调整 CA 文件会影响结果。但这不意味着应该把所有中间证书都混进信任库,也不能由一次成功推断其他 TLS 后端行为相同。显式整理客户端链,能够让身份材料与服务器信任设置分别受控。

关闭服务器证书验证不会补齐客户端身份链。不要用 -k 掩盖另一方向的验证问题,也不要把服务器改成接受任意客户端证书来取得成功响应。

验收身份结果,而不只看握手完成

使用原先失败的 curl 环境,保持目标、代理和认证条件一致,检查服务端是否识别出预期客户端身份,并确认接口返回的是该身份应得到的业务结果。链通过后仍然返回 401,应继续核对账号映射与授权,不能反复追加不相关的 CA。

本次修改可以只涉及客户端证书链文件和调用路径。发现异常时恢复旧调用参数,保留新文件用于核对,不需要删除原证书、重签私钥或放宽服务端认证。诊断日志可能含 Cookie、认证头或内部地址,对外分享前应脱敏。