错误信息
x509: certificate specifies an incompatible key usage
也可能输出为
- tls: failed to verify certificate: x509: certificate specifies an incompatible key usage
TLS 客户端会加上“tls: failed to verify certificate: ”前缀;验证客户端证书的 Go 服务器会记录“http: TLS handshake error from <addr>: tls: failed to verify certificate: x509: certificate specifies an incompatible key usage”。
Go crypto/x509 — CertificateInvalidError (IncompatibleUsage): extendedKeyUsage does not allow this role
含义
证书链是受信任的,但证书的 extendedKeyUsage(或者中间证书的 extendedKeyUsage,Go 也会检查它)不允许证书承担当前的角色:服务器证书缺少 serverAuth,或者双向 TLS 中的客户端证书缺少 clientAuth。Go 严格执行 EKU 检查;其他一些 TLS 实现更宽松,因此同一张证书在别处可能可以正常使用。
原因
把客户端证书当作服务器证书使用
常见来自客户端身份验证模板的证书(EKU 只有 clientAuth),例如内部 PKI 签发的证书,被安装到了服务器上。
把服务器证书用于双向 TLS
常见把 Web 服务器的证书(EKU 只有 serverAuth)复用为客户端证书,在要求 clientAuth 的 Go 服务器上会验证失败。
中间证书限制了允许的用途
偶见Go 把中间证书上的 EKU 视为对其下所有证书的约束;限定为 clientAuth 的中间证书无法签发可用的服务器证书。
修复方法
- 1.
查看每张证书的 extendedKeyUsage
检查叶子证书和每张中间证书;你需要的用途必须在整条路径上都被允许。
bash # Leaf and intermediates: which purposes do they allow? openssl x509 -in server.pem -noout -ext extendedKeyUsage,keyUsage # TLS Web Server Authentication = serverAuth, TLS Web Client Authentication = clientAuth - 2.
使用正确的 EKU 重新签发
服务器证书使用 serverAuth,客户端证书使用 clientAuth(如果一张证书确实必须同时承担两种角色,就两者都加上)。
bash # Server certificates need serverAuth; for mutual TLS client certificates need clientAuth openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \ -addext "subjectAltName=DNS:api.example.com" -addext "extendedKeyUsage=serverAuth" - 3.
双向 TLS:签发带 clientAuth 的客户端证书
设置了 RequireAndVerifyClientCert 的 Go 服务器会检查客户端证书的 EKU 中是否有 clientAuth。
go // Go server verifying client certificates: they need clientAuth (or no EKU at all) cfg := &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAPool, }