why ruby get the cert trust chain is different from gnutls-cli - ruby

I found a strange phenomenon when using the command gnutls-cli and ruby code to test the cert pinning of the website. Sometimes the number of certificate trust chains obtained by the two methods is different.
commandline gnutls-cli github-cloud.s3.amazonaws.com will get 4:
(I removed some redundant information)
Certificate[0] info:
subject `CN=*.s3.amazonaws.com'
pin-sha256="hK1awhGE7onU0O+/0pwyTCX1ngEBhLhdNNtD8P11+xY="
Certificate[1] info:
subject `CN=Amazon,OU=Server CA 1B,O=Amazon,C=US'
pin-sha256="JSMzqOOrtyOT1kmau6zKhgT676hGgczD5VMdRMyJZFA="
Certificate[2] info:
subject `CN=Amazon Root CA 1,O=Amazon,C=US'
pin-sha256="++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI="
Certificate[3] info:
subject `CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\
pin-sha256="KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I="
Using ruby (github-cloud.s3.amazonaws.com):
/CN=*.s3.amazonaws.com
hK1awhGE7onU0O+/0pwyTCX1ngEBhLhdNNtD8P11+xY=
/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon
JSMzqOOrtyOT1kmau6zKhgT676hGgczD5VMdRMyJZFA=
/C=US/O=Amazon/CN=Amazon Root CA 1
++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=
commandline gnutls-cli www.netflix.com will get 2:
Certificate[0] info:
subject `CN=www.netflix.com,O=Netflix\, Inc.
pin-sha256:3TGagkVvINvo827M04z0YZlg5kctebcod1Qwb83pA0s=
Certificate[1] info:
subject `CN=DigiCert TLS RSA SHA256 2020 CA1,O=DigiCert Inc
pin-sha256="RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc="
Using ruby (www.netflix.com):
/C=US/ST=California/L=Los Gatos/O=Netflix, Inc./CN=www.netflix.com
3TGagkVvINvo827M04z0YZlg5kctebcod1Qwb83pA0s=
/C=US/O=DigiCert Inc/CN=DigiCert TLS RSA SHA256 2020 CA1
RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc=
/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=
Here is Ruby Code:
#!/usr/bin/env ruby
require 'colorize'
require 'net/http'
require 'openssl'
require 'base64'
domain = "www.netflix.com"
http = Net::HTTP.new(domain, 443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_callback = lambda do | preverify_ok, cert_store |
return false unless preverify_ok
end_cert = cert_store.chain[0]
return true unless end_cert.to_der == cert_store.current_cert.to_der
cert_store.chain.each do |i|
sha256 = OpenSSL::Digest::SHA256.new
digest = sha256.digest(i.public_key.to_der)
spki = Base64.strict_encode64(digest)
puts i.subject.to_s, spki
end
true
end
res = http.get '/'
ruby code reference to Implementing HTTPS certificate/pubkey pinning with Ruby
Thanks!

Let's take www.netflix.com as an example. There are three certificates used in the trust chain:
The certificate for CN=www.netflix.com with the public key PIN 3TGagkVvINvo827M04z0YZlg5kctebcod1Qwb83pA0s=, which is signed by:
The DigiCert intermediary certificate CN=DigiCert TLS RSA SHA256 2020 CA1 with the public key PIN RQeZkB42znUfsDIIFWIRiYEcKl7nHwNFwWCrnMMJbVc=, which is signed by:
The DigiCert Root CA CN=DigiCert Global Root CA with the public key PIN r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=
#1 & #2 are sent to you by the remote host when you establish a connection to it. #3 exists on your device and is used to verify that #1 & #2 are valid certificates that you can trust.
When you use OpenSSL inside Net::HTTP to connect to these hosts it is being extra informative and printing the complete certificate chain of trust so that you know what Root CA signed #2. You weren't sent #3 over the wire along with #1 and #2 but it's telling you about it anyway because OpenSSL knows that it's part of the trust chain.
When you use gnutls-cli www.netflix.com --print-cert </dev/null 2>&1 to connect it is being succinct and printing only #1 and #2 -- the certificates that were sent by the remote host -- and instead telling you:
Status: The certificate is trusted.
... based on it knowing that the Root CA that you have on disk was used to sign the intermediary certificate and that the intermediary certificate was used to sign the Netflix certificate.
There's nothing different about the connections or the responses received; there's only a difference in what the tools print out when they are run.

Related

Error Certificate verify failed (certificate has expired)): in Mac OSX 11.6.1 and ruby 3.0.3

