如果不使用来自系统的证书,curl 中的公钥固定不起作用

克劳迪乌

我正在尝试将 libcurl 与公钥固定一起使用,以便在下载文件时验证服务器的真实性。

Curl 被编译,因此它不使用系统上的任何证书,而只依赖于它从用户那里收到的证书:

./configure --without-ca-bundle --without-ca-path --without-ca-fallback && make

首先,我获得服务器证书的公钥的SHA256总和,为解释在这里

$ openssl s_client -servername www.example.com -connect www.example.com:443 < /dev/null | sed -n "/-----BEGIN/,/-----END/p" > www.example.com.pem
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
verify return:1
depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
verify return:1
DONE
$ openssl x509 -in www.example.com.pem -pubkey -noout > www.example.com.pubkey.pem
$ openssl asn1parse -noout -inform pem -in www.example.com.pubkey.pem -out www.example.com.pubkey.der
$ openssl dgst -sha256 -binary www.example.com.pubkey.der | openssl base64
xmvvalwaPni4IBbhPzFPPMX6JbHlKqua257FmJsWWto=

然后我在 libcurl 中设置公钥的哈希和其他相关选项:

curl_easy_setopt(conn, CURLOPT_PINNEDPUBLICKEY, "sha256//xmvvalwaPni4IBbhPzFPPMX6JbHlKqua257FmJsWWto=");
curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(conn, CURLOPT_URL, "https://example.com/index.html");
curl_easy_setopt(conn, CURLOPT_VERBOSE, 1);
curl_code = curl_easy_perform(conn);
if (curl_code != CURLE_OK)
{
    printf("%s\n", curl_easy_strerror(curl_code));
}

下载失败并出现错误:

* SSL certificate problem: unable to get local issuer certificate
...
Peer certificate cannot be authenticated with given CA certificates

好吧,似乎 curl 正在寻找一些证书,所以我重新编译它以使其包含默认证书:

./configure && make

现在,下载将工作:

* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
...
*  SSL certificate verify ok.
*    public key hash: sha256//xmvvalwaPni4IBbhPzFPPMX6JbHlKqua257FmJsWWto=
...

在 CURLOPT_PINNEDPUBLICKEY 文档中,有解释:

When negotiating a TLS or SSL connection, the server sends a certificate
indicating its identity. A public key is extracted from this certificate
and if it does not exactly match the public key provided to this option,
curl will abort the connection before sending or receiving any data. 

所以我的印象是 curl 只需要用户的公钥,以便将它与从服务器证书中提取的公钥进行比较。

我在这里缺少什么?

克劳迪乌

问题是CURLOPT_SSL_VERIFYPEER设置为 1 会启用 CA 固定。Curl 接受同时设置 CA pinning 和 public-key pinning,并且因为 CA pinning 在 public key pinning 之前尝试,CA pinning 失败并且它永远不会进行 public key pinning。

解决方案是在进行公钥固定时显式禁用 CA 固定:

curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);

这需要明确地完成,因为默认值为CURLOPT_SSL_VERIFYPEER1。

注意:CURLOPT_SSL_VERIFYPEER通常应该避免设置为 0,但在这种情况下它是安全的,因为正在完成公钥固定。

有关更多详细信息,请参阅此 curl 问题

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章