Я пытаюсь использовать 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 нужен только открытый ключ от пользователя, чтобы сравнить его с открытым ключом, извлеченным из сертификата сервера.
Что мне здесь не хватает?