SSLError: 主机名"W.X.Y.Z"与服务器证书不匹配

5
我刚开始学习Ruby,经过一些基础知识后,我正在尝试了解如何在Ruby中进行REST调用。我可以毫不困难地向foursquare API发出get请求。另一方面,对Cisco CMX API的调用却会出现错误。我的Ruby版本是2.1.2。我在网上搜索了很多解决方案,但仍然遇到问题。这是我运行的Shell命令。
工作正常的命令:
$resclient
>> RestClient.get 'https://api.foursquare.com/v2/venues/search?ll=40.7,-74&oauth_token=0ZDO1JMJ0PW2QTCDK50OGZ21UENHZ0Y3KIDQZJLLURTQNRQ2&v=20150106'

这会出现错误

$restclient
>> RestClient.get 'https://learning:learning@64.103.26.61/api/contextaware/v1/maps/.json'

我的错误日志:

OpenSSL::SSL::SSLError: hostname "64.103.26.61" does not match the server certificate
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/2.1.0/openssl/ssl.rb:139:in `post_connection_check'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/http.rb:922:in `connect'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/http.rb:863:in `do_start'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/http.rb:852:in `start'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rest-client-1.7.2/lib/restclient/request.rb:413:in `transmit'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rest-client-1.7.2/lib/restclient/request.rb:176:in `execute'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rest-client-1.7.2/lib/restclient/request.rb:41:in `execute'
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rest-client-1.7.2/lib/restclient.rb:65:in `get'
from (irb):3
from /Users/apple/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/rest-client-1.7.2/bin/restclient:93:in `<top (required)>'
from /Users/apple/.rbenv/versions/2.1.2/bin/restclient:23:in `load'
from /Users/apple/.rbenv/versions/2.1.2/bin/restclient:23:in `<main>'

你能给一些意见吗?谢谢

3个回答

14

您能给一些建议吗?

这里有一个更详细的答案,以及如何在Ruby中修复问题,而不是使用可悲的OpenSSL::SSL::VERIFY_NONE

$ openssl s_client -connect 64.103.26.61:443 | openssl x509 -text -noout
depth=2 C = BM, O = QuoVadis Limited, CN = QuoVadis Root CA 2
...
        Subject: C=US, ST=CA, L=San Jose, O=Cisco Systems, Inc., CN=msesandbox.cisco.com
...
            X509v3 Subject Alternative Name: 
                DNS:msesandbox.cisco.com

所以该设备具有 msesandbox.cisco.com 的 DNS 名称。nslookup 告诉您这是一个良好的主机名:

$ nslookup msesandbox.cisco.com
Server:     192.168.1.1
Address:    192.168.1.1#53

Non-authoritative answer:
Name:   msesandbox.cisco.com
Address: 64.103.26.61

因此,你首先要做的是通过DNS名称连接它,而不是IP地址。

如果您为cisco.com域发行证书(或可以提出请求),那么您可以要求将IP地址64.103.26.61添加为主题备用名称(SAN)。因此,证书中将会有两个SAN。


现在,如果你回到openssl命令:

$ openssl s_client -connect 64.103.26.61:443 | openssl x509 -text -noout
depth=2 C = BM, O = QuoVadis Limited, CN = QuoVadis Root CA 2
verify error:num=19:self signed certificate in certificate chain
...
    Issuer: C=US, O=HydrantID (Avalanche Cloud Corporation), CN=HydrantID SSL ICA G2
...
    Subject: C=US, ST=CA, L=San Jose, O=Cisco Systems, Inc., CN=msesandbox.cisco.com

您会看到证书的颁发者和主题不同。这意味着这不是自签名证书。HydrantID(Avalanche Cloud Corporation)颁发了该证书。

如果您进一步查看,您会发现颁发者的公钥(授权密钥标识符)与主题的公钥(主题密钥标识符)不同:

X509v3 Authority Key Identifier: 
    keyid:98:6A:B6:2D:2E:BF:A7:AA:9F:F6:F7:D6:09:AF:D5:8B:57:F9:8A:B7
...
X509v3 Subject Key Identifier: 
    B5:3D:50:53:0A:A2:06:9E:9A:29:89:7A:AB:96:90:FE:9D:6B:57:A0

再次强调,它不是自签名的。


如果你回到 OpenSSL 命令,你会看到颁发者是 HydrantID SSL ICA G2,它的颁发者是 QuoVadis Root CA2 G3

depth=2 C = BM, O = QuoVadis Limited, CN = QuoVadis Root CA 2
verify return:1
depth=1 C = US, O = HydrantID (Avalanche Cloud Corporation), CN = HydrantID SSL ICA G2
verify return:1
depth=0 C = US, ST = CA, L = San Jose, O = "Cisco Systems, Inc.", CN = msesandbox.cisco.com
verify return:1

这意味着QuoVadis Root CA2 G3颁发了HydrantID SSL ICA G2; 而HydrantID SSL ICA G2又颁发了msesandbox.cisco.comQuoVadis Root CA2 G3是食物链的顶端。

您可以从QuoVadis CA证书下载获取QuoVadis Root CA2 G3

$ curl -O -J -L https://www.quovadisglobal.bm/Repository/~/media/Files/Roots/quovadis_rca2g3_der.ashx
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1380  100  1380    0     0   1808      0 --:--:-- --:--:-- --:--:--  5726
curl: Saved to filename 'quovadis_rca2g3_der.cer'

$ openssl x509 -in quovadis_rca2g3_der.cer -inform DER -out quovadis-ca.pem -outform PEM
$ cat quovadis-ca.pem 
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
...
-----END CERTIFICATE-----

如果你信任QuoVadis对这个设备进行认证,那么:

$ openssl s_client -connect msesandbox.cisco.com:443 -CAfile quovadis-ca.pem 
CONNECTED(00000003)
depth=2 C = BM, O = QuoVadis Limited, CN = QuoVadis Root CA 2
verify return:1
depth=1 C = US, O = HydrantID (Avalanche Cloud Corporation), CN = HydrantID SSL ICA G2
verify return:1
depth=0 C = US, ST = CA, L = San Jose, O = "Cisco Systems, Inc.", CN = msesandbox.cisco.com
verify return:1
...

    Start Time: 1420616960
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)

注意OpenSSL完成并显示Verify return code: 0 (ok),这表明您已获得了一个良好的证书链。OpenSSL不执行主机名匹配,但我们已经知道证书中的主机名是正确的。


现在,对于Ruby代码,您只需要将CA插入Ruby即可:

#!/usr/bin/ruby

require 'net/http'
require 'net/https'
require 'openssl'

uri = URI('https://msesandbox.cisco.com:443')

options_mask = OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | OpenSSL::SSL::OP_NO_COMPRESSION

http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)

if uri.scheme == "https"
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  http.ca_file = File.join(File.dirname(__FILE__), "quovadis-ca.pem")
  # http.ssl_options = options_mask
end

response = http.request request

这里有一次奔跑:

$ ./Connect-Test.rb 
$ 

没有例外,也不要使用 OpenSSL::SSL::VERIFY_NONE

你应该尝试使用 options_mask,因为它会删除弱/受损/破碎的协议。但是由于 Ruby 有时太过混乱和未记录,我从未能够让它正常工作。


我能够使用 OpenSSL 对 HydrantID SSL ICA G2QuoVadis Root CA2 G3 进行根信任(意味着我从 OpenSSL 得到了 Verify Result 0 (OK))。但是 Ruby 只能处理 QuoVadis Root CA2 G3 (它无法构建到 HydrantID SSL ICA G2 的链)。更多的 Ruby 混乱。


1
谢谢您的详细解答。我现在理解了问题。看起来这个自签名证书不适用于我的本地主机。我安装了一个叫做certified的gem,它可以处理这个问题(我不知道它是如何工作的)。我从代码中删除了OpenSSL :: SSL :: VERIFY_NONE。正如你所说的,对于生产环境,我将从QuoVadis CA证书下载证书。 - Ahmet Tanakol

1
如果您使用浏览器访问您的主机,比如:

>  https://64.103.26.61/

你会看到你将得到相同的错误。该服务器上的证书无效,因为服务器响应的主机名与证书中写入的不同。
如果您在Digicert Helpcenter输入服务器地址,则可以查找更复杂的描述。该证书已发行给msesandbox.cisco.com。如果这是您要访问的地址,请使用此地址而不是IP地址。如果这是您的服务器,请更改响应名称。

不幸的是,它不是我的。 - Ahmet Tanakol
这是一种可行的方法,但非常不安全,所以我不建议你这样做。 - chenino

-2

一种几乎总是不好的解决方法是忽略SSL证书检查:

:verify_ssl => OpenSSL::SSL::VERIFY_NONE

更新:这种解决方法几乎从来不被推荐,因为它通常会成为一个明显的安全漏洞,很容易被忽略。在大多数情况下,如果您认为需要SSL而没有检查证书,最好生成自签名证书并锁定双方使用相同的证书。


3
不,那不是一个解决方案。它可以成为解决方案的一部分,例如公钥固定(Public Key Pinning)。但是作为独立的措施,它根本不是解决方案。 - jww
我认为如果你知道自己在做什么,或者在受控环境下,或者尝试与本地主机通信,那么这是一个可以接受的解决方案。并不是像 SSL 解决了所有安全问题,也不是没有使用 SSL 就自动变得非常不安全。 - NoelProf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接