Rustls TLS Client not trusting

I get this error when a client (that should trust the certificate my HTTPS server is using) tries connecting:

[2023-06-08T01:44:49Z ERROR rustls::conn] TLS alert received: AlertMessagePayloa
d {
        level: Fatal,
        description: UnknownCA,
    }

I'm 100% positive that the certificate is trusted because when I use the certificate with a different webserver backend, it works, but not with the actix-web server I have set up.

The following curl examples are here to add more insight if needed:
(using the following command below)

curl -v https://localhost/endpoint.php -k -X POST

Rust Server (trying to fix):

*   Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server accepted h2
* Server certificate:
*  subject: C=JP; ST=A; L=A; CN=something
*  start date: Jun  5 00:07:59 2023 GMT
*  expire date: Jun  2 00:07:59 2033 GMT
*  issuer: C=JP; ST=A; L=A; CN=something
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* h2 [:method: POST]
* h2 [:scheme: https]
* h2 [:authority: localhost]
* h2 [:path: /endpoint.php]
* h2 [user-agent: curl/8.1.2]
* h2 [accept: */*]
* Using Stream ID: 1 (easy handle 0x56006474af50)
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
> POST /endpoint.php HTTP/2
> Host: localhost
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/2 200 
< content-length: 1
< content-type: text/html
< date: Thu, 08 Jun 2023 01:38:02 GMT
< 
* Connection #0 to host localhost left intact
GOOD

ASP Net Webserver (server I'm translating)

*   Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server accepted h2
* Server certificate:
*  subject: C=JP; ST=A; L=A; CN=something
*  start date: Jun  5 00:07:59 2023 GMT
*  expire date: Jun  2 00:07:59 2033 GMT
*  issuer: C=JP; ST=A; L=A; CN=something
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* h2 [:method: POST]
* h2 [:scheme: https]
* h2 [:authority: localhost]
* h2 [:path: /endpoint.php]
* h2 [user-agent: curl/8.1.2]
* h2 [accept: */*]
* Using Stream ID: 1 (easy handle 0x558eb2a8ff50)
> POST /endpoint.php HTTP/2
> Host: localhost
> User-Agent: curl/8.1.2
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200 
< content-type: text/plain; charset=utf-8
< date: Thu, 08 Jun 2023 01:29:44 GMT
< server: Kestrel
< 
* Connection #0 to host localhost left intact
GOOD

My rustls setup is here:

fn load_rustls_config() -> rustls::ServerConfig {
    // init server config builder with safe defaults
    let config = ServerConfig::builder().with_safe_defaults().with_no_client_auth();

    // load TLS key/cert files
    let cert_file = &mut BufReader::new(File::open("./certs/something.crt").expect("Certificate not found!"));
    let key_file = &mut BufReader::new(File::open("./certs/something.key").expect("Key not found!"));

    // convert files to key/cert objects
    let cert_chain = certs(cert_file).unwrap().into_iter().map(Certificate).collect();
    let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file).unwrap().into_iter().map(PrivateKey).collect();

    // exit if no keys could be parsed
    if keys.is_empty() {
        eprintln!("Could not locate PKCS 8 private keys.");
        std::process::exit(1);
    }

    config.with_single_cert(cert_chain, keys.remove(0)).unwrap()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
    let config = load_rustls_config();
    info!("Certificates loaded.");
    println!("Started!");
    HttpServer::new(|| {
        App::new()
            .service(good_endpoint)
            .route("{path:.*}", web::get().to(index))
    })
    .bind("0.0.0.0:80")?
    .bind("0.0.0.0:5107")?
    .bind_rustls("0.0.0.0:443", config)?
    .run()
    .await
}

Some more information:

The endpoint.php should return the string "GOOD".

I initially thought this was because the client wanted TLS 1.0, but that is not the case, it can use 1.2 and 1.3 just fine. I'm still getting ERROR_WINHTTP_SECURE_FAILURE and I have no idea how to solve it. I'm not able to share the application that is giving this error.

Also, the dotnet server I'm translating uses a pkcs12 cert/key combo file. I translated this crt and key file into the pfx file using this command if it matters:

openssl pkcs12 -export -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -nomac -inkey something.key -in something.crt -out something.pfx

(This is the only pfx format that Windows accepts)

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.