dogweather

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

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

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

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

dogweather

Texas A&M fixed their configuration. I’m not used to large orgs like this taking outside reports seriously. Pretty nice.

D4no0

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

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}

Where Next?

Popular in Questions Top

Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New

Other popular topics Top

belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement