跳过正文
  1. 博客/

cURL 的 --pinnedpubkey

·233 字·2 分钟·
CURL TLS

偶然看到一篇文章:Go: Calculating public key hashes for public key pinning in curl,其中提到 cURL 的一个有趣的参数:--pinnedpubkey

使用过 cURL 的读者可能已经知道 -k / --insecure 参数,在使用 TLS 链接时(如访问 HTTPS 链接)时指定这个参数,将忽略对服务端返回的证书的合法性检查。通常访问使用自签名证书的链接时(典型场景如:非生产环境测试)会使用到它。

简单来说,TLS 1.3 中的主要流程是:

  1. 通过 ECDH(E) 协议在不可信的信道上进行对称加密密钥及参数的协商
  2. 通过 X509 证书进行对端的身份认证(多数情况由链接发起方进行,即客户端检查服务端,双向无非就是下面的流程互相做一次)
    1. 证书的合法性判断 1 – 是否由可信的 CA 签发
    2. 证书的拥有者判断 2 – 是否持有证书中展示的公钥对应的私钥

根据 man page 中的描述,这个 --pinnedpubkey 参数,不需要配合 -k 参数才能使用。个人理解就是 cURL 的一种自定义的、在上述 2 之外的一种额外的身份认证手段。

--pinnedpubkey 参数的值可以是以下任意一种:

  • 文件路径,文件的内容需要含有 PEM / DER 格式的公钥
  • base64 编码的公钥的 sha256 哈希值加上固定的 sha256// 头部,多个值的话需要使用 ; 表示分隔

man page

如果对 OpenSSH 的身份认证较为了解的读者应该会有一种熟悉感,是的,这类身份认证手段在概念上都是近似的。

OpenSSH 默认将远端主机的 SSH Host Key 的哈希值存储在 ~/.ssh/known_hosts 文件中。

第二种按哈希方法的具体值怎么计算可以参考文章中的 Go 代码:

func calculatePublicKeyHashes(certs []*x509.Certificate) ([]string, error) {
  hashes := make([]string, len(certs))

  for i, cert := range certs {
    derCert, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
    if err != nil {
      return nil, err
    }
    hash := sha256.New()
    hash.Write(derCert)
    hashes[i] = fmt.Sprintf("sha256//%s", base64.StdEncoding.EncodeToString(hash.Sum(nil)))
  }

  return hashesnil
}

忽略代码中这个让人造成困惑的 derCert 变量名 (觉得叫 pkInfoDERBytes 或者直接 bs 更合适),理解计算的过程就行。

当然在实际使用中,还是使用文中提出的通过脚本调用 OpenSSL 进行计算的方式更为方便:

# Assuming default.crt is a PEM-encoded cert, this extracts the public key
# converts it to DER form, hashes it with SHA-256, then base64-encodes it
# and prepends "sha256//"

echo sha256//$(openssl x509 -in default.crt -pubkey -noout \
   | openssl asn1parse -inform PEM -in - -noout -out - \
   | openssl dgst -sha256 -binary - \
   | openssl base64)

# Outputs something like sha256//Y/CGGnkaoZwUgOqArQs12llyoaX0bkjSIgHCPtXba+c=

使用公钥的哈希的设计在我看来完全可以直接使用 X509 中的 SKID,只是不知道 cURL 为什么选择了自定义格式的方式。

这个参数是在 7.39.0 (只支持 OpenSSL) 引入的。见 SSL: implement public key pinning

类似的 --proxy-pinnedpubkey 在 7.59.0 加入,见 curl: add –proxy-pinnedpubkey