2012年12月2日日曜日

[Ruby] open-uri の HTTPS リクエストで certificate verify failed

open-uri の HTTPS リクエストで、証明書の検証に失敗する場合の対処方法です。
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

##### 方法1 #####
証明書を検証をしない(乱暴な方法)

require 'open-uri'
require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
puts open('https://www.google.co.jp/').read

ただ、定数を変更すると警告が出ます。
warning: already initialized constant VERIFY_PEER

この警告を抑えるには、事前に remove_const します。
さらには、メソッドの中で定数を変更する場合ならば、上記のままだと dynamic constant assignment エラーになるので、迂回する方法もあります。
OpenSSL::SSL.module_eval{ remove_const(:VERIFY_PEER) }
OpenSSL::SSL.const_set( :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE )

ただ、現時点では上記の定数を変更するやり方が多く出回っていますが、
これは方法として(場合により)乱暴じゃないですか?


##### 方法2 #####
証明書を検証をしない(堅実な方法)

require 'open-uri'
require 'openssl'
puts open('https://www.google.com/', :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE).read

随分とスッキリしました。


##### 方法3 #####
接続したいサーバーの公開鍵証明書を使用します。

require 'open-uri'
require 'openssl'
puts open('https://www.google.co.jp/', :ssl_ca_cert => './googlecom.cer' ).read

証明書ファイル googlecom.cer は準備する必要があります。

証明書ファイルの入手方法(Windows)
> https://www.google.com をブラウザで開く
> 鍵マーク(ブラウザによる)をクリックして、「証明書」ダイアログを表示
> 「詳細」タブの「ファイルにコピー」ボタンを押す。
> 「証明書エクスポートウィザード」が開始されるので、ウィザードに従う
> # ファイル形式は「Base64 encoded X.509」を選択して、ファイルを出力する。


##### 方法4 #####
ルートCA証明書を使用します。

Rubyのデフォルト設定では、ルートCA証明書は無いのでは?
デフォルトの CA 証明書パスをコマンドで確認できます。
ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE'

まずは、Googleのルート証明書を準備します。

証明書ファイルの入手方法(Windows)
> https://www.google.com をブラウザで開く
> 鍵マーク(ブラウザによる)をクリックして、「証明書」ダイアログを表示
> 「証明のパス」タブを開いて、証明書のパス ツリーのトップ(ルート)を選択する
> # ※現在は、こうなっていました。
> # GeoTrust(ルート)
> #  + Google Internet Authority
> #   + google.com
> 証明書の表示ボタンを押すと、再び「証明書」ダイアログが開く
> ※あとは「方法2」と同じです
> 「詳細」タブの「ファイルにコピー」ボタンを押す。
> 「証明書エクスポートウィザード」が開始されるので、ウィザードに従う
> # ファイル形式は「Base64 encoded X.509」を選択して、ファイルを出力する。

この CA 証明書を、前記のパスに保存すれば良いのですが・・・!?
Ruby 1.9.3 - Windows7 の場合は、デフォルトの CA 証明書ファイルは
C:/Users/Luis/Projects/oss/oneclick/rubyinstaller/sandbox/openssl/ssl/cert.pem

さすがにココで証明書を管理したくは無いです、ただデフォルト定義を変更するにはいろいろありまして・・・証明書ファイル名を変更する方法はこちら

入手した証明書を環境変数 (SSL_CERT_FILE) に設定するのが、すんなりと納まると思われる。
(例) set SSL_CERT_FILE=C:\work\cert.pem


##### 方法5 #####
ルートCA証明書を使用 2nd.

デフォルトの CA 証明書ディレクトリを変更します。
ただ、証明書のファイル名はハッシュ名である必要があり、大がかりなシステムでもなければ複数の証明書ファイルを使う必要が無いと思われるので詳細は省略します。

デフォルトの CA 証明書ディレクトリは、やはり定数に定義されています。
ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_DIR'


##### 方法6 #####
ルートCA証明書を使用 3rd.

安易には、様々な認証局のルートCA証明書てんこもりの証明書を使う

cURL が提供しているルートCA証明書寄せ集め
http://curl.haxx.se/ca/cacert.pem

Google ならばルートCAはジオトラスト社 (GeoTrust) でしたが、これも含まれています。この証明書を先に示した方法で認識させます。
環境変数 (SSL_CERT_FILE) に設定する場合は
(例) set SSL_CERT_FILE=C:\work\cacert.pem