错误信息
curl: (60) SSL certificate problem: self-signed certificate
也可能输出为
- curl: (60) SSL certificate problem: self signed certificate
- curl: (60) SSL certificate OpenSSL verify result: self-signed certificate (18)
OpenSSL 3.0 及更高版本打印“self-signed certificate”;OpenSSL 1.1.1 打印“self signed certificate”。较新的 curl 版本(我们测试的是 8.22.0)会写作“SSL certificate OpenSSL verify result: self-signed certificate (18)”。不要把它与“self-signed certificate in certificate chain”混淆,后者存在一条 CA 证书链,并终止于不受信任的根证书。
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
含义
服务器只出示了一张用自身密钥签名的证书(颁发者与主体相同),而 curl 的信任库中没有这张证书本身。它背后没有可供 curl 查找的 CA,因此验证在深度 0 处就停止了。这对开发服务器和专用设备来说很正常,安装中间证书永远解决不了这个问题。
原因
使用自动生成证书的开发或测试服务器
常见本地 HTTPS 服务器、docker-compose 编排的服务栈和测试夹具常常在启动时生成自签名证书(例如使用 openssl req -x509)。每次重新生成都会产生一张新证书,因此昨天信任的副本今天可能就对不上了。
设备或产品仍在使用出厂证书
常见路由器、NAS 设备、打印机、iDRAC/iLO 管理界面,以及 Proxmox、Jenkins 或 Elasticsearch 的安装实例,出厂时都带有自签名证书,直到有人安装正式证书为止。
代理或 Ingress 提供的是默认证书
偶见如果没有与请求名称匹配的证书,ingress-nginx 会提供“Kubernetes Ingress Controller Fake Certificate”,Traefik 会提供“TRAEFIK DEFAULT CERT”,两者都是自签名证书。这时真正的问题在于路由或 SNI,而不是信任。
服务器指向了错误的文件
罕见如果配置引用的是临时自签名证书(Debian 上的 snakeoil.pem、某个软件包附带的 localhost.crt),而不是正式签发的证书,即使磁盘上存在真正的证书,也会产生这个错误。
修复方法
- 1.
核实后,只信任这一张证书
对于单个内部端点,保存该证书,通过可信渠道(设备的控制台、创建它的管理员)确认其 SHA-256 指纹,再用 --cacert 传入。这样 curl 在这次调用中只接受这张证书,不接受其他任何证书。
bash # Fetch the certificate once, compare its fingerprint out of band, then trust exactly that file openssl s_client -connect api.example.com:8443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -out api.example.com.pem openssl x509 -in api.example.com.pem -noout -fingerprint -sha256 curl --cacert api.example.com.pem https://api.example.com:8443/ - 2.
开发环境:用本地 CA 签发证书
使用本地 CA(mkcert、step-ca 或手工创建的根证书),只需信任一个根证书一次,就能为任意数量的开发主机签发证书。这些证书不再是自签名的,因此在安装了该根证书的所有地方,这个错误都会消失。
bash # mkcert creates a local CA, installs it into the system and browser stores, # and issues certificates signed by it (no longer self-signed) mkcert -install mkcert api.example.test localhost 127.0.0.1 - 3.
公网主机:安装公共 CA 签发的证书
凡是供他人或第三方客户端访问的服务,都应出示由公开受信任的 CA 签发的证书;certbot、lego 或 Caddy 等 ACME 客户端可以自动获取并续期证书。
bash # Public names: get a certificate from a public CA, e.g. with certbot (ACME) sudo certbot certonly --nginx -d api.example.com - 4.
临时诊断:关闭验证以检查服务
-k 让 curl 跳过证书检查,因此如果返回 200,就说明缺少的只是信任。不要保留它:未经检查就接受的自签名证书随时可能被替换成攻击者的证书,而 curl 不会察觉。
bash # Diagnostic only: confirms the service answers when certificate checks are skipped curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com:8443/
相关错误
curl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)verify error:num=18:self-signed certificateOpenSSL CLI (openssl s_client / openssl verify)code: 'DEPTH_ZERO_SELF_SIGNED_CERT'Node.js tls / https / fetch