跳到主要内容
ecdsa.com
rustls (via reqwest, ureq, tonic)Rust

错误信息

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)

含义

服务器用 TLS 告警 40(handshake_failure)响应了 rustls 的 ClientHello。rustls 有意只实现现代 TLS:采用 ECDHE 密钥交换和 AEAD 加密算法的 TLS 1.2 和 1.3。如果服务器要求 RSA 密钥交换、CBC 套件、TLS 1.0/1.1 或客户端证书,就会以这种方式拒绝它。

原因

修复方法

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

    旧服务器无法维护时的最后手段:native-tls

    用 reqwest 的 native-tls 特性构建一个专用客户端,会使用平台的 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. 3.

    双向 TLS:提供客户端证书

    用包含证书和私钥的 PEM 调用 reqwest::Identity::from_pem,并把得到的身份设置到构建器上。

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

相关错误

浏览完整的签名错误库