n1c0

n1c0

Hackney and client certificates => {:error, :closed}

Hi Guys,

I am trying to do a POST request via hackney (1.14.3).

The curl query is as follow:

curl -v \
--cert /Users/nico/projects/pfx/cert.pem \
--key /Users/nico/projects/pfx/key.pem \
--pass PASSWORD \
--insecure \
--header "Content-type: application/soap+xml; charset=utf-8; action=\"http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe\"" \
--header "Accept: application/soap+xml; charset=utf-8" \
--data "<?xml version=\"1.0\" encoding=\"UTF-8\"?>...</soap12:Envelope>" \
https://www1.nfe.fazenda.gov.br/NFeDistribuicaoDFe/NFeDistribuicaoDFe.asmx

which gives the following results:

* Trying 200.198.239.181...
* TCP_NODELAY set
* Connected to www1.nfe.fazenda.gov.br (200.198.239.181) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: C=BR; O=ICP-Brasil; OU=Secretaria da Receita Federal do Brasil - RFB; OU=ARSERPRO; OU=RFB e-Servidor A1; CN=www1.nfe.fazenda.gov.br
* start date: Sep 19 16:49:12 2018 GMT
* expire date: Sep 19 16:49:12 2019 GMT
* issuer: C=BR; O=ICP-Brasil; OU=Secretaria da Receita Federal do Brasil - RFB; CN=Autoridade Certificadora do SERPRORFB SSL
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> POST /NFeDistribuicaoDFe/NFeDistribuicaoDFe.asmx HTTP/1.1
> Host: www1.nfe.fazenda.gov.br
> User-Agent: curl/7.54.0
> Content-type: application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe"
> Accept: application/soap+xml; charset=utf-8
> Content-Length: 790
> 
* upload completely sent off: 790 out of 790 bytes
* TLSv1.2 (IN), TLS handshake, Hello request (0):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
< HTTP/1.1 200 OK
< Cache-Control: private, max-age=0
< Content-Type: application/soap+xml; charset=utf-8
< Server: Microsoft-IIS/8.5
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Thu, 13 Dec 2018 21:17:00 GMT
< Content-Length: 6380

now when i am trying to do the same with hackney in elixir (1.7.4), i am getting an {:error, :closed}:

url = "https://www1.nfe.fazenda.gov.br/NFeDistribuicaoDFe/NFeDistribuicaoDFe.asmx"
xml_soap = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>...</soap12:Envelope>"

headers = 
  [
    {"Content-type", "application/soap+xml; charset=utf-8; action=\"http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe\""},
    {"Accept", "application/soap+xml; charset=utf-8"}
  ]

opts = [
  :insecure,
  recv_timeout: 30_000,
  ssl_options: [
    certfile: "/Users/nico/projects/pfx/cert.pem",
    keyfile: "/Users/nico/projects/pfx/key.pem",
    password: 'PASSWORD',
    versions: [:'tlsv1.2']
  ]
]

leading to:

iex(8)> :hackney.request(:post, url, headers, xml_soap, opts)
{:error, :closed}

I guess the issue must be in the SSL negotiation, but don’t know how to debug this…any idea?

thanks!

Marked As Solved

voltone

voltone

Fix is now available as part of patch package 21.2.1:

Also Liked

voltone

voltone

Another update: I was able to reproduce the issue against OpenSSL. It turns out it only happens if the client sends data prior to the server’s renegotiation request. The test cases included in OTP for renegotiation between an Erlang client and OpenSSL server perform renegotiation prior to any data exchange.

Based on all the information gathered I opened an issue in the Erlang bug tracker:

If anyone wants to reproduce the issue, first launch an OpenSSL test server in one terminal window (you need a certificate and key, for example a self-signed certificate):

$ openssl s_server -cert server.pem -key server_key.pem -msg
Using default temp DH parameters
ACCEPT

In another terminal window, start an Erlang or Elixir shell and connect to the test server and send some data:

Erlang/OTP 21 [erts-10.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :ssl.start
:ok
iex(2)> {:ok, s} = :ssl.connect('localhost', 4433, [])
{:ok,
 {:sslsocket, {:gen_tcp, #Port<0.7>, :tls_connection, :undefined},
  [#PID<0.128.0>, #PID<0.127.0>]}}
iex(3)> :ssl.send(s, "Hello!")   
:ok
iex(4)> 

At this point, type ‘R’ and hit Enter in the OpenSSL window.

voltone

voltone

Here’s what I’ve found so far:

The issue is not Hackney specific: the same thing happens when connecting with Erlang’s ssl API directly.

The issue is not related to the client certificate: the renegotiation only gets as far as the client sending the ClientHello, and the server immediately closes the connection. The server never gets to the point where it asks for the client certificate.

This appears to be a regression in renegotiation handling in recent OTP versions: when I tried the connection on OTP 21.0.9 the renegotiation succeeded (I don’t have a client certificate, but at least I do get the 403 response). On 21.1 and 21.2 it does not work.

voltone

voltone

I don’t have a valid client certificate, so I can’t fully replicate your scenario, but when I connect to that address with Hackney I see the same response. The important thing to note is that the connection gets closed after the TLS handshake completes.

Does the ‘curl’ command actually show a response from the server? The output you quoted only shows curl trying to send a request.

Last Post!

n1c0

n1c0

Perfect! thanks for your help on that matter

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
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
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

Other popular topics Top

jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44265 214
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement