Rustls vs. ancient RSA 1024 bit root cert,

I'm trying to use "rustls" to talk to a site with an old self-signed certificate. I have the appropriate root certificate and can get it to parse OK. The problem is that the root certificate is ancient, from 2005, with a 1024 bit RSA key. That key length has been deprecated.

OpenSSL will accept this, but only if I turn the "auth_level" down to 1.

openssl s_client -verifyCAfile LindenLab.crt -connect simhost-0cf44003add587ba0.aditi.secondlife.io:12043 -verify_return_error -auth_level 1

This will fail with an auth_level > 1.

But, even when I feed the root cert into rustls, like this:

fn new_tlsconfig(local_certs: &[&str]) -> Result<rustls::ClientConfig, Error> {
        let mut config = rustls::ClientConfig::new(); // empty TLS config
        config
            .root_store
            .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); // add all Mozilla root certs
        for certstring in local_certs {
            // add additional local certs
            let mut buf = std::io::Cursor::new(certstring.as_bytes().to_vec()); // string to readable
            let (good, bad) = match config.root_store.add_pem_file(&mut buf) {
                Ok((good, bad)) => (good, bad),
                Err(_) => return Err(anyhow!("Built-in TLS certificate load error.")),
            };
            log::info!("Added {} special TLS certificates", good);
            if good == 0 || bad > 0 {
                return Err(anyhow!(
                    "Built-in TLS certificate load error. {} loaded, {} failed.",
                    good,
                    bad
                ));
            }
        }
        Ok(config)
    }

This says it loaded 1 good and 0 bad certs, so it parsed the cert OK. That does not, per the documentation, mean it's cryptographically valid.

I get

Connection Failed: invalid certificate: UnknownIssuer'

Is there some way to get "rustls" to accept this? It's not in the list of "non-features" they don't support by choice. But there's no visible way to set "auth level" or some equivalent thereof, that I can find.

The published self-signed root cert is at https://bitbucket.org/lindenlab/llca/raw/master/LindenLab.crt.

Note that the domain simhost-0cf44003add587ba0.aditi.secondlife.io will disappear after a while; those names are generated dynamically. So testing this way may not work tomorrow.

(Why? I'm writing a new client for Second Life, and talking to some rather old server code.)

It's deliberately not supported.

Yes, I know the Second Life system is way out of date. I can file a bug report there. Mean time to repair on Second Life bug reports is 1-2 years.

If you happen to find a solution, I'd like to hear about it. (I haven't needed to operate with outdated crypto in a Rust project yet, but many many times in other contexts, so it'd be nice to know of any options.)

This area is very frustrating. See this article.

The actual certificate hierarchy is:

0: Linden Lab root CA certificate, RSA 1024, obsolete.
1: Server certificate, non-CA, RSA 2048, valid, found on each of several thousand servers.

I have copies of both certs. Loading 0) as a trust root is ignored, because RSA 1024 is obsolete. So I tried loading 1) into rustls, parsing the cert with rustls_pemfile::certs and then loading it with

config.root_store.add(...)

The cert loading all happens without any error returns, but doesn't seem to help. I'm trying here to add a non-CA cert as a trust root, which doesn't work because it's neither a CA nor self-signed. I didn't really expect that to work, but it was worth a try as a workaround.

Any other ideas?

After way too much work, I've managed to develop a workaround.

"rustls" has a "dangerous()" method on ClientConfig which allows defining your own certificate verifier. This requires creating a struct with a type of the ServerCertVerifier trait and implementing verify_server_cert in it. You can then accept or reject cert chains yourself in that function.

If you're only doing this for some special domain, after making your own checks in your own server_cert_verifier, call

rustls::WebPKIVerifier::verify_server_cert(& rustls::WebPKIVerifier::new(), roots, presented_certs, dns_name, ocspResponse)

for anything that doesn't need special handling. That will perform the standard checks.

When you do this, you have to make it very clear in Cargo.toml:

rustls = {version = "0.19", features = ["dangerous_configuration"]}

The "rustls" people really don't want people doing this unless absolutely necessary.

You also have to bring in "webpki". But, due to a breaking change in "webpki", the current version of "rustls" needs webpki 0.21.0, not the current 0.22.0. (DNSNameRef has been renamed to DnsNameRef, among other minor changes, breaking rustls.)

Your code will have to be fixed then "rustls" catches up with the breaking changes in "webpki", of course.

Not fun. Reduces security. But do-able.

1 Like

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.