分享 一则 Ruby 和 OpenSSL CA 证书的问题

lgn21st · 2015年03月26日 · 最后由 will7v 回复于 2015年07月20日 · 15234 次阅读

作为一个版本控,总是希望保持电脑中各种软件到最新版本。

最近通过 brew 升级 OpenSSL 和 ruby-build 到最新,尤其是 ruby-build 支持最新的 Ruby 2.2.1,新版 Ruby 中针对 Symobl GC 的改进很让人期待,升级步骤略暴力,采用自毁重装的方式:

rm -rf ~/.rbenv
rbenv install 2.2.1
rbenv global 2.2.1

升级后发现所有的 ssl 的请求,都会报告证书验证错误,具体错误信息如下:

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

暂时的解决方案,bypass 掉 SSL 的证书验证步骤,在 Rails 的 initalizers 中增加一个文件 bypass_ssl_verification.rb,只一行代码:

OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE

不过 bypass 证书验证步骤只是权宜之计,在本地开发倒是无所谓,生产环境下必须不能这么干!因为如果不验证证书的话,其实是留下了安全隐患。造成证书验证错误问题的原因可能有三种:

  1. 通过 brew 升级 OpenSSL 的过程中,证书文件出了问题,损坏或者未正确安装。
  2. 证书正常安装,但是路径不匹配。
  3. 安装包中附带的证书文件过期了。

首先检查证书不存在或者路径不对,brew 安装的 openssl 会把证书位置存放在这里:

/usr/local/etc/openssl/cert.pem

Ruby 运行环境中的 OpenSSL 的默认证书位置:

ruby -e "require 'openssl'; puts OpenSSL::X509::DEFAULT_CERT_FILE"

检查后发现 Ruby 运行环境中的结果跟 brew 安装的 OpenSSL 证书的位置完全对得上,且证书文件存在,说明不是证书丢失或者路径问题,最大的可能是证书过期了。

如果之前没有通过 brew 安装过 openssl 的话,ruby-build 会在编译 Ruby 的过程中自动下载并编译一份 openssl 到 ~/.rbenv 目录下,但是我亲测下来 ruby-build 自带的 openssl 也会产生同样的错误,所以最后还是决定保留 brew 安装的 openssl 版本,然后手动下载更新 ca root 证书的方式解决问题:

wget -c http://curl.haxx.se/ca/cacert.pem
mv cacert.pem /usr/local/etc/openssl/cert.pem 

检查了一下,在 cert.pem 的头部有证书生成时间,是 2015 年 2 月 25 日,还算蛮新的。

##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Wed Feb 25 04:12:04 2015
##

You saved my life 👍

#2 楼 @luikore :plus1: 这是科学使用证书的方法。

好贴怎么这样沉了。

如果是使用 RVM 的,可以先用 rvm osx-ssl-certs status all 查看 CA 证书的状态,如果显示 Old,就用 rvm osx-ssl-certs update all 升级 CA 证书 这个方法,简单便捷

另外,有人遇到过 OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: sslv3 alert handshake failure 这个错误并解决了的吗?

#6 楼 @crayon 遇到并解决多次,可能原因有:网络不通,本地或服务器证书没更新,证书被劫持,服务器太忙,本地防火墙设置,需要客户端证书,操作系统 bug, OpenSSL 安装不完整/链接错误,OpenSSL 的 Ruby 绑定安装不完整,rubygems 太老以致于 rubygems/ssl_certs 里的证书过期,人品不好等等....

首先你得定位出是哪个 url 出的问题,最笨的是人肉二分查找肯定能找出来。然后 curl -I 地址 看看报错信息是什么,如果 curl 没问题而 Ruby 有问题,大致可以知道是 Ruby 的安装问题了。

如果不想解决问题,只想盖住问题,可以尝试换台 VPS 上的机器重做,一般都会好...

#7 楼 @luikore 😂 折腾 OpenSSL 好艰辛,不拆。

#7 楼 @luikore 感谢你详细的回复 ruby 是用 rvm 安装的,rvm 已更新到最新,1.9、2.0、2.1、2.2 四个版本的 ruby 都安装来试了,都是报同样的错误 ca 证书更到最新,openssl 更到最新,问题依然 然后,我用 python 2.7 调用同一个 API,正常 curl -I 也正常 但是,试过好几个其他人的电脑,只要是 ruby 环境,就报同样的错误 同时,Android 5.0 的设备也出现了不能访问 API 的情况

本地环境下,实在不知道如何解决了,只能将目光转向服务端 SSL 证书是配置在负载均衡的,负载均衡设备比较老,openssl 版本比较低,但又一时无法联系厂商去升级 同时,做了全站的 CDN,这个问题的排查变得更加困难

今天,终于解决了这个问题! 原先负载均衡里,Cipher 没指定,是 default,根据 Chrome 浏览器查看的结果,基本是 3DES_EDE_CBC 和 RC4_128 这两种 今天,把这个 Cipher 设置成了 AES256-SHA,问题就解决了!

好文章,顶起来!

又来问题了,

wget -c http://curl.haxx.se/ca/cacert.pem
mv cacert.pem /usr/local/etc/openssl/cert.pem 

下载证书,更新之后,一切都正常了, 但是每天都会被替换掉,格式跟‘security find-certificate’生产出来的一样,开头就是 SHA1, 不包含,

##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Wed Feb 25 04:12:04 2015
##

然后 Rails 就又报错了!Any Idea?

微信刚接的项目就掉这个坑,是看这个链接里 sheharyarn 的答案解决的 简单说就是下一个 cacert.pem,把这个添加到 zshrc 环境变量里,每次都用这个证书做 ssl 验证

huacnlee 关闭了讨论。 08月24日 15:03
需要 登录 后方可回复, 如果你还没有账号请 注册新账号