net/httpsでクライアント証明書を使って接続する

何回かハマッたのでついに備忘録代わりにメモ。
すべてのケースでうまくいくかどうかは知らない。

手順だけ示すとこんな感じだった.
以下の例は全て、InternetExplorer(6とか)を使う前提。

  1. アクセス先ホストの公開鍵証明書チェーンのルートCA証明書を Base64 encoded (.cer) で保存
    1. IEで実際に目的のサイトにアクセスしてみる
    2. 右下の錠アイコンをダブルクリック
    3. 「証明書ボタン」をクリック
    4. 「証明のパス」タブをクリック
    5. 証明のパスのうち、ルートにあたるものをダブルクリック
    6. 開いた証明書ウインドウで、また「詳細」タブをクリック
    7. 「ファイルにコピー」をクリック
    8. "Base 64 encoded X509 (.CER)" を選択して「次へ」
    9. ファイル名は適当に。今回の例では例えば "root.cer" とする。
  2. クライアント証明書を秘密鍵付きで、.pem形式で保存する
    1. クライアント証明書を .pfx でexportする
      1. 「インターネットオプション」を開く
      2. 「コンテンツ」タブを選択
      3. 「証明書」ボタンをクリック
      4. 使うクライアント証明書を選択して、「エクスポート」ボタンをクリック
      5. 「はい、秘密キーをエクスポートします」と宣言してしまう
      6. pfx形式で、「すべて含む」をON、「強力な保護」をOFF、「削除する」をOFFにする*1
      7. 秘密鍵のパスワードを入れる
      8. 出力先を決める。ここでは "client.pfx" とする。
    2. pkcs12(.pfx)→x509(.pem) 変換する
      1. 以下のコマンドをたたく(例)
C:\temp>openssl pkcs12 -in client.pfx -out client.pem -nodes
Enter Import Password: (先ほどのパスワードを入れる)
MAC verified OK
C:\temp>

ここまで来たらrubyで (proxyを使う例 使わないときはProxyを使わなければよい)

    @client = Net::HTTP::Proxy($proxy_addr, $proxy_port).new("目的ホスト",443)
    @client.use_ssl = true
    @client.ca_file = './root.cer'
    @client.cert = OpenSSL::X509::Certificate.new(File.read('./client.pem'))
    @client.key = OpenSSL::PKey::RSA.new(File.read('./client.pem'))
    @client.verify_mode = OpenSSL::SSL::VERIFY_PEER
    @client.verify_depth = 5

    @client.start

これでOKかと。
クライアント証明書は解り易いんだけど、相手サイトの公開鍵証明書*のルートCA証明書*をca_fileに指定しないといけない、ってのが躓いた。

*1:この組み合わせはいろいろ試してないので他の方法でもうまくいくかも