Сообщение об ошибке
received fatal alert: HandshakeFailure
reqwest выводит эту ошибку в конце цепочки ошибок, ureq — как «io: received fatal alert: HandshakeFailure». При подключении к серверу, который поддерживает только TLS 1.0 (tls-v1-0.badssl.com), наш клиент на rustls получил это же оповещение. Другие оповещения выводятся под своими именами из rustls после «received fatal alert: ».
rustls (via reqwest, ureq, tonic) — rustls::Error::AlertReceived(HandshakeFailure)
Что это означает
Сервер ответил на ClientHello от rustls оповещением TLS 40 (handshake_failure). rustls намеренно реализует только современный TLS: TLS 1.2 и 1.3 с обменом ключами ECDHE и шифрами AEAD. Серверы, которым нужны обмен ключами RSA, наборы шифров с CBC, TLS 1.0/1.1 или клиентский сертификат, отклоняют подключение rustls именно так.
Почему это происходит
Ни одного набора шифров, реализованного в rustls
частоУ устаревших серверов, ограниченных наборами шифров с обменом ключами RSA, с CBC-SHA или с 3DES, нет ничего общего с rustls.
Сервер поддерживает только TLS 1.0 или 1.1
частоrustls не реализует версии TLS ниже 1.2.
Сервер требует клиентский сертификат
иногдаЭндпоинты со взаимной TLS-аутентификацией отвечают на TLS 1.2 клиенту без сертификата оповещением handshake_failure.
Как исправить
- 1.
Проверьте, что предлагает сервер
Если сервер не предлагает ни одного набора шифров ECDHE + AEAD, его нужно модернизировать.
bash # What does the server accept? rustls offers TLS 1.2/1.3 with ECDHE + AES-GCM or ChaCha20 only nmap --script ssl-enum-ciphers -p 443 legacy.example.com - 2.
Крайняя мера для устаревшего сервера, который нельзя обновить: native-tls
Отдельный клиент, собранный с feature native-tls в reqwest, использует платформенную библиотеку TLS (в Linux это OpenSSL, и для сборки нужны его заголовочные файлы для разработки), а эта библиотека может ещё разрешать старые наборы шифров, если это допускает системная политика. Используйте такой клиент только для устаревшего эндпоинта; слабая конфигурация сервера по-прежнему остаётся риском.
toml # A legacy server you cannot upgrade: build a separate client on the OS TLS library # (OpenSSL on Linux), which still implements older suites. Cargo.toml: reqwest = { version = "=0.13.5", default-features = false, features = ["native-tls", "blocking"] } - 3.
Взаимная TLS-аутентификация: предъявите клиентский сертификат
Вызовите reqwest::Identity::from_pem для PEM, в котором есть сертификат и закрытый ключ, и передайте полученный Identity в ClientBuilder.
rust // Mutual TLS with rustls: present a client certificate (PEM with certificate + private key) let identity = reqwest::Identity::from_pem(&std::fs::read("client-cert-and-key.pem")?)?; let client = reqwest::Client::builder().identity(identity).build()?;