Public key pinning in curl does not work without using certificates from the system

冷暖自知 提交于 2021-02-08 09:34:07

问题


I am trying to use libcurl with public-key pinning in order to verify a server's authenticity when downloading a file.

Curl is compiled so that it doesn't use any certificates on the system, but only relies on certificates it receives from the user:

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

First I obtain the sha256 sum of the server certificate's public key, as explained here:

$ 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=

Then I set the public key's hash and other related options in 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));
}

The download fails with an error:

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

Well, it seems curl is looking for some certificates, so I recompile it in order for it to include the default certificates:

./configure && make

Now, the download will work:

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

In the CURLOPT_PINNEDPUBLICKEY documentation, it is explained:

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. 

So my impression was that curl only needs the public key from the user, in order to compare it with the public key extracted from the server's certificate.

What am I missing here?


回答1:


The problem is that CURLOPT_SSL_VERIFYPEER being set to 1 enables CA pinning. Curl accepts setting both CA pinning and public-key pinning at the same time, and because CA pinning is tried before public key pinning, the CA pinning fails and it never gets to do the public key pinning.

The solution is to explicitly disable CA pinning when doing public key pinning:

curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0);

This needs to be done explicitly because the default value for CURLOPT_SSL_VERIFYPEER is 1.

NOTE: setting CURLOPT_SSL_VERIFYPEER to 0 should generally be avoided, but in this case it is safe because public-key pinning is being done.

For more details also see this curl issue.



来源:https://stackoverflow.com/questions/52114653/public-key-pinning-in-curl-does-not-work-without-using-certificates-from-the-sys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!