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)