Skip to content
ecdsa.com
rustls (via reqwest, ureq, tonic)Rust

Error message

received fatal alert: HandshakeFailure

reqwest prints it at the end of its error chain, ureq as "io: received fatal alert: HandshakeFailure". Against a TLS 1.0-only server (tls-v1-0.badssl.com) our rustls client received this same alert. Other alerts appear with their own rustls names after "received fatal alert: ".

rustls (via reqwest, ureq, tonic) — rustls::Error::AlertReceived(HandshakeFailure)

What it means

The server answered rustls's ClientHello with TLS alert 40 (handshake_failure). rustls deliberately implements only modern TLS: TLS 1.2 and 1.3 with ECDHE key exchange and AEAD ciphers. Servers that require RSA key exchange, CBC suites, TLS 1.0/1.1 or a client certificate refuse it this way.

Why it happens

How to fix it

  1. 1.

    Check what the server offers

    If it offers no ECDHE + AEAD suite, the server needs to be modernised.

    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. 2.

    Last resort for an unmaintainable legacy server: native-tls

    Building a dedicated client with reqwest's native-tls feature uses the platform TLS library (OpenSSL on Linux, which needs its development headers to build), and that library may still allow older suites, subject to the system policy. Keep it scoped to the legacy endpoint; the server's weak configuration remains a risk.

    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. 3.

    Mutual TLS: present a client certificate

    reqwest::Identity::from_pem with a PEM containing the certificate and private key, set on the builder.

    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()?;

Related errors

Browse the full signature error database