7

I am a newbie on SSL concept, I am trying to connect to API which has x509 mutual auth using HTTParty.

I got client cert, client key and server cert (all are pem files).

I got it working with client cert and key and with verify: false.

Now next step is how to verify server cert also?

HTTParty documentation link https://github.com/jnunemaker/httparty/tree/master/docs#working-with-ssl

class ServiceClient
  include HTTParty
  DEFAULT_HEADERS = {
    'Content-Type' => 'application/json'
  }.freeze
  base_uri 'https://example.com'
  pem File.read("#{File.expand_path('.')}/path/to/certs/cert.pem")

  def self.iframe_url(**payload)
    post(
     '/test/create',
     body: payload.to_json,
     headers: DEFAULT_HEADERS,
     verify: false
    )
  end
end

Call to service client

payload = {user_id: "100", account_id: "1234"} 
ServiceClient.iframe_url(payload)

Edit:

HTTParty is not a hard requirement so solution with any http client would work for me.

If I remove verify: false I get below error.

OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed
pramodtech
  • 6,300
  • 18
  • 72
  • 111
  • This code is missing a *lot* of context and the relationship of the constants, variables, and method calls to that `iframe_url` method are far from clear. – tadman May 24 '18 at 17:02
  • Added more details. I was just looking for pointers on how to specify server cert and validate response against it. HTTParty Documentation doesn't have any details on this. I guess answer might be pretty simple but I am unable to find any reference on how to do it. Thanks. – pramodtech May 25 '18 at 05:05
  • You could clean that up a bit as `File.read(File.expand_path('./path/to/certs/cert.pem`))` instead. No need to interpolate strings. – tadman May 25 '18 at 19:20
  • Does HTTParty let you get the server certificate for verification purposes? I know it's a pretty good library for "quick and easy" integrations, but I'm not sure it's all that sophisticated. – tadman May 25 '18 at 19:21
  • I think it does but not confident. Code shows that cert_store param can be sent thru options. – pramodtech May 28 '18 at 06:51
  • `verify: false` does not verify the server as you can see in the source code https://github.com/jnunemaker/httparty/blob/master/lib/httparty/connection_adapter.rb:160. By default `verify` and `verify_peer` are set to `true` – Kartikey Tanna May 28 '18 at 07:04
  • @KartikeyTanna yes, I understand that. I have set verify false just to check my request is going thru. When I remove it, request fails and that is where I need to implement server cert validation. – pramodtech May 28 '18 at 08:39
  • Can you dump the error message? – Kartikey Tanna May 28 '18 at 08:51
  • updated error message in question itself – pramodtech May 28 '18 at 09:00

4 Answers4

4

By default, the TLS protocol only proves the identity of the server to the client, leaving the authentication of the client to the server to the application layer, e.g., HTTP Basic authentication.

Mutual TLS authentication refers to the client and server authenticating each other at the same time. It can be used as an alternative to HTTP Basic authentication. It's also often called certificate-based authentication.

Looks like you're getting this error (but works with verify: false) because the server certificate isn't trusted. Is it self-signed? Or perhaps the certificate authority (CA) who issued the certificate isn't trusted in your system.

HTTParty is built on top of Net::HTTP, which uses OpenSSL, which gets the trusted CA certificates from the system, e.g. /etc/ssl/cert.pem. Although you could override the SSL_CERT_FILE environment variable, I don't think it's a good idea because it might impact other parts of your application. A better solution would be to pass the CA certificates to HTTParty only.

I've never used HTTParty, so bear with me. But taking a quick look at the code and assuming you're using the default ConnectionAdapter, looks like you need to define both pem and ssl_ca_file options, which will define the client and CA certificate files, respectively. Also note that by doing it so, it will turn certificate verification on.

class ServiceClient
  include HTTParty
  pem "path/to/client_cert_and_key.pem"
  ssl_ca_file "path/to/ca_certificates.pem"
end

I don't think it's related to your issue, but if your client key has a passphrase, make sure to pass it on pem(pem_contents, password).

HTH. Best.

wicz
  • 2,315
  • 12
  • 13
1

Try as you I suppose you are using ssl certificate that you want to verify. Also it's quite easier in RestClient, I am adding snippet at the end.

class ServiceClient
  include HTTParty
  DEFAULT_HEADERS = {
    'Content-Type' => 'application/json'
  }.freeze
  base_uri 'https://example.com'
  ssl_ca_file "#{File.expand_path('.')}/path/to/certs/cert.pem"


  def self.iframe_url(**payload)
    post(
     '/test/create',
     body: payload.to_json,
     headers: DEFAULT_HEADERS,
    )
  end
end

Rest Client solution:

client = RestClient::Resource.new('https://example.com/',
                              :ssl_client_cert => p12.certificate,
                              :ssl_client_key => p12.key,
                              :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
Przemek Mroczek
  • 357
  • 1
  • 11
1

This should solve the problem:

RestClient::Resource.new(
  'https://example.com',
  :ssl_client_cert  =>  OpenSSL::X509::Certificate.new(File.read("cert.pem")),
  :ssl_client_key   =>  OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
  :ssl_ca_file      =>  "ca_certificate.pem",
  :verify_ssl       =>  OpenSSL::SSL::VERIFY_PEER
).get

Ref: https://github.com/rest-client/rest-client

Kartikey Tanna
  • 1,423
  • 10
  • 24
-1

Use " OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE"

In your code , maybe look like to:

    class ServiceClient
      include HTTParty
      OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
      {...your code...}
    end