dogweather
HTTP SSL error: "Unknown CA"—going down a Rabbit hole. Maybe an asdf Erlang issue?
I get this error with both :httpc and HTTPoison.get. Here’s HTTPoison:
iex(1)> HTTPoison.get! "https://grad.tamu.edu/"
22:15:54.179 [notice] TLS :client: In state :certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA
** (HTTPoison.Error) {:tls_alert, {:unknown_ca, ~c"TLS client: In state certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA\n"}}
(httpoison 2.2.0) lib/httpoison/base.ex:451: HTTPoison.request!/5
iex:1: (file)
Same with this:
iex(1)> :httpc.request(:get, {'https://grad.tamu.edu/', []}, [], [])
22:24:10.836 [notice] TLS :client: In state :certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA
{:error,
{:failed_connect,
[
{:to_address, {~c"grad.tamu.edu", 443}},
{:inet, [:inet],
{:tls_alert,
{:unknown_ca,
~c"TLS client: In state certify at ssl_handshake.erl:2138 generated CLIENT ALERT: Fatal - Unknown CA\n"}}}
]}}
.tool-versions:
elixir 1.15.2-otp-26
erlang 26.0.2
I’ve seen this pop up periodically over the years. What’s the root cause?
I’m thinking of making an elixir lib that simply shells out to curl, which has no problems:
$ curl --head https://grad.tamu.edu/
HTTP/2 200
cache-control: private
content-length: 46798
content-type: text/html; charset=utf-8
server: Microsoft-IIS/10.0
x-aspnetmvc-version: 5.2
x-aspnet-version: 4.0.30319
x-powered-by: ASP.NET
date: Wed, 15 Nov 2023 05:20:20 GMT
strict-transport-security: max-age=4294967294
EDIT: I made this dead simple Hex Package.
CurlEx.get!(url)
Marked As Solved
voltone
Looks like the server only sends its own certificate, not any intermediary CAs that would allow it to be traced to a trusted root CA. Instead it relies on the client to fetch the intermediate CA(s) from the URL in the Authority Information Access extension. This is supported by browsers and by some CLI tools, but not by Erlang’s ssl. I suspect they would have issues with other non-browser clients too.
For maximum compatibility they should consider adding the trust chain to their server, so it sends all the intermediate CA certificates and the client can complete the trust chain locally without additional network requests.
Also Liked
dch
a few handy tricks for next time:
> curl -v --cert-status https://grad.tamu.edu/
* Trying 128.194.14.62:443...
* Connected to grad.tamu.edu (128.194.14.62) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (OUT), TLS alert, handshake failure (552):
* OpenSSL/3.0.12: error:0A000152:SSL routines::unsafe legacy renegotiation disabled
* Closing connection
curl: (35) OpenSSL/3.0.12: error:0A000152:SSL routines::unsafe legacy renegotiation disabled
TLDR, their system is broken. Your client tries to negotiate downwards until it finds a common TLS standard that it can work with, but eventually gives up, as TLS<1.1 is defacto broken and is generally excluded across the public internet now, if configured correctly.
But why?
> openssl s_client -showcerts -connect grad.tamu.edu:443
CONNECTED(00000003)
0020014A48190000:error:0A000152:SSL routines:final_renegotiate:unsafe legacy renegotiation disabled:/usr/src/crypto/openssl/ssl/statem/extensions.c:894:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7297 bytes and written 326 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID: 509160C19F5059FAA272B38986056F4DF3C0EB536516F4E14EA934EB708E1A32
Session-ID-ctx:
Master-Key:
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1700654113
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
---
Compare that to connecting with s_client again but to tamu.edu and you’ll see that a pile of intermediate certs are sent along with the response for the parent site, which is correct.
For other neat libraries, katipo is a high-performance NIF to libcurl.
voltone
I think you may be trying to call :public_key.pkix_decode_cert on a PEM encoded certificate, but it expect a DER binary. Try one of these:
pem |> :public_key.pem_decode() |> hd() |> elem(1) |> :public_key.pkix_decode_cert(:otp)
pem |> :public_key.pem_decode() |> hd() |> :public_key.pem_entry_decode()
Or try x509 | Hex for a convenient high-level API for working with certificates and other PKI data structures…
dogweather
Texas A&M fixed their configuration. I’m not used to large orgs like this taking outside reports seriously. Pretty nice.
D4no0
It seems that the underlying issue is with the certificate chain. You can take a look at this report.
From ssl erlang side, from which all libraries end up doing the request, it seems that erlang is failing to decode the certificate, cases being either the format being bleeding edge or a incorrect ASN.1 definition, which might be the case here.
I would recommend contacting one of their site administrators and inquiring about that, as it seems that this certificate is used for all of their subdomain sites and most probably was generated manually. This is a potential security threat and should be handled on their side ideally instead of using a workaround in the code.
I will do a investigation on why this happens and return here with a more detailed response.
D4no0
I’m not sure about the implementation, however I’ve downloaded the certificate locally and tried to decode it with :public_key.pkix_decode_cert/2 and it failed.
Another issue is for example when using a custom verify_fun, no peer certificate is returned (even though it should be, as you are validating the certificate and chain manually) only {:error, :unknown_ca}