I have a ruby on rails webapp sending requests to a third party SOAP API. When I request like:
endpoint = "https://www.booking-manager.com/cbm_web_service2/services/CBM?wsdl"
client = Savon.client(wsdl: endpoint,
#log_level: :info,
log_level: :debug,
log: true,
pretty_print_xml: true,
open_timeout: 300,
read_timeout: 300)
message = {'in0' => xxx,
'in1' => 'xxxx',
'in2' => 'xxx'}
response = client.call(:get_bases, message: message)
I´m getting next error:
HTTPI::SSLError (SSL_connect returned=1 errno=0 state=error: certificate verify failed (certificate has expired)):
The webapp is running under:
Mac OSX Big Sur 11.6.1
ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-darwin20]
I have this issue for weeks and I don´t know what else to do. According to many posts, I tested
openssl s_client -showcerts -host valid-isrgrootx1.letsencrypt.org -port 443
and got:
CONNECTED(00000005)
depth=1 O = Digital Signature Trust Co., CN = DST Root CA X3
verify error:num=10:certificate has expired
notAfter=Sep 30 14:01:15 2021 GMT
verify return:0
depth=1 O = Digital Signature Trust Co., CN = DST Root CA X3
verify error:num=10:certificate has expired
notAfter=Sep 30 14:01:15 2021 GMT
verify return:0
depth=3 O = Digital Signature Trust Co., CN = DST Root CA X3
verify error:num=10:certificate has expired
notAfter=Sep 30 14:01:15 2021 GMT
verify return:0
---
Certificate chain
0 s:/CN=origin.letsencrypt.org
i:/C=US/O=Let's Encrypt/CN=R3
so, according to this: https://community.letsencrypt.org/t/help-thread-for-dst-root-ca-x3-expiration-september-2021/149190/970
I manually updated the file /etc/ssl/cert.pem to remove the DST Root CA X3 certificate. After that, I think that I moved one step forward. When running:
openssl s_client -showcerts -host valid-isrgrootx1.letsencrypt.org -port 443
Now, I don´t get the error and I think looks good:
CONNECTED(00000005)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = origin.letsencrypt.org
verify return:1
---
Certificate chain
0 s:/CN=origin.letsencrypt.org
i:/C=US/O=Let's Encrypt/CN=R3
However, unfortunately the error in my ruby app still remains the same. According to this, I understand ruby is running an openssl that is not getting the information from this certs. I´m not skilled with this at all and don´t know if this makes sense.
I just read other posts and checking openssl version
I got LibreSSL 2.8.3
which openssl
/usr/bin/openssl
In my /usr/local/opt I see three openssl versions folders:
openssl
openssl#1.1
openssl#3
I updated my .zshrc file and now openssl version notifies
OpenSSL 3.0.1 14 Dec 2021 (Library: OpenSSL 3.0.1 14 Dec 2021)
and ruby seems to be using:
ruby -ropenssl -e "puts OpenSSL::OPENSSL_VERSION"
OpenSSL 1.1.1l 24 Aug 2021
I´m aware that the ruby OpenSSL version is 1.1.1 and the system is running 3.0.1. I don´t know how to update ruby to run OpenSSL 3.0.1, although I´m not sure if this can be the root problem. I´m lost at this point.
UPDATE
I think I´m narrowing the issue down. My guess is that Ruby is using a version of openSSL, in this case 1.1.1, that is pointing to /Users/Rober/.rbenv/versions/3.0.3/openssl/ssl/certs bundler instead of pointing to /etc/ssl/cert.pem
irb
irb(main):001:0> require "openssl"
=> true
irb(main):002:0> puts OpenSSL::OPENSSL_VERSION
OpenSSL 1.1.1l 24 Aug 2021
=> nil
irb(main):003:0> puts "SSL_CERT_FILE: %s" % OpenSSL::X509::DEFAULT_CERT_FILE
irb(main):004:0> puts "SSL_CERT_DIR: %s" % OpenSSL::X509::DEFAULT_CERT_DIR
SSL_CERT_FILE: /Users/Rober/.rbenv/versions/3.0.3/openssl/ssl/cert.pem
SSL_CERT_DIR: /Users/Rober/.rbenv/versions/3.0.3/openssl/ssl/certs
This file /Users/Rober/.rbenv/versions/3.0.3/openssl/ssl/cert.pem , unfortunately when I check the content is in the format:
-----BEGIN CERTIFICATE-----
certificate chain
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
I mean, in this file /etc/ssl/cert.pem I could read some readable headers that helped identify the certificate to remove, but in this case the headers are not present, so it´s not possible.
I think that I probably just need to config ruby to run openssl to point to this file /etc/ssl/cert.pem. According to mamy posts, I just added export SSL_CERT_FILE="/etc/ssl/cert.pem" to my .zshrc file, but still getting
OpenSSL::X509::DEFAULT_CERT_FILE
SSL_CERT_FILE: /Users/Rober/.rbenv/versions/3.0.3/openssl/ssl/cert.pem
SOLUTION
Thanks to #JanGaraj that provided the right solution to this problem in my other production post: SSL_connect returned=1 errno=0 state=error: certificate verify failed in ruby and Ubuntu 14.04
Just to summarize, apart from the points depicted above, I just needed to update my web service request specifying my ca-certificates file, like: Savon.client(ssl_ca_cert_file: "/etc/ssl/certs/ca-certificates.crt ")
The solution to this question was provided in another post by #jangaraj
It looks like you are using Ubuntu 14 and Savon 2 client. Savon 2 client doc: https://www.savonrb.com/version2/globals.html
ssl_ca_cert_file
Sets the SSL ca cert file to use.
Savon.client(ssl_ca_cert_file: "lib/ca_cert.pem")
I would point ssl_ca_cert_file to /etc/ssl/certs/ca-certificates.crt explicitly.

How to properly use ca_file for self-signed certificates with Net::HTTP in ruby?

I am trying to understand how to use the ca_file property of the Net::HTTP class to allow connections to hosts with self-signed certificates.
I have prepared a minimal example of my current approach. Check https://repl.it/repls/ElegantRaggedInstitutes or keep reading here:
I have used this command to get a hold of the certificate of https://self-signed.badssl.com
openssl s_client -showcerts -verify 5 -connect self-signed.badssl.com:443 < /dev/null
I have then stored the certificate in a local file and tried to execute the following snippet
require 'net/http'
require 'openssl'
http_conn = Net::HTTP.new('self-signed.badssl.com', 443)
http_conn.use_ssl = true
http_conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
http_conn.ca_file = '/path/to/badssl.cert'
http_conn.start
I expected this to successfully open a connection, accepting the certificate. Instead, it gives me this error:
SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate)
I am surely doing something wrong, can you please advise?
Somehow the certificates got messed. Not sure if I executed the wrong command, copied from the wrong shell or maybe the certificate of self-signed.badssl.com just changed. The certificate that is included in the original repl is actually not self-signed but is signed by an untrusted intermediate CA that was not included in the chain (i.e. I did not add it to the ca_file).
I have verified that by running the command
openssl x509 -text -in /path/to/badssl.crt
and observing the oputput
...
Serial Number:
cd:bc:5a:4a:ec:97:67:b1
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Intermediate Certificate Authority
Validity
Not Before: Aug 8 21:17:05 2016 GMT
Not After : Aug 8 21:17:05 2018 GMT
Subject: C = US, ST = California, L = San Francisco, O = BadSSL Fallback. Unknown subdomain or no SNI., CN = badssl-fallback-unknown-subdomain-or-no-sni
...
Today I grabbed the certificate again
openssl s_client -showcerts -verify 5 -connect self-signed.badssl.com:443 < /dev/null
After updating the certificate in my (forked) repl, the snippet passes:
https://repl.it/repls/UnselfishAvariciousDeletions

SSLv3 certificate verify failure when TLSv1 was specified?

Can someone help me figure this out. I've seen this asked all over the web, but no one has an adequate answer.
I am on Ubuntu, Ruby 1.8.7, and OpenSSL 1.0.1
Net::HTTP.ssl_context_accessor 'ssl_version'
uri = URI.parse("https://www.paypal.com")
http = Net::HTTP.new(uri.host, uri.port)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.use_ssl = true
http.ssl_version = :TLSv1
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
It returns
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
Why is it complaining about SSLv3, if I am specifying TLSv1?
I know it's using TLSv1 because if I did
http.ssl_version = :SSLv3
It will return
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server hello A: sslv3 alert handshake failure
Which is expected.
I am not sure what to make of this error, my ca-certificates are up to date.
I can't do any of that fancy OpenSSL::SSL::OP_NO_SSLv3 stuff because Ruby 1.8.7 does not support any of those options.
Thanks for your help.
Related and helpful answers, but not the solution
How to set TLS context options in Ruby (like OpenSSL::SSL::SSL_OP_NO_SSLv2)
Faraday Throws Error When Setting SSL Version
So as I said in the comments, this is most likely a bug that affects Ruby 1.8.7 and OpenSSL 1.0.1 - When you try to connect to a server that has SSLv3 disabled using TLSv1, it will return a certificate verify failed error.
After a lot of searching around, here is what I did
http.instance_eval {
#ssl_context = OpenSSL::SSL::SSLContext.new
#ssl_context.set_params({:options=>OpenSSL::SSL::OP_NO_SSLv3})
}
This hack allows you to set the OpenSSL::SSL::OP_NO_SSLv3 option, which fixes the problem.
Thanks to a tip from https://www.ruby-forum.com/topic/200072
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed...
Why is it complaining about SSLv3, if I am specifying TLSv1?
Its an artifact of the error messages in OpenSSL. Don't worry too much about it (other than the take away of "verification failed").
You did not ask this question, but here's how to fix it. You should include ca_file:
Net::HTTP.ssl_context_accessor 'ssl_version'
uri = URI.parse("https://www.paypal.com")
http = Net::HTTP.new(uri.host, uri.port)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.use_ssl = true
http.ssl_version = :TLSv1
http.ca_file = File.join(File.dirname(__FILE__), "ca.pem")
ca.pem should be a PEM encoded version of VeriSign's Class 3 Public Primary Certification Authority or VeriSign Class 3 Public Primary Certification Authority - G5. You can fetch it from the Symantec's website under Use of Root Certificates.
You can discover the needed CA certificate with the following command. You are usually interested in the last Issuer in the chain.
The issuer is the one displayed with the "i:", like i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority. The "s:" is used for Subject, and that's who the certificate was issued to.
$ openssl s_client -connect www.paypal.com:443 -tls1 -servername www.paypal.com -showcerts
CONNECTED(00000003)
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:/jurisdictionC=US/jurisdictionST=Delaware/businessCategory=Private Organization/serialNumber=3014267/C=US/postalCode=95131-2021/ST=California/L=San Jose/street=2211 N 1st St/O=PayPal, Inc./OU=CDN Support/CN=www.paypal.com
i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 EV SSL CA - G2
-----BEGIN CERTIFICATE-----
MIIG0jCCBbqgAwIBAgIQB2T3ui0CFx+cSA3+e2W7bzANBgkqhkiG9w0BAQUFADB3
MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd
BgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVj
IENsYXNzIDMgRVYgU1NMIENBIC0gRzIwHhcNMTUwNDIyMDAwMDAwWhcNMTUxMDMx
MjM1OTU5WjCCAQkxEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIB
AhMIRGVsYXdhcmUxHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRAwDgYD
VQQFEwczMDE0MjY3MQswCQYDVQQGEwJVUzETMBEGA1UEERQKOTUxMzEtMjAyMTET
MBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxQIU2FuIEpvc2UxFjAUBgNVBAkU
DTIyMTEgTiAxc3QgU3QxFTATBgNVBAoUDFBheVBhbCwgSW5jLjEUMBIGA1UECxQL
Q0ROIFN1cHBvcnQxFzAVBgNVBAMUDnd3dy5wYXlwYWwuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPisQKaRu+4RFWHn4T5QUeeoQ0H5U6KXTdXp
EfjvdCDhAJQjDA4qwHw514zBcgrVIV/c22yUsr+RBnBYZ/rdlCiZGPT0kVYal9ts
XJyBFfvS3q2XMAzBg5I171geP7G46VbMBXfVkAoH1zND7O7AcNjb7z44oJgELvym
almQzTrfmN7RPjosfGJrQzCnMATT0Up94yIt1Imv5yCavWrIY1ImcuSjUMxTHaRy
D3jtnp2aBC+jhfoZYJ8a2uM6+j5h0gYpaEsLq5ilFWpvsHoKa1ZQit2/p/yE9+6U
oCAuJHYJRicHMNv3EdZMt7xVi5MKFCX7H+ZOmHHuZidDeL0gUQIDAQABo4ICxDCC
AsAwbgYDVR0RBGcwZYIMYy5wYXlwYWwuY29tgg1jNi5wYXlwYWwuY29tghRkZXZl
bG9wZXIucGF5cGFsLmNvbYISaGlzdG9yeS5wYXlwYWwuY29tggx0LnBheXBhbC5j
b22CDnd3dy5wYXlwYWwuY29tMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWgMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBmBgNVHSAEXzBdMFsGC2CGSAGG
+EUBBxcGMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUG
CCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMB8GA1UdIwQYMBaA
FEv6LeTuMzLi3w0BoYbToDs6uayuMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9z
dC5zeW1jYi5jb20vc3QuY3JsMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYT
aHR0cDovL3N0LnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3N0LnN5bWNi
LmNvbS9zdC5jcnQwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgCkuQmQtBhYFIe7
E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAUzjP5slAAAEAwBHMEUCIFvjUjcbhLRI
0c2PUzTVMSItRsGRsoZqdz433/3MnXilAiEA3paAILaCCR6OSp/H7js1R4IxsdCx
Y/d9UhzFxUFevxoAdQBWFAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAA
AUzjP5waAAAEAwBGMEQCICcbFi1Lzw4R+ptZUnTBHJGRSTUUjptEljDAVNZmI7Xi
AiAM/Gly9qoF18kqaPIRZIibSoh+JnYYFuTpec2vNi12XzANBgkqhkiG9w0BAQUF
AAOCAQEAoUACGhsYuAMEt+420Y+q97KpGBGfRDw+5mJFuER1e3p69KAsFZRBufJb
BjJCbY3yWgqnNTvF+klDvok499g8I51+3fo/wdROX+fyeor+0W8Nv7Q/tNQ3J5gZ
CaoNT4yYOdT2wyswOzHLaQJhNNcTlbxy0lEh3f3S04MnhpB4jVCakRvORlU0FD2R
G4oHGhNJqthJc54f5yvlvhXi5ac9hHd8n+G86dS6QI/QWvkg2EXm0/6huSLP2Bvt
z6CSbS+tefVGVei0hvFvlM/ZVkaWGyJvQXli9MnQd1Fh+CkhGgOJSaGJ2/PM47zz
Gp3OLqh4jMEbNLobkIdLkZ2F9jYMDw==
-----END CERTIFICATE-----
1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 EV SSL CA - G2
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
-----BEGIN CERTIFICATE-----
MIIFKzCCBBOgAwIBAgIQNmWFB3qIZ6tY9KCU+BA3MzANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw
CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs
YXNzIDMgRVYgU1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDa7MVqNhGG2yAho+xMIf0PxVw6vuyibpgOgplrenrrbvIlyS1gIF3WK+bd
mdnxVFx2mwv5J0IkfOrU5jwqziOf+3jJFUT0VixVzla3MPBJif4N2G4BfGHTRJlm
XpZIGIcJyMNJ6TXsu0x6FZFV7WCAlQvXGPOnS+bodAwua0X0F/nLv1RW7jbUTubP
rR9NfY+zVTXdYtJuVIWeRVN67mCWIn+4Gq9voA6mjhO2haHMXIyerR1xuAiU5s68
ONDR6T9xQ/uXI6hs9DHcAjveKVXyoL4eIWyvGlzfajdkAprQUXGlf7ppxSyJZ3A7
EwVy7SEjECsjKhquOglkI3onYhLRAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj
MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw
BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov
L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z
eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi
LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMyMB0GA1UdDgQWBBRL+i3k7jMy4t8N
AaGG06A7OrmsrjAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
hkiG9w0BAQUFAAOCAQEARqb1s+ikVsm4NPD3/vMsLmORYyzp1RR/E5P2TN5NgpnM
a8l4YfsSLUgPcOonb3jo9wM+yrg7usnfsDri3iLQMS1m2m4RJUL1eyTC3ksSd81W
2PuGgLoKU27lAQgb0cmydZ2rBics8lKPWb2uHXT648b8RE1bSmzJuDnZ91qE+aAD
wjhOizKlQNrCdfS8yvlX+YYHdVvudjUwhQdz0lxG7Q965X9sPTfBveaFSWC6jfnv
2qxKMeFk9nlnvz9v4pVS3k+NydQ9/L07uDHw9dn1QQRU4CafmYP5BTYlccTLwSse
g1CoewuMVg6qXabpL6Fn/jcgxT6d/J2sIP17u5y4IA==
-----END CERTIFICATE-----
2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
-----BEGIN CERTIFICATE-----
MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC
BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA
A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
-----END CERTIFICATE-----
I am not sure what to make of this error, my ca-certificates are up to date.
Don't add the CA Zoo (ca-certs.pem). You know the CA that certifies PayPal, so use it. There's no need to bring in an addition 300 or so CAs (299 which are incorrect).
Probably Ruby can't find any trustworthy root certificates, see for example this question, among others. Your ruby version is really old, I'd assume your certificate bundle is, too. You should update. Some weeks ago the support for Ruby 1.9.3 ended, let alone support for Ruby 1.8.7. The current version is Ruby 2.2.2. There won't be any fixes for versions that old anymore.

How do I pass a specific ca cert or path to the Sekken constructor?

When I try to create a new Sekken client, it throws an OpenSSL error that there is a self signed certificate in the chain.
require 'sekken'
url = "https://bridgerinsighteu.lexisnexis.com/webservicesapi/9.0/xgservices.svc?wsdl"
client = Sekken.new(url)
I can duplicate the error from OpenSSL, and I can fix it by passing the location for the SSL cert store.
openssl s_client -showcerts -connect bridgerinsighteu.lexisnexis.com:443
errors with return code 19 (self signed certificate in certificate chain) but
openssl s_client -showcerts -CApath /etc/ssl/certs -connect bridgerinsighteu.lexisnexis.com:443
returns code 0 (ok)
So I'm not sure how or what I need to do to pass that cert path to Sekken to use for the openssl check. Sekken does provide for an HTTPClient gem object to be passed to the constructor, so maybe something there? But I just can't quite get my head around this. Or possibly an environment variable? Does anyone have any ideas about how I can get the Sekken constructor to use a specific cert path or cert?
Machine is Ubuntu 14.04 x64, ruby via rvm is ruby 2.1.1p76, sekken is installed via a Gemfile from github.
openssl s_client -showcerts -CApath /etc/ssl/certs -connect bridgerinsighteu.lexisnexis.com:443
Ignore this. The server is misconfigured, and its sending the CA Root. The server should only send the server's certificate and all intermediates required to build a path to a root. Its up to the client to trust the root.
Here's what your command should look like (avoiding the CA Zoo in /etc/ssl/certs, and trusting only what is needed):
openssl s_client -connect bridgerinsighteu.lexisnexis.com:443 -CAfile <Trustwave Root CA>
You can get <Trustwave Root CA> from Trustwave SSL - Support - Root Download. Get the one named Trustwave Extended Validation CA in PEM format.
Here's what it looks like when using the Trustwave Extended Validation CA (evca.crt). Notice the Verify return code: 0 (ok) at the tail of the output.
$ openssl s_client -connect bridgerinsighteu.lexisnexis.com:443 -CAfile evca.crt
CONNECTED(00000003)
depth=2 C = US, O = SecureTrust Corporation, CN = SecureTrust CA
verify return:1
depth=1 C = US, ST = Illinois, L = Chicago, O = "Trustwave Holdings, Inc.", CN = "Trustwave Organization Validation CA, Level 2", emailAddress = ca#trustwave.com
verify return:1
depth=0 CN = *.lexisnexis.com, O = LexisNexis, L = Miamisburg, ST = Ohio, C = US
verify return:1
---
Certificate chain
0 s:/CN=*.lexisnexis.com/O=LexisNexis/L=Miamisburg/ST=Ohio/C=US
i:/C=US/ST=Illinois/L=Chicago/O=Trustwave Holdings, Inc./CN=Trustwave Organization Validation CA, Level 2/emailAddress=ca#trustwave.com
1 s:/C=US/ST=Illinois/L=Chicago/O=Trustwave Holdings, Inc./CN=Trustwave Organization Validation CA, Level 2/emailAddress=ca#trustwave.com
i:/C=US/O=SecureTrust Corporation/CN=SecureTrust CA
2 s:/C=US/O=SecureTrust Corporation/CN=SecureTrust CA
i:/C=US/O=SecureTrust Corporation/CN=SecureTrust CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFJzCCBA+gAwIBAgITBiMcj33v1H9svkXvkWOYn2JyTTANBgkqhkiG9w0BAQUF
ADCBrjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdD
aGljYWdvMSEwHwYDVQQKExhUcnVzdHdhdmUgSG9sZGluZ3MsIEluYy4xNjA0BgNV
BAMTLVRydXN0d2F2ZSBPcmdhbml6YXRpb24gVmFsaWRhdGlvbiBDQSwgTGV2ZWwg
MjEfMB0GCSqGSIb3DQEJARYQY2FAdHJ1c3R3YXZlLmNvbTAeFw0xMzA1MTUwOTE5
NThaFw0xNjA3MDcxNTE5NThaMGExGTAXBgNVBAMMECoubGV4aXNuZXhpcy5jb20x
EzARBgNVBAoMCkxleGlzTmV4aXMxEzARBgNVBAcMCk1pYW1pc2J1cmcxDTALBgNV
BAgMBE9oaW8xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAvsygjRx3DKUm/gmceKny65HMUmRzm8FP0Te9eXsQ76OLa3co4hWrF5ZS
bXlDzB6dgCTFnQOwRcsLpVyXlazDugdibjfnqdyStcjI75+J2emRYzHVJ7P9p+Bw
pL1k01POV/pes87abX1ffodK+OwnWDkfABqLaaJlsluv/NJd5cdGTn8C1+7mw3MR
KxUTGuGdsTgV/H5aEQFAP6BIklpywhk+QJb1BN28bR2UMTi0QB6qBNP+oe5aWG6Q
rq/ghb31FF0jmL9pCGVJfY5eewIXjBiEwFSdkxv8rxPmkmjDV9E5/OGKXuALtJIE
SFfM9WwTKHV3rs79QV38yD6Cbf3yVQIDAQABo4IBiDCCAYQwDAYDVR0TAQH/BAIw
ADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0G
A1UdDgQWBBQUma9eMJEc5IUlSe4n6X7eViCS9zAfBgNVHSMEGDAWgBRd2ZaaQMcn
yyybouzPGavIr8yGSDBIBgNVHSAEQTA/MD0GDysGAQQBge0YAwMDAwQEAzAqMCgG
CCsGAQUFBwIBFhxodHRwczovL3NzbC50cnVzdHdhdmUuY29tL0NBME8GA1UdEQRI
MEaCECoubGV4aXNuZXhpcy5jb22CDmxleGlzbmV4aXMuY29tghEqLmxleGlzLW5l
eGlzLmNvbYIPbGV4aXMtbmV4aXMuY29tMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6
Ly9jcmwudHJ1c3R3YXZlLmNvbS9PVkNBX0wyLmNybDA2BggrBgEFBQcBAQQqMCgw
JgYIKwYBBQUHMAGGGmh0dHA6Ly9vY3NwLnRydXN0d2F2ZS5jb20vMA0GCSqGSIb3
DQEBBQUAA4IBAQAXvdGvtggb6dfa46IFX81rGr69ank7rON6VQaqUrUeExqmSyhw
r+n8wh0YFo69GKVx1LFa1+eWIz48ROtOhveSr/Gib4ujBLtq5urITOcmH4IYj3sw
2VFuaCZ0+TNgqmt6HPTPfBwWjcCRLbtDYPeFFo52HMu+ObeNeVR1Ll58Iijl4sOo
CwaDNFYiveLwcPXgGQhvYn6NFXW0D2cRpeTJzjXOjcLebPY9h//Fl6loZh/APwGT
gKz43mIChdcTQz/caeDjj0VkiSpJ4XDXYRDabSkpzvwJ5AXDC4f4jZy8jxnx9sSP
NnGvKxaJwr2ArewSfYX7W/JtVUAF+wIEVPux
-----END CERTIFICATE-----
subject=/CN=*.lexisnexis.com/O=LexisNexis/L=Miamisburg/ST=Ohio/C=US
issuer=/C=US/ST=Illinois/L=Chicago/O=Trustwave Holdings, Inc./CN=Trustwave Organization Validation CA, Level 2/emailAddress=ca#trustwave.com
---
No client certificate CA names sent
---
SSL handshake has read 3569 bytes and written 831 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
Session-ID: 684051C7B37B4A255AE51BFC67CFC4BF...
Session-ID-ctx:
Master-Key: 53C559C9F85A6CB1788BFC20E1A1997C...
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1399081838
Timeout : 300 (sec)
Verify return code: 0 (ok)
So I'm not sure how or what I need to do to pass that cert path to Sekken to use for the openssl check.
All you need to do is specify Trustwave Extended Validation CA (evca.crt) as the root to use in Sekken when building the validation path. I'm not a Sekken guy, but I know how to do it in other languages and libraries like .Net, Java, OpenSSL, PERL, Python, etc.
Since you specified the Ruby tag, here's a test script I was using for some PKI testing with Ruby:
#!/usr/bin/ruby
require 'net/http'
uri = URI('https://bridgerinsighteu.lexisnexis.com:443')
http = Net::HTTP.new(uri.host, uri.port)
# Enable SSL/TLS ?
if uri.scheme == "https"
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = File.join(File.dirname(__FILE__), "evca.crt")
end
req = Net::HTTP::Get.new('/')
http.request(req)
This is just bike shedding... You have the Trustwave Extended Validation CA in /etc/ssl/certs. If the certificate was missing, then s_client would have failed when using CApath.
Trustwave has proven itself to be very untrustworthy. In the past, it facilitated interception of all SSL/TLS traffic by issuing certificates for domains not under the control of the person who wanted the certificates.
"Trust" is tricky to define. One of the better definitions I have seen is "X expects Y to do Z". That is, X expects or trusts Y to do Z because (1) Y says it does Z and (2) X accepts or approves of Z.
If you plug in PKI: "Users expect CAs to follow their CP and CPS". CP is "Certification Practice" and its policy; and CPS Is Certification Practice Statement and its procedure. So the CP and CPS specifies how the CA operates by defining policies (CP), and procedures to implement or enforce the policies (CPS).
If Trustwave followed their own published policies and procedures, then Trustwave would not have issued the certificates to intercept the SSL/TLS traffic. Trustwave did not follow their own policies and procedures, so they have proven themselves to be untrustworthy. Quod erat demonstrandum.
So ok, looks like you can pass an instance of an HTTPClient to the Sekken constructor. But there's a bug in the constructor that keeps it from using the passed client. I hacked my way around it, but hopefully the owner will fix it better? https://github.com/savonrb/sekken/issues/10
Once that is fixed, this is how I solved the problem. I created an instance of the HTTPClient then added the Trustwave root CA cert to the instance cert store and passed that to the Sekken constructor.
require 'sekken'
require 'httpclient'
url = "https://bridgerinsighteu.lexisnexis.com/webservicesapi/9.0/xgservices.svc?wsdl"
# this is the secure trust CA
cert = "/etc/ssl/certs/stca.pem"
http = HTTPClient.new
http.ssl_config.cert_store.add_file cert
client = Sekken.new(url, http)

