httpclient icon indicating copy to clipboard operation
httpclient copied to clipboard

support OpenSSL::SSL::SSLContext#add_certificate

Open dmlond opened this issue 1 year ago • 0 comments

we are running into an issue using a wrapper client httpi that is using httpclient under the hood. We are trying to post a request using SSL authentication to a U.S. government system. We have a single certificate with the entire certificate chain, with its key. We can make curl calls using the --cert and --key arguments pointing to these files to this website.

curl -v --cert our.chained.cer --key our.key --header "Content-Type: text/xml;charset=UTF-8" --header '{"SOAPAction"=>"\soapUrl\", "Content-Type"=>"text/xml;charset=UTF-8", "Content-Length"=>"1066"}' --data @data.xml 'https://government.url'

But calls to the same site using httpclient do not work. I would expect the following to be equivalent

require 'httpclient'

cert_file = File.read('our.chained.cer') # pem encoeded certificate file
key_file = File.read('our.key')

client = HTTPClient.new
client.ssl_config.client_cert = OpenSSL::X509::Certificate.new(cert_file)
client.ssl_config.client_key = OpenSSL::PKey::RSA.new(key_file)
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
client.ssl_config.ssl_version = :TLSv1_2.to_s

host = 'https://government.url'
uri = URI(host)
body = File.read('data.xml')
headers = {
    'SOAPAction' => 'soapUrl',
    'Content-Type' => 'text/xml;charset=UTF-8', 
    'Content-Length' => body.length.to_s
}
res = client.post(uri, body, headers)

But this raises an SSL error: /usr/local/bundle/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:103:in `connect': SSL_connect returned=1 errno=0 peeraddr=52.54.170.122:443 state=error: sslv3 alert certificate unknown (OpenSSL::SSL::SSLError)

In my investigations, I was able to use net/http to make a successful request using the :extra_chain_cert method

require 'openssl'
require 'net/http'

bundle = File.read('our.chained.cer') # pem encoeded certificate file
bundle_certs = bundle.scan(/-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/)
client_cert = OpenSSL::X509::Certificate.new(bundle_certs[0])
intermediate_cert = OpenSSL::X509::Certificate.new(bundle_certs[1])

options = {
  use_ssl: true,
  verify_mode: OpenSSL::SSL::VERIFY_PEER,
  ssl_version: :TLSv1_2,
  cert: client_cert,
  extra_chain_cert: [intermediate_cert],
  key: OpenSSL::PKey::RSA.new(File.read('our.key'))
}

host = 'https://government.url'
uri = URI(host)
body = File.read('data.xml')
req = Net::HTTP::Post.new(uri)
req['SOAPAction'] = 'soapUrl'
req['Content-Type'] = 'text/xml;charset=UTF-8'
req['Content-Length'] = body.length
req.body = body
res = Net::HTTP.start(uri.hostname, uri.port, options) do |http|
    http.request(req)
end
puts res.response.body

Is there an equivalent in httpclient to the :extra_chain_cert option (which essentially calls OpenSSL::SSL::SSLContext.add_certificate(client_cert, client_key, [intermediate_cert])???

dmlond avatar Aug 09 '23 17:08 dmlond