mTLS Websocket Client

I’m struggling to get a websocket client that uses mTLS when communicating with a websocket server using a “wss://“ URL. The PKI hierarchy is private so both client and server need to be able to present BOTH root CA certs and a client cert.

I have found some Rust examples using various libraries, but none does mTLS. For example, rust-websockets will do TLS, but has no facility for providing a client cert. The tokio-rustls library supports a full featured TlsConnector (that allows a client cert to be specified in addition to a CA cert chain), but doesn’t support client-side web sockets (that sends the appropriate “upgrade” headers).

Ideally there would be a client side library that uses tokio-rustls‘s TcpConnector.

Can anyone provide help?

You should be able to pair tokio-rustls and tokio-tungstenite to do that.

Well, I made some progress, and have what I think SHOULD be a working program. However, I get the following error when invoking it:

thread 'main' panicked at src/main.rs:123:55:
called `Result::unwrap()` on an `Err` value: Error { code: -50, message: "One or more parameters passed to a function were not valid." }

The error is coming from this line:

    let identity = Identity::from_pkcs8(&certs, &key).unwrap();

And what I'm providing as parameters comes from here:

    let mut cert_file = File::open("foo.crt").unwrap();
    let mut certs = vec![];
    cert_file.read_to_end(&mut certs).unwrap();
    let mut key_file = File::open("foo.key").unwrap();
    let mut key = vec![];
    key_file.read_to_end(&mut key).unwrap();

Where both foo.crt and foo.key contains PKCS#8-encoded certificate and key.
These files work perfectly fine with other apps, including "openssl s_client", where I have tested them against the server that I'm trying to connect to.

Anyone recognize that error message or could tell me how to track down what it is unhappy with?

Here is the stack backtrace. I'm not sure if it helps:

stack backtrace:
   0: rust_begin_unwind
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/result.rs:1654:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/result.rs:1077:23
   4: test2::main::{{closure}}
             at ./src/main.rs:123:20
   5: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/park.rs:281:63
   6: tokio::runtime::coop::with_budget
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/coop.rs:107:5
   7: tokio::runtime::coop::budget
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/coop.rs:73:5
   8: tokio::runtime::park::CachedParkThread::block_on
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/park.rs:281:31
   9: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/context/blocking.rs:66:9
  10: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/scheduler/multi_thread/mod.rs:87:13
  11: tokio::runtime::context::runtime::enter_runtime
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/context/runtime.rs:65:16
  12: tokio::runtime::scheduler::multi_thread::MultiThread::block_on
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/scheduler/multi_thread/mod.rs:86:9
  13: tokio::runtime::runtime::Runtime::block_on
             at /Users/eswenson/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/runtime/runtime.rs:349:45
  14: test2::main
             at ./src/main.rs:154:5
  15: core::ops::function::FnOnce::call_once
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/ops/function.rs:250:5

In case it helps to look at the whole program, here it is:

https://s3.amazonaws.com/eswenson/public/main.rs

Please be gentle. I've only been programming in Rust for 3 days. I cobbled together this program after doing the "learn Rust" tutorial and after looking at code in the various related packages (tokio-tungstenite, tokio-rustls) and borrowing code from examples as needed to get to a program that compiles and runs.

Note: I had an earlier verison of this program that DIDN'T specify the .identity(...) call in the TlsConnector builder -- and that one worked fine -- but of course failed to connect to my websocket server becuase the latter required a client certificate (mTLS). So I simply copied code from simple_server_pkcs8.rs in rust-native-tls/examples to create the Identity and now my test program panics. Help please!

I tried running the simple_server_pkcs8.rs in rust-native-tls/examples and it failed in the same way. So I found another example that used a PKCS#12 file instead, and changed my code to use it. That code looks like:

    let mut file = File::open("identity.pfx").unwrap();
    let mut identity = vec![];
    file.read_to_end(&mut identity).unwrap();
    let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap();

At first, this didn't work -- giving me some error about the MAC verification failed on the PKCS#12 file (suggesting that it was an incorrect passphrase). I knew it wasn't that because I created identity.pfx with an openssl command line and specified "hunter2" as the password.

It turned out that I needed to add a -legacy option to the openssl pkcs12 command line. So rust-native-tls must not support the "non-legacy" PKCS#12 files.

In any case, my test code now connects to my websocket server with successful mutual authentication.

But WHY does the use of PKCS#8 PEM files not work for the certificates and key with Identity::from_pkcs8?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.