Using a self-signed certificate

I am just trying to get my head around SSL.
I have set up a Jetty server on my localhost, and generated my own certificate using Keytool.
Now when I go to https://localhost:8443/ I get the can't trust this certificate error.
I use
keytool -export -alias pongus -keystore keystore -file certfile.cer
To create the certificate which I think is what the client needs to authenticate with the server. (This is where I could be very wrong!)
I have the following ruby code :
require 'net/https'
require 'openssl'
require 'open-uri'
puts 'yay' if File.exists?('certfile.cer')
uri = URI.parse("https://localhost:8443/")
http_session = Net::HTTP.new(uri.host, uri.port)
http_session.use_ssl = true
http_session.verify_mode = OpenSSL::SSL::VERIFY_PEER
http_session.ca_file = 'certfile.cer'
res = http_session.start do |http|
# do some requests here
http.get('/')
end
This does print 'yay', so the certfile.cer file does exist.
But I get the errors
/Applications/NetBeans/NetBeans 6.8.app/Contents/Resources/NetBeans/ruby2/jruby-1.4.0/lib/ruby/1.8/net/http.rb:586 warning: can't set verify locations
/Applications/NetBeans/NetBeans 6.8.app/Contents/Resources/NetBeans/ruby2/jruby-1.4.0/lib/ruby/1.8/net/http.rb:586:in `connect': certificate verify failed (OpenSSL::SSL::SSLError)
Any ideas what I am doing wrong?
EDIT
I want to get it so I guarantee that I am connecting to the right server, and the server can guarantee that it is me connecting to it, without any tampering in between. I am developing both the server and the client.
Your client needs access to its
private key.
You dont need the private key for server certificate verification. All you need is the certificate itself which contains the public key. Only the server has the private key. Well described here http://www.helpbytes.co.uk/https.php and here http://www.verisign.com/ssl/ssl-information-center/how-ssl-security-works/
My recommendation is simple. Check your certificate is correct.
openssl x509 -text -in mycert.crt
Also if you have access to the server you can explicitely validate if the certificate and key (used in httpd configuration) are correct (matches): http://kb.wisc.edu/middleware/page.php?id=4064 Please note this is explicit check ran on server. Never give out the private key. This check can be done only by the administrator to verify if the httpd was not misconfigured.
(openssl x509 -noout -modulus -in server.pem | openssl md5 ;\
openssl rsa -noout -modulus -in server.key | openssl md5) | uniq
You can also debug the SSL certificate communication using standard openssl command. Issue this command then wait few seconds and then type QUIT and hit enter. You will see the certificate the server sends out.
openssl s_client -connect your.server.com:443
Also try to import the certificate to your browser and access the URL resource. Browser is able to validate it by clicking on https (Firefox and Chrome). Then you will see the certificate itself and validity information.
All the above was all about the server certificate. This is only one part of the problem. "I am connecting to the right server, and the server can guarantee that it is me connecting to it" Actully in your code above you only check for the server cert. Now. If you want a client certificate (the second part of your statement) than you need this in Ruby:
File.open( "client_certificate.pem", 'rb' ) { |f| cert = f.read }
File.open( "client_key.pem", 'rb' ) { |f| key = f.read }
http_session.cert = OpenSSL::X509::Certificate.new(cert)
http_session.key = OpenSSL::PKey::RSA.new(key, nil)
This is how client cert should be used in Ruby. If your private key is encrypted with a password just pass it instead nil in the second argument of RSA constructor.
I highly recommend to get server certificate working (your code) and then start with client certificate. Please note you keep your current code (ca_cert, validation constant) and add the above four lines to it.
Hope this helps.
Your client needs access to its private key. The private key is not in the certificate, the certificate only contains the public key. Sorry, I don't know ruby, but a common technique is to bundle the private key and certificate in a single PKCS#12, aka p12, file and supply this to the crypto library.
Change
http_session.verify_mode = OpenSSL::SSL::VERIFY_PEER
to
http_session.verify_mode = OpenSSL::SSL::VERIFY_NONE
Once you do that, the SSL will work properly. I have used this multiple times in my development environments, always works flawlessly.

Resources