为啥 Ruby 无法验证 SSL 证书?

Posted

技术标签:

【中文标题】为啥 Ruby 无法验证 SSL 证书?【英文标题】:Why is Ruby unable to verify an SSL certificate?为什么 Ruby 无法验证 SSL 证书? 【发布时间】:2012-03-01 06:17:54 【问题描述】:

这是我第一次尝试使用 XMLRPC::Client 库与远程 API 交互,但我一直收到此错误:

warning: peer certificate won't be verified in this SSL session

四处搜索,我发现很多人都遇到了这个错误。通常它带有自签名证书,他们只是希望它消失,所以他们做了一些肮脏的事情,比如用 XMLRPC::Client 打开它的 http 会话的方式进行猴子补丁。

我最初认为这只是客户不关心证书是否有效,所以我继续搜索并遇到了this gem。它只是强制验证所有 SSL 证书,如果它也不能,则会抛出一个硬错误。这正是我想要的。我包含它,再次运行代码,现在我得到了这个:

OpenSSL:SSL::SSLError:
  SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B:
  certificate verify failed

当然!证书不好!但我仔细检查以确保 openssl 的内置 s_client 像这样:

openssl s_client -connect sub.example.com:443

我会得到什么:

CONNECTED(00000003)
---
Certificate chain
<snip>
Verify return code: 0 (ok)

所以现在我们来回答我的问题。 OpenSSL(命令行版本)说证书很好。 OpenSSL(Ruby 库)不同意。我所有的网络浏览器都说证书很好。

一些可能有用的附加细节。证书是通配符,但对域有效。除了 Ruby 代码之外,openssl s_client 在同一台机器上运行了几秒钟。这是与 RVM 一起安装的 Ruby 1.8.7 p357。

Ruby 是否使用主机操作系统提供的 CA 包以外的东西?有没有办法告诉 Ruby 使用特定的 CA 包或系统包?

【问题讨论】:

【参考方案1】:

如果您有 ca-certificates 文件,请执行以下操作:

http.ca_file = <YOUR CA-CERT FILE PATH>
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_depth = 5

【讨论】:

我应该在哪里设置?【参考方案2】:

如果您只对如何使 Ruby 以与 OpenSSL s_client 或您的浏览器相同的方式运行感兴趣,您可以跳到最后一部分,我将在下文中详细介绍。

默认情况下,用于建立连接的OpenSSL::X509::Store 根本不使用任何受信任的证书。根据您对应用程序域的了解,您通常会为 X509::Store 的实例提供与您的应用程序相关的可信证书。有几种选择:

Store#add_file 采用 PEM/DER 编码证书的路径 Store#add_cert 采用 X509::Certificate 的实例 Store#add_path 采用指向可找到受信任证书的目录的路径

“浏览器”方法

这与浏览器、Java (cacerts) 或具有自己内部可信证书存储的 Windows 的做法形成鲜明对比。该软件预先配备了一组受信任的证书,在软件供应商看来,这些证书被认为是“好”的。一般来说,这不是一个坏主意,但如果你真的查看这些集合,那么你很快就会注意到证书太多了。个人无法真正判断是否应该盲目信任所有这些证书。

Ruby 方法

另一方面,典型 Rub​​y 应用程序的要求与浏览器的要求有很大不同。浏览器必须能够让您导航到任何带有 TLS 证书并通过 https 提供服务的“合法”网站。但在典型的 Ruby 应用程序中,您只需要处理一些使用 TLS 或需要证书验证的服务。

Ruby 方法还有一个好处——虽然它需要更多的手动工作,但您最终会得到一个完全信任在给定应用程序上下文中应该信任的证书的手工定制解决方案。这很乏味,但是这种方式的安全性要高得多,因为您暴露的攻击面要少得多。以最近发生的事件为例:如果您不必在信任集中包含 DigiNotar 或任何其他受损根,那么此类违规行为就不会影响您。

然而,正如您已经注意到的那样,这样做的缺点是,默认情况下,如果您不主动添加受信任的证书,OpenSSL 扩展将无法验证 任何 对等证书一点也不。为了使事情正常进行,您必须手动设置配置。

这种不便导致了许多可疑的措施来规避它,最糟糕的是全局设置OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE。请不要这样做。我们甚至开玩笑说,如果我们遇到这种 hack,添加的代码会让您的应用程序随机崩溃:)

如果手动信任设置看起来过于复杂,我现在将提供一个简单的替代方案,它使 OpenSSL 扩展的行为与 s_client 等 OpenSSL CLI 命令完全相同。

为什么s_client可以验证证书

OpenSSL 使用与浏览器和 Windows 类似的方法。典型的安装会将一组受信任的证书放在硬盘的某个位置(例如/etc/ssl/certs/ca-bundle.crt),这将作为默认的受信任证书集。这就是s_client 在需要验证对等证书时所查看的位置,这就是您的实验成功的原因。

让 Ruby 像 s_client 一样工作

如果您仍然希望在使用 Ruby 验证证书时获得同样的舒适感,您可以通过调用 OpenSSL::X509::Store#set_default_paths 告诉它使用受信任证书的 OpenSSL 捆绑包(如果在您的系统上可用)。可以在here 找到更多信息。要将其与XMLRPC::Client 一起使用,只需确保在它使用的X509::Store 上调用set_default_paths

【讨论】:

非常感谢。向商店添加特定的 CA 证书正是我想要的。我特别感谢冗长而详细的答案。 我认为您可能会欣赏我如何ended up solving 我的特殊问题。它不是最干净的,扩展 XMLRPC::Client 可能会更好,但对于我正在做的事情来说,这似乎有点矫枉过正。 很棒的详细回复。一般来说,我在 Ruby 中遇到了类似的问题(与 xmlrpc 无关),并且一直在努力找出我所缺少的东西。这最终帮助我把这些碎片拼凑在一起。所以谢谢! @SamStelfox 博客文章的更新网址是 stelfox.net/blog/2012/02/rubys-xmlrpc-client-and-ssl @SamStelfox 的博文更新后的 URL 是 here。

以上是关于为啥 Ruby 无法验证 SSL 证书?的主要内容,如果未能解决你的问题,请参考以下文章

当 HTTPS 站点使用“ISRG Root X1”的 CA 时,Python3.4+requests 2.26 无法验证 SSL 证书 - 为啥?

如何使用 net/http 在 ruby​​ 中验证 SSL 证书链

Ruby 2.6.6 OpenSSL 1.1.1g - 证书验证失败(无法获取本地颁发者证书)

为啥我的SSL证书验证失败

为啥要使用证书对客户端进行身份验证?

iOS 和 SSL:无法验证自签名服务器证书