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) 
1 Like

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.

1 Like

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.

2 Likes

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}

1 Like

I’m not having any issues decoding the server’s certificate using public_key. The only reason ssl rejects it is because it can’t find that certificate’s issuer (/C=US/O=Internet2/CN=InCommon RSA Server CA 2).

If that certificate were included in the chain sent by the server, then the chain to /C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority could be completed and the certificate verification would succeed.

1 Like

Strange, I will try it again later, are you using openssl v1 or v3?

Does not matter: OpenSSL is not used by public_key for decoding certificates.

Here is the PEM encoded server certificate, for reference:

-----BEGIN CERTIFICATE----- MIIPnjCCDgagAwIBAgIRALFxge5FKu03igK//v8wV1owDQYJKoZIhvcNAQEMBQAw RDELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUludGVybmV0MjEhMB8GA1UEAxMYSW5D b21tb24gUlNBIFNlcnZlciBDQSAyMB4XDTIzMTEwOTAwMDAwMFoXDTI0MTEwODIz NTk1OVowXzELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMR8wHQYDVQQKDBZU ZXhhcyBBICYgTSBVbml2ZXJzaXR5MR8wHQYDVQQDExZ3ZWItY21zLWxiLmFzLnRh bXUuZWR1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArOM6EOdBPsE0 agjTJmBIWaWWFWsRhDDVJOvASSR4p6bcCjgWSgkdm+TLFUEbfAsfvgs3hJu0TY56 1qC5r+YYSWhar+0BK1Fc065jsBiZc1OSeGp5e0tAkfS+CcGGDzCdTgXVzbTOlwMC FU9zQnbKxEyP2MQe1yTp4ohAD86EVXmmnwwuWIketm2CYMbcMGvIq9yPpMuJasGh hCFuJj/k3ogZd5fIDQS0ZK/1WpiaxNK6LEQ8zZjG1TlYgtYAfN5kUSGkJbtrnaiP A0cP+6fNxekB2tlyr5RzDjl0fgblBLDuVVTglVdJiWtkruFMbVkOH1jC7ztIGcNP aCjLWBHhc9gbGSO04SgapOiLDMB5ZhSab/SVK6RBYVEhP9dte11v+0WgLPdqXwfF Z8qOJ/YxltTGkuUEViM58NiYXvIpUpwVTA6bVLyhPpB2W9nSfS6Zi8a0dukU+gHh p+v7TLXQc++V5VFtIsiBgeTsMnS1KZvSifCozHgs3We5ow+UETSyF8pPZcLWXfbY z+j927T+xXF4MxJL03o6nfL9bneAE7SIUtLD9rkymzyQF99hmi+SzYrt/FAVTNJ+ FxaPoBokkd9Cn0Y3vyftq7TauvF5BzxKov7kaAm9sfjgAYDbpk0SNCYKEMhS0Q65 nl66mzCMojuw8LDXw0HTP5Dwzs+ukpcCAwEAAaOCCu4wggrqMB8GA1UdIwQYMBaA FO9MAJKm+3YuXpXiyV+HGxnVTeLZMB0GA1UdDgQWBBTVR/Y7TXwSVdbwJAO08keX O8daDDAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggr BgEFBQcDAQYIKwYBBQUHAwIwSQYDVR0gBEIwQDA0BgsrBgEEAbIxAQICZzAlMCMG CCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgIwQAYD VR0fBDkwNzA1oDOgMYYvaHR0cDovL2NybC5zZWN0aWdvLmNvbS9JbkNvbW1vblJT QVNlcnZlckNBMi5jcmwwcAYIKwYBBQUHAQEEZDBiMDsGCCsGAQUFBzAChi9odHRw Oi8vY3J0LnNlY3RpZ28uY29tL0luQ29tbW9uUlNBU2VydmVyQ0EyLmNydDAjBggr BgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wggF8BgorBgEEAdZ5AgQC BIIBbASCAWgBZgB1AHb/iD8KtvuVUcJhzPWHujS0pM27KdxoQgqf5mdMWjp0AAAB i7ZAX3wAAAQDAEYwRAIgcw+Km0Lp0M5Ys2W0Dz0EHp2tEQhrSHg0YnK0x07O0CcC IGuBz8dOXJdSVch1eXpHT57V3xGsrthTfyYKEGO9W0sCAHUAPxdLT9ciR1iUHWUc hL4NEu2QN38fhWrrwb8ohez4ZG4AAAGLtkBgFgAABAMARjBEAiAsO8+RgpHTdK5G ppRR7W5XfoFQdNB0Vr5jwVXGQ7RagwIgI+J7UvPXYBfYbTevcVxkiE4eOyW+Bo2C nEVIsQIL0gIAdgDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYu2 QF+zAAAEAwBHMEUCIQCaqIv6Y4k21dKTJOIWrp/1bLl0IoDHKR9/EUgBUqjBNQIg KR1cNczB1RE0kEAG6gHDwDVKU5g9uhWlOg0u65dFywAwggfqBgNVHREEggfhMIIH 3YIWd2ViLWNtcy1sYi5hcy50YW11LmVkdYINYWJwYS50YW11LmVkdYITYWRtaW4u YWJwYS50YW11LmVkdYIdYWRtaW4uYWNjb3VudGFiaWxpdHkudGFtdS5lZHWCGWFk bWluLmFkbWlzc2lvbnMudGFtdS5lZHWCGWFkbWluLmFnZXAtdHhhcm0udGFtdS5l ZHWCFGFkbWluLmFnZ2llLnRhbXUuZWR1ghJhZG1pbi5hc2MudGFtdS5lZHWCGWFk bWluLmFzc2Vzc21lbnQudGFtdS5lZHWCE2FkbWluLmF2cGEudGFtdS5lZHWCG2Fk bWluLmNhcmVlcmNlbnRlci50YW11LmVkdYIUYWRtaW4uY2lydGwudGFtdS5lZHWC F2FkbWluLmNwZWRpbmZvLnRhbXUuZWR1ghJhZG1pbi5jdGUudGFtdS5lZHWCF2Fk bWluLmRpc3RhbmNlLnRhbXUuZWR1ghhhZG1pbi5kaXZlcnNpdHkudGFtdS5lZHWC HGFkbWluLmZhY3VsdHlzZW5hdGUudGFtdS5lZHWCG2FkbWluLmZpbmFuY2lhbGFp ZC50YW11LmVkdYIVYWRtaW4uZ2xvYmFsLnRhbXUuZWR1ghNhZG1pbi5ncmFkLnRh bXUuZWR1ghVhZG1pbi5sYXVuY2gudGFtdS5lZHWCEmFkbWluLmxtcy50YW11LmVk dYIUYWRtaW4ubWJpb3QudGFtdS5lZHWCEmFkbWluLm1sYy50YW11LmVkdYIdYWRt aW4ubXZjY29udGVudDEuYXMudGFtdS5lZHWCHWFkbWluLm12Y2NvbnRlbnQyLmFz LnRhbXUuZWR1gh1hZG1pbi5tdmNjb250ZW50My5hcy50YW11LmVkdYIdYWRtaW4u bXZjY29udGVudDQuYXMudGFtdS5lZHWCHWFkbWluLm12Y2NvbnRlbnQ1LmFzLnRh bXUuZWR1ghthZG1pbi5tdmNzdGFnZTEuYXMudGFtdS5lZHWCG2FkbWluLm12Y3N0 YWdlMi5hcy50YW11LmVkdYIbYWRtaW4ubXZjc3RhZ2UzLmFzLnRhbXUuZWR1ghth ZG1pbi5tdmNzdGFnZTQuYXMudGFtdS5lZHWCG2FkbWluLm12Y3N0YWdlNS5hcy50 YW11LmVkdYIUYWRtaW4ub2FicGEudGFtdS5lZHWCF2FkbWluLnBhbmRlbWljLnRh bXUuZWR1ghlhZG1pbi5wa3B0YW11MDUzLnRhbXUuZWR1ghZhZG1pbi5wcm92b3N0 LnRhbXUuZWR1ghhhZG1pbi5yZWdpc3RyYXIudGFtdS5lZHWCHWFkbWluLnN0dWRl bnRzdWNjZXNzLnRhbXUuZWR1ghdhZG1pbi5zdHVkeWh1Yi50YW11LmVkdYIZYWRt aW4udGFtdXMtYWdlcC50YW11LmVkdYIWYWRtaW4udGVzdGluZy50YW11LmVkdYIT YWRtaW4udHRsYy50YW11LmVkdYIRYWRtaW4udXMudGFtdS5lZHWCEmFkbWluLnV3 Yy50YW11LmVkdYIUYWRtaW4udmlzaXQudGFtdS5lZHWCE2FkbWluLnZtbGMudGFt dS5lZHWCE2FkbWlzc2lvbnMudGFtdS5lZHWCDmFnZ2llLnRhbXUuZWR1ghVhZ2dp ZW9uZXN0b3AudGFtdS5lZHWCD2FnZ2llcy50YW11LmVkdYIMYXNjLnRhbXUuZWR1 ghVjYXJlZXJjZW50ZXIudGFtdS5lZHWCHGNvbnRpbnVpbmdlZHVjYXRpb24udGFt dS5lZHWCEWNwZWRpbmZvLnRhbXUuZWR1ggxjdGUudGFtdS5lZHWCFmZhY3VsdHlz ZW5hdGUudGFtdS5lZHWCFWZpbmFuY2lhbGFpZC50YW11LmVkdYIPZ2xvYmFsLnRh bXUuZWR1gg1ncmFkLnRhbXUuZWR1gg9sYXVuY2gudGFtdS5lZHWCE21hdGhjZW50 ZXIudGFtdS5lZHWCDm1iaW90LnRhbXUuZWR1ggxtbGMudGFtdS5lZHWCF212Y2Nv bnRlbnQxLmFzLnRhbXUuZWR1ghdtdmNjb250ZW50Mi5hcy50YW11LmVkdYIXbXZj Y29udGVudDMuYXMudGFtdS5lZHWCF212Y2NvbnRlbnQ0LmFzLnRhbXUuZWR1ghdt dmNjb250ZW50NS5hcy50YW11LmVkdYIVbXZjc3RhZ2UxLmFzLnRhbXUuZWR1ghVt dmNzdGFnZTIuYXMudGFtdS5lZHWCFW12Y3N0YWdlMy5hcy50YW11LmVkdYIVbXZj c3RhZ2U0LmFzLnRhbXUuZWR1ghVtdmNzdGFnZTUuYXMudGFtdS5lZHWCFnBpdG9j ZG5jc3MuYXMudGFtdS5lZHWCGHBpdG9jZG5tZWRpYS5hcy50YW11LmVkdYIacGl0 b2NkbnNjcmlwdHMuYXMudGFtdS5lZHWCE3BrcHRhbXUwNTMudGFtdS5lZHWCEnJl Z2lzdHJhci50YW11LmVkdYIRc3R1ZHlodWIudGFtdS5lZHWCE3RhbXVzLWFnZXAu dGFtdS5lZHWCFnRlc3RjbXNpdGUuYXMudGFtdS5lZHWCEHRlc3RpbmcudGFtdS5l ZHWCDXR0bGMudGFtdS5lZHWCC3VzLnRhbXUuZWR1gg52aXNpdC50YW11LmVkdTAN BgkqhkiG9w0BAQwFAAOCAYEAFWtuggQYnTnV0GUK/SrRvGkQk+GEdIEGulnp+sVw x2cszdDPG7kJDr70RTrTFgAT72XCxM7FY4/UDducSDLm0x791tNMZXA7jpgfHYe6 mWesw+CEtGzT55/qPKGnyTyjex4RcPwC8juiL6QHr6WiVyvbDlhqdwkZpz1Qy/Um GTXYPO9VF3opI8BnXF9/32C+mlKZRawVl5OFlLJvbZA70hxlUsH7x9OX79i7eGCL yq0rbJJyfhsfoIFfSzN6yaPb3o5j30rTaCZz6+QqUczlOWTRLKibi1daQFGOmADQ VKKySTN+TR7j+tvvH4yUi8Vis8+/vGbvUyEyiZCeaFWTtufB8ck3CNEBV46NxRzo 1DkMheFCiK23ydIHdOlKDauW98Yf1kprp6Ts2uS8v5YXE3nuVJQ5q5qdkS+xQpB6 V+aKGQTYXX4C8YC9hte3V7a2jX8vdk6hfTKT6nKidwcSLGhUxqo7F5YGotFGrj2/ zZEn2+h9sXdhBWahyIpDofL8 -----END CERTIFICATE----- 
1 Like

I can confirm that this doesn’t work:

** (MatchError) no match of right hand side value: {:error, {:asn1, {{:invalid_value, 3}, [{:asn1rt_nif, :decode_ber_tlv, 1, [file: ~c"asn1rt_nif.erl", line: 93]}, {:“OTP-PUB-KEY”, :decode, 2, [file: ~c"…/src/OTP-PUB-KEY.erl", line: 1233]}, {:pubkey_cert_records, :decode_cert, 1, [file: ~c"pubkey_cert_records.erl", line: 40]}, {:public_key, :pkix_decode_cert, 2, [file: ~c"public_key.erl", line: 522]}, {:elixir, :“-eval_external_handler/1-fun-2-”, 4, [file: ~c"src/elixir.erl", line: 376]}, {:erl_eval, :do_apply, 7, [file: ~c"erl_eval.erl", line: 750]}, {:elixir, :eval_forms, 4, [file: ~c"src/elixir.erl", line: 361]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel_checker.ex", line: 112]}]}}}
public_key.erl:526: :public_key.pkix_decode_cert/2
iex:2: (file)

I am using erlang 26.1.2, tried both with otp and plain options.

1 Like

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…

2 Likes

I think you may be trying to call :public_key.pkix_decode_cert on a PEM encoded certificate

That was exactly what I was doing, thanks!

Very handy library, I will use it for sure in the future, thanks!

I chatted with Texas A&M IT helpdesk. They said they’ll send the report on. We’ll see!

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.

3 Likes

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

2 Likes