reqwest icon indicating copy to clipboard operation
reqwest copied to clipboard

Failing to connect with rustls-tls in reqwest 0.12

Open gustavowd opened this issue 5 months ago • 18 comments

I've been using the reqwest library for a long time with rustls-tls and self-signed certificates. However, when upgrading to version 0.12 the connection fails and the returning error does not help much. It simply says connection error.

What changed in this version that could cause the failure?

The code is very much like this:

  let mut contents_cert = Vec::new();
  File::open("./pki/ECC-secp256r1/ca_cert_and_key.pem").unwrap()
      .read_to_end(&mut contents_cert).unwrap();
  let root_cert = reqwest::Certificate::from_pem(&contents_cert).unwrap();

  let mut client_cert = Vec::new();
  let cert_and_key_name = "./pki/ECC-secp256r1/meter_client01_cert_and_key.pem".to_string();
  File::open(cert_and_key_name).unwrap()
      .read_to_end(&mut client_cert).unwrap();
  let identify = reqwest::Identity::from_pem(&client_cert).unwrap();

  // Cria um cliente que ira fazer as requisições para o servidor IEEE 2030.5
  let client = Client::builder()
      .timeout(Duration::from_secs(5))
      .add_root_certificate(root_cert)
      .identity(identify)
      .connection_verbose(true)
      .https_only(true)
      .danger_accept_invalid_certs(true) // para rustls
      .max_tls_version(reqwest::tls::Version::TLS_1_2)
      .use_rustls_tls()
      .build().unwrap();

      let result = client.get (url.clone())
          .send()
          .await;

Thank you very much in advance, Gustavo

gustavowd avatar Mar 20 '24 19:03 gustavowd

Interesting. Does it work with native-tls? Could you enable logging to see the logs from rustls?

You'll need to enable rustls/logging of v0.22.x, and turn on a logger such as env-logger.

seanmonstar avatar Mar 20 '24 19:03 seanmonstar

I can add that the peer_certificate() method now returns only first 2 bytes of server's TLS certificate (I also use rustls) https://github.com/seanmonstar/reqwest/blob/7a5df2126081b83d29758ef31f9f38369ac85ae4/src/tls.rs#L583

Not sure if its related, since other than that the connections works as expected

mbme avatar Mar 21 '24 11:03 mbme

@djc if you have time, does either of the above issues sound familiar? Perhaps I goofed something when updating.

seanmonstar avatar Mar 21 '24 12:03 seanmonstar

I can add that the peer_certificate() method now returns only first 2 bytes of server's TLS certificate (I also use rustls)

https://github.com/seanmonstar/reqwest/blob/7a5df2126081b83d29758ef31f9f38369ac85ae4/src/tls.rs#L583

Not sure if its related, since other than that the connections works as expected

This definitely seems unrelated.

I've been using the reqwest library for a long time with rustls-tls and self-signed certificates. However, when upgrading to version 0.12 the connection fails and the returning error does not help much. It simply says connection error.

What is the exact error? @seanmonstar are you wrapping rustls errors somehow? We usually try pretty hard to provide detailed errors.

djc avatar Mar 21 '24 13:03 djc

I mean, it's wrapped in a reqwest::Error, but it has the io::Error as a source. I suspect there is more useful information available, we just need it to understand the problem :)

seanmonstar avatar Mar 21 '24 14:03 seanmonstar

I finally got the error:

Error: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("utfpr.edu.br")), port: Some(8443), path: "/upt", query: None, fragment: None }, source: Error { kind: Connect, source: Some(Custom { kind: Other, error: Custom { kind: InvalidData, error: PeerMisbehaved(SignedKxWithWrongAlgorithm) } }) } }

gustavowd avatar Mar 22 '24 14:03 gustavowd

I finally got the error:

Error: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("utfpr.edu.br")), port: Some(8443), path: "/upt", query: None, fragment: None }, source: Error { kind: Connect, source: Some(Custom { kind: Other, error: Custom { kind: InvalidData, error: PeerMisbehaved(SignedKxWithWrongAlgorithm) } }) } }

It seems to be certificate signature algorithm differing from signature algorithm negotiated for connection. However, i'm using the same certificates that i used with reqwest 0.11.27.

gustavowd avatar Mar 22 '24 14:03 gustavowd

Would you be able to post a packet capture of this failure happening, and/or warn-level logs emitted from the rustls client? (RUST_LOG=warn if your process incorporates env_logger).

ctz avatar Mar 22 '24 14:03 ctz

Might also help if you can disclose the server that you are trying to connect to.

djc avatar Mar 22 '24 14:03 djc

The server domain is in the error message. Also, just noting down some versions: the upgrade went from rustls 0.21 to 0.22 (not yet on 0.23).

seanmonstar avatar Mar 22 '24 14:03 seanmonstar

Would you be able to post a packet capture of this failure happening, and/or warn-level logs emitted from the rustls client? (RUST_LOG=warn if your process incorporates env_logger).

[2024-03-22T14:25:47Z WARN rustls::client::tls12] peer signed kx with wrong algorithm (got Unknown(0) expect [ED25519, ECDSA_NISTP521_SHA512, ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256]) Error: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("utfpr.edu.br")), port: Some(8443), path: "/upt", query: None, fragment: None }, source: Error { kind: Connect, source: Some(Custom { kind: Other, error: Custom { kind: InvalidData, error: PeerMisbehaved(SignedKxWithWrongAlgorithm) } }) } }

gustavowd avatar Mar 22 '24 14:03 gustavowd

Might also help if you can disclose the server that you are trying to connect to.

The server is implemented in Java (spring boot). Remember that i said that i'm using the same code / certificates for at least two years. It just stop to work with reqwest 0.12.

gustavowd avatar Mar 22 '24 14:03 gustavowd

My certificates are ECDSA_SECP256R1.

gustavowd avatar Mar 22 '24 14:03 gustavowd

Same error with reqwest 0.12.1, with returned to rustls 0.21.

[2024-03-22T14:57:37Z WARN rustls::client::tls12] peer signed kx with wrong algorithm (got Unknown(0) expect [ED25519, ECDSA_NISTP521_SHA512, ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256])

gustavowd avatar Mar 22 '24 14:03 gustavowd

Would you be able to post a packet capture of this failure happening, and/or warn-level logs emitted from the rustls client? (RUST_LOG=warn if your process incorporates env_logger).

[2024-03-22T14:25:47Z WARN rustls::client::tls12] peer signed kx with wrong algorithm (got Unknown(0) expect [ED25519, ECDSA_NISTP521_SHA512, ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256]) Error: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("utfpr.edu.br")), port: Some(8443), path: "/upt", query: None, fragment: None }, source: Error { kind: Connect, source: Some(Custom { kind: Other, error: Custom { kind: InvalidData, error: PeerMisbehaved(SignedKxWithWrongAlgorithm) } }) } }

Thanks!

Over here https://github.com/seanmonstar/reqwest/blob/master/src/tls.rs#L557-L558 offers SignatureScheme::ECDSA_SHA1_Legacy, but rustls has so little support for ECDSA-SHA1 nowadays that it cannot check that it is compatible with the selected ciphersuite.

We could resolve that by:

  • reqwest doesn't offer SHA1 -- but that seems silly in the context of the purpose of that code
  • rustls could regain enough knowledge about SHA1 to pass this check -- I think this resolution seems preferable?

ctz avatar Mar 22 '24 15:03 ctz

So, getting to the bottom of the cause of this on discord with @cpu :

In rustls 0.22 we removed the default function definition for ServerCertVerifier::supported_verify_schemes. In eb94f26919e881177ad3d9cd172f47d1a8263799 (contained in reqwest 0.12.0 & 0.12.1) this was supplied in reqwest's verifier (see https://github.com/seanmonstar/reqwest/blob/master/src/tls.rs#L557-L558, compare to https://github.com/rustls/rustls/commit/e9c15abe0633416aefc783bbc5f7ab525e899c77#diff-6a346e34b62dcc7aa7ea373ca29791ac4c81c55202d265397418507d3235a4e3R408) but with a larger than previous set of offered signature schemes (and, because that list is in priority order, SHA1 is preferred).

ctz avatar Mar 22 '24 15:03 ctz

So should I update reqwest to pick a different set of signature schemes?

seanmonstar avatar Mar 22 '24 19:03 seanmonstar

I think that's one option - you could change your NoVerifier impl to return the default list we were using before it was removed from the trait. I think you could also just remove SignatureScheme::ECDSA_SHA1_Legacy.

On the other hand, Ctz worked up a fix on our side as well: https://github.com/rustls/rustls/pull/1869 and we're backporting it into 0.22: https://github.com/rustls/rustls/pull/1870 I believe taking that fix in an updated Rustls dependency would also resolve the problem without needing a change on your side.

cpu avatar Mar 22 '24 21:03 cpu

Fix for this is in:

  • https://crates.io/crates/rustls/0.23.4
  • https://crates.io/crates/rustls/0.22.3

ctz avatar Mar 25 '24 15:03 ctz

Thank you all! With that, and another release of reqwest this morning, a cargo update should help. Feel free to open an issue again if there's different problems.

seanmonstar avatar Mar 25 '24 15:03 seanmonstar

Thank both of you. I tested reqwest 0.12.2 and it working again. :)

gustavowd avatar Mar 25 '24 17:03 gustavowd