Ruby OpenSSL::SSL::SSLError on CentOS 6.2 - ruby

I'm trying to run the following code (taken from codeacademy) in CentOS 6.2:
require 'rubygems'
require 'oauth'
# Change the following values to those provided on dev.twitter.com
# The consumer key identifies the application making the request.
# The access token identifies the user making the request.
consumer_key = OAuth::Consumer.new(
"MY_KEY",
"MY_SECRET")
access_token = OAuth::Token.new(
"STRING1",
"STRING2")
# All requests will be sent to this server.
baseurl = "https://api.twitter.com"
# The verify credentials endpoint returns a 200 status if
# the request is signed correctly.
address = URI("#{baseurl}/1.1/account/verify_credentials.json")
# Set up Net::HTTP to use SSL, which is required by Twitter.
http = Net::HTTP.new address.host, address.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
# Build the request and authorize it with OAuth.
request = Net::HTTP::Get.new address.request_uri
request.oauth! http, consumer_key, access_token
# Issue the request and return the response.
http.start
response = http.request request
puts "The response status was #{response.code}"
and get the following error message:
/usr/lib/ruby/1.8/net/http.rb:586:in `connect': SSL_connect returned=1
errno=0 state=SSLv3 read server certificate B: certificate verify
failed (OpenSSL::SSL::SSLError)
The keys have been omitted (tehy are, after all, secret), but i'm using correct ones.
The neccessary gems are installed.
What might the problem be?

http = Net::HTTP.new address.host, address.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
...
You also need:
http.ca_file = File.join(File.dirname(__FILE__), "ca-cert.pem")
Since its Tweeter:
$ openssl s_client -connect api.twitter.com:443
CONNECTED(00000003)
depth=1 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = Terms of use at https://www.verisign.com/rpa (c)10, CN = VeriSign Class 3 Secure Server CA - G3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=San Francisco/O=Twitter, Inc./OU=Twitter Security/CN=api.twitter.com
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
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
...
You need the top issuer, (i: at level 1), which is VeriSign Class 3 Public Primary Certification Authority - G5. You can get that from Public Root CA - VeriSign. The filename is PCA-3G5.pem.
After you download the root, you can then run s_client again and the server certificate will verify:
$ openssl s_client -connect api.twitter.com:443 -CAfile PCA-3G5.pem

Related

OpenSSL::SSL::SSLServer doesn't add peer_cert

I have server working that looks a little bit like this
require "socket"
require "openssl"
require "thread"
listeningPort = Integer(ARGV[0])
server = TCPServer.new(listeningPort)
sslContext = OpenSSL::SSL::SSLContext.new
sslContext.cert = OpenSSL::X509::Certificate.new(File.open("cert.pem"))
sslContext.key = OpenSSL::PKey::RSA.new(File.open("priv.pem"))
sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
puts "Listening on port #{listeningPort}"
loop do
connection = sslServer.accept
Thread.new {...}
end
When I connect with TLS1.3 and I provide a client cert, I can see that it's working when I verify the cert in the ssl context, but peer_cert is never set on the connection, only the context receives a session.
Do I need to upgrade manually to TLS to access the cert from the client?
The reason why I want it is, I can restrict content or authenticate by looking at the cert on the Gemini protocol
After a lot of reading in the OpenSSL docs I found a solution:
I set the sslContext.verify_mode = OpenSSL::SSL::VERIFY_PEER and add a verification callback
sslContext.verify_callback = proc do |_a, _b|
true
end
Which will behave like VERIFY_NONE, but it does request the peer certificate (which it won't when mode is set to VERIFY_NONE as the documentation states: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html

Traefik not getting SSL certificates for new domains

I've got Traefik/Docker Swarm/Let's Encrypt/Consul set up, and it's been working fine. It managed to successfully get certificates for the domains admin.domain.tld, registry.domain.tld and staging.domain.tld, but now that I've tried adding containers that are serving domain.tld and matomo.domain.tld those aren't getting any certificates (browser warns of self signed certificate because it's the default Traefik certificate).
My Traefik configuration (that's being uploaded to Consul):
debug = false
logLevel = "DEBUG"
insecureSkipVerify = true
defaultEntryPoints = ["https", "http"]
[entryPoints]
[entryPoints.ping]
address = ":8082"
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[traefikLog]
filePath = '/var/log/traefik/traefik.log'
format = 'json'
[accessLog]
filePath = '/var/log/traefik/access.log'
format = 'json'
[accessLog.fields]
defaultMode = 'keep'
[accessLog.fields.headers]
defaultMode = 'keep'
[accessLog.fields.headers.names]
"Authorization" = "drop"
[retry]
[api]
entryPoint = "traefik"
dashboard = true
debug = false
[ping]
entryPoint = "ping"
[metrics]
[metrics.influxdb]
address = "http://influxdb:8086"
protocol = "http"
pushinterval = "10s"
database = "metrics"
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "domain.tld"
watch = true
exposedByDefault = false
network = "net_web"
swarmMode = true
[acme]
email = "my#mail.tld"
storage = "traefik/acme/account"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
Possibly related, in traefik.log I repeatedly (as in almost once per second) get the following (but only for the registry subdomain). Sounds like an issue to persist the data to consul, but there are no errors indicating such an issue.
{"level":"debug","msg":"Looking for an existing ACME challenge for registry.domain.tld...","time":"2019-07-07T11:37:23Z"}
{"level":"debug","msg":"Looking for provided certificate to validate registry.domain.tld...","time":"2019-07-07T11:37:23Z"}
{"level":"debug","msg":"No provided certificate found for domains registry.domain.tld, get ACME certificate.","time":"2019-07-07T11:37:23Z"}
{"level":"debug","msg":"ACME got domain cert registry.domain.tld","time":"2019-07-07T11:37:23Z"}
Update: I managed to find this line in the log:
{"level":"error","msg":"Error getting ACME certificates [matomo.domain.tld] : cannot obtain certificates: acme: Error -\u003e One or more domains had a problem:\n[matomo.domain.tld] acme: error: 400 :: urn:ietf:paramsacme:error:connection :: Fetching http://matomo.domain.tld/.well-known/acme-challenge/WJZOZ9UC1aJl9ishmL2ACKFbKoGOe_xQoSbD34v8mSk: Timeout after connect (your server may be slow or overloaded), url: \n","time":"2019-07-09T16:27:43Z"}
So it seems the issue is the challenge failing because of a timeout. Why the timeout though?
Update 2: More log entries:
{"level":"debug","msg":"Looking for an existing ACME challenge for staging.domain.tld...","time":"2019-07-10T19:38:34Z"}
{"level":"debug","msg":"Looking for provided certificate to validate staging.domain.tld...","time":"2019-07-10T19:38:34Z"}
{"level":"debug","msg":"No provided certificate found for domains staging.domain.tld, get ACME certificate.","time":"2019-07-10T19:38:34Z"}
{"level":"debug","msg":"No certificate found or generated for staging.domain.tld","time":"2019-07-10T19:38:34Z"}
{"level":"debug","msg":"http: TLS handshake error from 10.255.0.2:51981: remote error: tls: unknown certificate","time":"2019-07-10T19:38:34Z"}
But then, after a couple minutes to an hour, it works (for two domains so far).
not sure if its a feature or a bug, but removing the following http to https redirect solved it for me:
[entryPoints.http.redirect]
entryPoint = "https"

ruby HTTPClinet - SSL & set_auth

I am trying understand what is going wrong here. Due to reasons I don't understand, my company's software requires two ports to connect using NTLM. Initially it connects on 443, then redirects to 4244 for authentication then is returned to 443 afterwards. The same certificate is bound on the server to both ports.
My first HTTPS GET works fine, however the second complains about the certificate. I believe this is due to the redirect back. If I set the domain for set_auth to 4244, it fails on the redirect back, if I set it to 443 (i.e. leave the port off) then it fails to authenticate.
client = HTTPClient.new()
client.ssl_config.set_trust_ca('./SenseStandalone/root.pem')
client.ssl_config.set_client_cert_file(cert, key)
...
#base_uri = "https://SenseStandalone"
https_url = #base_uri+path #+'?xrfkey='+#xrf
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER
client.set_auth(#base_uri, 'login', 'password')
t = client.get(https_url, query, extheader, :follow_redirect => true)
pp t
redirect = t.http_header.request_uri.to_s
puts redirect
client.set_auth('https://SenseStandalone:4244', 'login', 'password')
r = client.get(redirect, nil, extheader, :follow_redirect => true)
pp r.status_code, r.body
What I really neeed it do be able to set the domain to the whole server. Setting it to 'nil' causes it to fail altogether.
Of course I can fix this with:
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
But I'd rather not do that if I have a choice.
As an aside, any idea why I need to do the first redirect manually? The second one redirects on its own.
The actual error:
So to be specific the error is:
C:/dev/Ruby22-x64/lib/ruby/2.2.0/openssl/ssl.rb:240:in `post_connection_check': hostname "sensestandalone" does not match the server certificate (OpenSSL::SSL::SSLError)
The certificate is the same for both ports:
C:\Windows\system32>netsh http show sslcert
SSL Certificate bindings:
IP:port : 0.0.0.0:4244
Certificate Hash : 7b2969cf62af93fc0ebca19b597a370a32be89b7
Application ID : {cebd7eb4-e9bb-4377-85be-d961248daa80}
Certificate Store Name : (null)
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
IP:port : 0.0.0.0:443
Certificate Hash : 7b2969cf62af93fc0ebca19b597a370a32be89b7
Application ID : {3af37b68-4503-431e-b020-9e85fe225814}
Certificate Store Name : (null)
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
SSL Certificate (443):
C:\openssl\bin>openssl s_client -connect sensestandalone:443 -tls1 -servername sensestandalone | openssl x509 -text -noout
Loading 'screen' into random state - done
depth=0 CN = SenseStandalone
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = SenseStandalone
verify error:num=21:unable to verify the first certificate
verify return:1
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
d9:cd:81:4e:f8:8c:28:ed:f5:1d:0c:67:ae:5c:45
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=SenseStandalone-CA
Validity
Not Before: Jan 2 04:19:53 2017 GMT
Not After : Jan 9 04:19:53 2027 GMT
Subject: CN=SenseStandalone
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:8b:23:c4:f9:6c:d2:19:90:db:9c:51:2a:14:5e:
88:c5:24:cf:e9:3a:67:82:7a:8f:db:9b:ad:39:99:
9c:4c:1c:c4:11:ae:6a:96:20:24:e1:e2:19:c3:9e:
53:ba:52:45:4d:93:79:80:8b:c3:d9:3f:e7:7e:88:
65:16:b7:e5:84:8e:7c:1d:1e:e4:b4:df:29:8b:b4:
1e:6b:c2:c0:b8:83:78:16:de:4d:65:80:b0:b5:c6:
53:86:05:63:b3:2b:52:a0:20:8a:35:b0:fc:5d:25:
e8:77:32:b3:8c:28:b3:53:39:d1:4e:7b:df:0b:ee:
4c:51:bd:bf:01:f2:99:4b:59:31:c2:8e:04:a3:15:
0e:2c:34:da:e7:66:11:1d:77:85:80:28:d2:6b:05:
97:28:c0:97:a3:e4:8e:28:a7:d0:24:d5:69:da:e2:
2c:b1:5f:ee:5b:28:4e:44:04:c2:45:32:26:d7:8f:
19:56:95:e1:2a:ac:72:e1:57:ef:85:7e:53:dc:09:
44:22:4e:02:d6:20:69:02:c0:6a:49:23:76:5e:6d:
4f:e1:c6:9c:1b:a2:75:9d:b2:f7:65:89:cf:89:10:
37:c3:57:b0:a8:77:aa:c4:15:a8:7c:00:e6:75:c9:
06:7d:76:9e:cb:51:e9:ae:4e:e9:f8:57:ee:e2:e4:
de:c7
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
17:90:47:3E:66:51:DA:14:1A:A5:85:77:D2:36:66:61:E4:3A:08:FF
X509v3 Authority Key Identifier:
keyid:C9:D1:A4:38:7F:FA:6F:9A:05:DD:CC:8A:D9:7B:4C:12:98:38:86:AE
1.3.6.1.5.5.7.13.3:
..Service
1.3.6.1.5.5.7.13.1:
vgu,...e...q......Y.*r...GiG!./aL..+..O......>..w.9...M`f..,....T..w`...i^.b..&...!C.... ]..{h9.W...W3...F(...(=.)..Z....x..F..2
1.3.6.1.5.5.7.13.2:
...SJ....S.8/....................(P*R.............5]..........?..b..l..=\.O......L.W.x.,.......|.. .,....t2aK..z)..s=..3,......x
Signature Algorithm: sha256WithRSAEncryption
27:2d:1d:c1:43:00:77:ec:76:a9:f1:f8:c3:73:26:58:e8:7d:
fc:61:3b:cf:91:dd:cf:b7:6c:66:ac:5d:c7:bb:08:10:85:2a:
4a:be:b8:d2:df:c3:02:ff:02:f9:9d:89:e7:6c:6c:82:d9:99:
9a:47:2a:65:01:c8:d2:ad:f4:c8:e1:a4:12:72:3a:c6:11:d6:
90:b2:4e:2a:42:a5:d6:53:69:1b:57:ee:2c:02:b6:a1:8a:a5:
bb:6d:23:04:46:69:74:fa:c7:f3:70:d4:a7:d0:8b:ca:cd:ff:
65:5e:e3:44:20:be:28:58:08:5b:5f:9d:f0:07:1b:b9:ee:ca:
7e:33:99:49:2e:57:b8:5e:dd:82:e4:7e:85:33:e7:3d:27:7c:
d5:a9:b0:24:22:6b:17:3e:34:91:c6:a0:22:b7:3c:85:6c:64:
ed:d2:72:a7:ac:a7:5d:04:b5:fa:4a:48:49:bc:31:0f:48:38:
20:84:4e:a3:fe:f2:2c:bc:89:ee:0d:2f:4d:3f:87:29:9f:f7:
c6:3d:97:a3:12:28:a0:92:5a:64:7f:45:de:18:b3:c3:91:5f:
eb:85:9a:99:e9:e2:f5:cc:a6:47:65:5f:be:d6:dd:50:88:38:
5c:17:88:16:25:07:20:e2:cd:13:ca:69:14:97:73:ad:d9:cd:
52:3b:0b:e4
SSL Certificate (4244):
C:\openssl\bin>openssl s_client -connect sensestandalone:4244 -tls1 -servername sensestandalone | openssl x509 -text -noout
depth=0 CN = SenseStandalone
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = SenseStandalone
verify error:num=21:unable to verify the first certificate
verify return:1
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
d9:cd:81:4e:f8:8c:28:ed:f5:1d:0c:67:ae:5c:45
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=SenseStandalone-CA
Validity
Not Before: Jan 2 04:19:53 2017 GMT
Not After : Jan 9 04:19:53 2027 GMT
Subject: CN=SenseStandalone
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:8b:23:c4:f9:6c:d2:19:90:db:9c:51:2a:14:5e:
88:c5:24:cf:e9:3a:67:82:7a:8f:db:9b:ad:39:99:
9c:4c:1c:c4:11:ae:6a:96:20:24:e1:e2:19:c3:9e:
53:ba:52:45:4d:93:79:80:8b:c3:d9:3f:e7:7e:88:
65:16:b7:e5:84:8e:7c:1d:1e:e4:b4:df:29:8b:b4:
1e:6b:c2:c0:b8:83:78:16:de:4d:65:80:b0:b5:c6:
53:86:05:63:b3:2b:52:a0:20:8a:35:b0:fc:5d:25:
e8:77:32:b3:8c:28:b3:53:39:d1:4e:7b:df:0b:ee:
4c:51:bd:bf:01:f2:99:4b:59:31:c2:8e:04:a3:15:
0e:2c:34:da:e7:66:11:1d:77:85:80:28:d2:6b:05:
97:28:c0:97:a3:e4:8e:28:a7:d0:24:d5:69:da:e2:
2c:b1:5f:ee:5b:28:4e:44:04:c2:45:32:26:d7:8f:
19:56:95:e1:2a:ac:72:e1:57:ef:85:7e:53:dc:09:
44:22:4e:02:d6:20:69:02:c0:6a:49:23:76:5e:6d:
4f:e1:c6:9c:1b:a2:75:9d:b2:f7:65:89:cf:89:10:
37:c3:57:b0:a8:77:aa:c4:15:a8:7c:00:e6:75:c9:
06:7d:76:9e:cb:51:e9:ae:4e:e9:f8:57:ee:e2:e4:
de:c7
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
17:90:47:3E:66:51:DA:14:1A:A5:85:77:D2:36:66:61:E4:3A:08:FF
X509v3 Authority Key Identifier:
keyid:C9:D1:A4:38:7F:FA:6F:9A:05:DD:CC:8A:D9:7B:4C:12:98:38:86:AE
1.3.6.1.5.5.7.13.3:
..Service
1.3.6.1.5.5.7.13.1:
vgu,...e...q......Y.*r...GiG!./aL..+..O......>..w.9...M`f..,....T..w`...i^.b..&...!C.... ]..{h9.W...W3...F(...(=.)..Z....x..F..2
1.3.6.1.5.5.7.13.2:
...SJ....S.8/....................(P*R.............5]..........?..b..l..=\.O......L.W.x.,.......|.. .,....t2aK..z)..s=..3,......x
Signature Algorithm: sha256WithRSAEncryption
27:2d:1d:c1:43:00:77:ec:76:a9:f1:f8:c3:73:26:58:e8:7d:
fc:61:3b:cf:91:dd:cf:b7:6c:66:ac:5d:c7:bb:08:10:85:2a:
4a:be:b8:d2:df:c3:02:ff:02:f9:9d:89:e7:6c:6c:82:d9:99:
9a:47:2a:65:01:c8:d2:ad:f4:c8:e1:a4:12:72:3a:c6:11:d6:
90:b2:4e:2a:42:a5:d6:53:69:1b:57:ee:2c:02:b6:a1:8a:a5:
bb:6d:23:04:46:69:74:fa:c7:f3:70:d4:a7:d0:8b:ca:cd:ff:
65:5e:e3:44:20:be:28:58:08:5b:5f:9d:f0:07:1b:b9:ee:ca:
7e:33:99:49:2e:57:b8:5e:dd:82:e4:7e:85:33:e7:3d:27:7c:
d5:a9:b0:24:22:6b:17:3e:34:91:c6:a0:22:b7:3c:85:6c:64:
ed:d2:72:a7:ac:a7:5d:04:b5:fa:4a:48:49:bc:31:0f:48:38:
20:84:4e:a3:fe:f2:2c:bc:89:ee:0d:2f:4d:3f:87:29:9f:f7:
c6:3d:97:a3:12:28:a0:92:5a:64:7f:45:de:18:b3:c3:91:5f:
eb:85:9a:99:e9:e2:f5:cc:a6:47:65:5f:be:d6:dd:50:88:38:
5c:17:88:16:25:07:20:e2:cd:13:ca:69:14:97:73:ad:d9:cd:
52:3b:0b:e4
The command seemed to hang at the end both times..
So I managed to get a difference certificate. I set both the FQDN and hostname as SAN's in lowercase.
One of these has fixed my issue. I am not sure which. I would appreciate a reference to the requirement for Subject Alternate names as the developers I spoke to as my company see this as just a suggestion.

Show content of OpenSSL::X509::Store object

A bit desperate on this one...
I am implementing an OCSP checking service, mainly based on those two examples:
http://docs.ruby-lang.org/en/2.2.0/OpenSSL/OCSP.html
How to programmatically check if a certificate has been revoked?
I already verified the validity of my request via the openssl client:
openssl ocsp -issuer ISSUER_OF_TESTCERT.pem.crt -cert TESTCERT.pem.crt -url http://url.of.ocspservice/ocsp -VAfile SIGNING_CERT_OF_OCSP_SERVICE_RESPoNSE.pem.crt
this gives me:
Response verify OK
TESTCERT.pem.crt good
This Update: <timestamp>
when using ruby's openssl api, i also get a positive response, 200 OK
However, once i want to verify the the response, i get
warning: error:27069076:OCSP routines:OCSP_basic_verify:signer certificate not found
so here is how i try to verify the response:
# instantiate a ocsp response object from the http response body (side note: instantiating a BasicResponse object directly let's the irb segfault in the strangest way)
response = OpenSSL::OCSP::Response.new http_response.body
# transform into BasicResponse
basic_response = response.basic
# instantiate certificate store
cert_store = OpenSSL::X509::Store.new
# add the ocsp responder's cert and its root ca cert
cert_store.add_file('ocsp_cert')
cert_store.add_file('ocsp_cert_root')
# finally the verification
basic_response.verify([], cert_store)
# result:
=> OCSP routines:OCSP_basic_verify:signer certificate not found
when i try to double-add certs, i do get the expected error:
# instantiate certificate store
cert_store = OpenSSL::X509::Store.new
# double-add the ocsp responder's cert and its root ca cert
cert_store.add_file('ocsp_cert')
cert_store.add_file('ocsp_cert')
# result:
=> cert already in hash table (OpenSSL::X509::StoreError)
i'm not sure how else to trouble shoot, as i am not good in reading the source of these functions.
This leads me to my questions:
1. Is there any way to dump and analyse the content of said hash table, so i can be sure the right certificates are loaded?
2. Am i missing something obvious here?
thanks for any input and feedback.
fyi, the system i try to verify certificates against is the ocsp responder of the Estonian id card certificate centre.
To expand on my earlier comment, here's an example script that uses the current certificate on www.verisign.com.
require 'net/http'
require 'openssl'
data = Net::HTTP.get(URI("http://sr.symcd.com/MFEwTzBNMEswSTAJBgUrDgMCGgUABBR0JBRnBp/14Jg/Xj4aa6BlKlQVdQQUAVmr5906C1mmZGPWzyAHV9WR52oCEB8AC4suGGDADBOTgwigBIE="))
response = OpenSSL::OCSP::Response.new(data)
issuer = OpenSSL::X509::Certificate.new("-----BEGIN CERTIFICATE-----
MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw
CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs
YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9
FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM
BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL
pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu
IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c
zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj
MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw
BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov
L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z
eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi
LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk
Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F
nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE
qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P
eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw
LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j
2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q==
-----END CERTIFICATE-----")
root = OpenSSL::X509::Certificate.new("-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----")
store = OpenSSL::X509::Store.new
store.add_cert(issuer)
store.add_cert(root)
puts response.basic.verify([], store)
The output should be "true".

Why is this Ruby SSL-enabled server/client test working?

I'm working through creating an SSL-enabled server in Ruby, along with a corresponding Ruby client to use with the server. In order to test, I created my own Root CA certificate with the following commands.
$:~/devel/ssl-test/ssl/CA$ openssl genrsa -out TestCA.key 2048
Generating RSA private key, 2048 bit long modulus
............+++
...........................+++
e is 65537 (0x10001)
$:~/devel/ssl-test/ssl/CA$ openssl req -new -key TestCA.key -out TestCA.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$:~/devel/ssl-test/ssl/CA$ openssl x509 -req -days 365 -in TestCA.csr -out TestCA.crt -signkey TestCA.key
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key
I then generated an SSL certificate for my server:
$:~/devel/ssl-test/ssl/keys$ openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.+++
............................................+++
e is 65537 (0x10001)
$:~/devel/ssl-test/ssl/keys$ cd ../csrs/
$:~/devel/ssl-test/ssl/csrs$ openssl req -new -key ../keys/server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:my.secure.test
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$:~/devel/ssl-test/ssl/csrs$ cd ../certs/
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
I am unable to access the ./demoCA/newcerts directory
./demoCA/newcerts: No such file or directory
$:~/devel/ssl-test/ssl/certs$ mkdir -p demoCA/newcerts
$:~/devel/ssl-test/ssl/certs$ touch demoCA/index.txt
$:~/devel/ssl-test/ssl/certs$ echo "01" > demoCA/serial
$:~/devel/ssl-test/ssl/certs$ openssl ca -in ../csrs/server.csr -cert ../CA/TestCA.crt -keyfile ../CA/TestCA.key -out server.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Oct 25 16:25:05 2011 GMT
Not After : Oct 24 16:25:05 2012 GMT
Subject:
countryName = AU
stateOrProvinceName = Some-State
organizationName = Internet Widgits Pty Ltd
commonName = my.secure.test
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
48:50:B5:04:11:02:F1:40:97:58:BF:5F:8B:27:50:10:C0:3F:EE:D9
X509v3 Authority Key Identifier:
DirName:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
serial:81:44:16:06:5C:EB:5E:71
Certificate is to be certified until Oct 24 16:25:05 2012 GMT (365 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
After that, I created a simplistic SSL-enabled server to use the SSL certificate I just created.
require 'socket'
require 'openssl'
require 'thread'
server = TCPServer.new(1337)
context = OpenSSL::SSL::SSLContext.new
context.cert = OpenSSL::X509::Certificate.new(File.open('ssl/certs/server.crt'))
context.key = OpenSSL::PKey::RSA.new(File.open('ssl/keys/server.key'))
secure = OpenSSL::SSL::SSLServer.new(server, context)
puts 'Listening securely on port 1337...'
loop do
Thread.new(secure.accept) do |conn|
begin
while request = conn.gets
$stdout.puts '=> ' + request
response = "You said: #{request}"
$stdout.puts '<= ' + response
conn.puts response
end
rescue
$stderr.puts $!
end
end
end
When started, it seems to work fine...
$:~/devel/ssl-test$ ruby server.rb
Listening securely on port 1337...
I then created a non-SSL capable client just to ensure it was denied connectivity.
require 'socket'
require 'thread'
client = TCPSocket.new('127.0.0.1', 1337)
Thread.new do
begin
while response = client.gets
$stdout.puts response
end
rescue
$stderr.puts "Error from client: #{$!}"
end
end
while request = $stdin.gets
request = request.chomp
client.puts request
end
When I run this via the following:
$:~/devel/ssl-test$ ruby client.rb
hello
Error from client: Connection reset by peer
Correspondingly, I get the following from the server:
$:~/devel/ssl-test$ ruby server.rb
Listening securely on port 1337...
/usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept': SSL_accept returned=1 errno=0 state=SSLv2/v3 read client hello A: unknown protocol (OpenSSL::SSL::SSLError)
from /usr/local/rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/openssl/ssl-internal.rb:164:in `accept'
from server.rb:16:in `block in <main>'
from server.rb:15:in `loop'
from server.rb:15:in `<main>'
This was all expected. Next, I modified the client code to use an SSL context.
require 'socket'
require 'openssl'
require 'thread'
client = TCPSocket.new('127.0.0.1', 1337)
context = OpenSSL::SSL::SSLContext.new
secure = OpenSSL::SSL::SSLSocket.new(client, context)
secure.sync_close = true
secure.connect
Thread.new do
begin
while response = secure.gets
$stdout.puts response
end
rescue
$stderr.puts "Error from client: #{$!}"
end
end
while request = $stdin.gets
request = request.chomp
secure.puts request
end
I fully expected this to fail as well during the handshake process, but it did not... I got the following result:
$:~/devel/ssl-test$ ruby client.rb
hello
You Said: hello
Why did this work? I was assuming it would fail because I didn't think the client would have any idea about the Root CA I created and signed the server SSL certificate with, and therefore wouldn't be able to verify the server's certificate. What am I missing? When I created and signed the server's certificate and it was "committed", did this somehow make it available to the OpenSSL library? I was expecting to have to somehow tell the SSL context in the client where to look for the Root CA I created for testing purposes...
As a follow-up test, I copied my client code over to a different machine that definitely knows nothing about the Root CA I created for this test, changed the IP address the client connects to, and ran the test again. This test produced the same results - the client was able to communicate with the server when I assumed it wouldn't be able to. Any ideas?
Depending on the version of Ruby you are using, the default verify mode for the SSLContext object may not be enforcing certificate verification. You can force the verify mode with:
context = OpenSSL::SSL::SSLContext.new
context.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
This should cause the client's connection attempt to fail, as expected.

Resources