Windowsでwgetを気分良く使うのに Win32 API と Python の助けを借りる

「wget的な(一括・オフライン)ダウンロード」のために wget そのものにこだわる時代でもないし、そもそも「wget的に持ってきてオフラインで」という需要自体が年々減ってるんで、旬なネタではないけれど。

ときどきこのサイトで書いてる通りで、「インターネットに行けない環境に、チームメンバーのために一括で持ってきておく」ために稀に使うのと、あとページ取得だけなら python であれば urllib, urllib2 とかでやった方が柔軟だったりはするけどそれでも「手間にはなる」ので、そういう場合に wget でいーや、てのも、多少はあってだな。

MSYSのでもGnuWin32のでもいいけれど、https なサイトだと

1 --2015-05-11 21:20:42--  https://en.wikipedia.org/wiki/Japan_Airlines
2 Resolving en.wikipedia.org... 198.35.26.96
3 Connecting to en.wikipedia.org|198.35.26.96|:443... connected.
4 ERROR: cannot verify en.wikipedia.org's certificate, issued by `/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2':
5   Unable to locally verify the issuer's authority.
6 To connect to en.wikipedia.org insecurely, use `--no-check-certificate'.
1 SYSTEM_WGETRC = c:/progra~1/wget/etc/wgetrc
2 syswgetrc = c:\Program Files (x86)\GnuWin32/etc/wgetrc
3 --2015-05-11 21:34:38--  https://en.wikipedia.org/wiki/Japan_Airlines
4 en.wikipedia.org をDNSに問いあわせています... 198.35.26.96
5 en.wikipedia.org|198.35.26.96|:443 に接続しています... 接続しました。
6 エラー: en.wikipedia.org の証明書(発行者: /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2)の検証に失敗しました:
7   発行者の権限を検証できませんでした。
8 en.wikipedia.org に安全の確認をしないで接続するには `--no-check-certificate' を使ってください。
9 SSL による接続が確立できません。

となるよね。

で、気持ちよくはないけれど --no-check-certificate 付ければ、まぁ用は足せる。けど気持ちよくはない。なにがって、 「IE、Chrome で「信頼」出来ている」サイトを「信頼出来ないとは何事だ」 てことだわ。ブラウザでも信頼してないものならそれは望むところだろ、むしろ。

半年ほど前に PC がぶっ壊れる前は色んなサイト参照して、ブラウザから証明書拾い上げて wget に読み込ませる、としてたんだけれど、今探してみてもなんだかどのサイトもあんまり綺麗に整理してくれてない。そもそも「ブラウザの設定からエキスポート」みたいなのも情報が廃れやすくて、「今のブラウザと UI が違う」のでわかりにくかったり。仕方ないので、改めて自分でも探してみた。

最初 openssl そのもののコマンドラインを駆使する方法を見つけたけれど、なんか鬱陶しいし、と思ってたら、「最も Windows フレンドリ」な方法を Python でやってる人がいた。PyPI に登録されてて、wincertstore。何かというと、ctypes で crypt32.dll とダイレクトにお話してる。「IE で「信頼」出来ている」サイトを「信頼出来ないとは何事だ」との格闘としては、最も正攻法ではないか:

小さいコードなので、全編読んでみるといいんじゃないかな(*)。

これ、単独としても用途ありそうだけど、ひとまず wget のためには、こんなコードを書く:

1 import wincertstore
2 
3 certfile = wincertstore.CertFile()
4 certfile.addstore("CA")
5 certfile.addstore("ROOT")
6 print(certfile.read())
7 certfile.close()

この出力を使う。出力はこんな感じ:

 1 -----BEGIN CERTIFICATE-----
 2 MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
 3 MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
 4 ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
 5 eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
 6 ...(snip)...
 7 pu/xO28QOG8=
 8 -----END CERTIFICATE-----
 9 -----BEGIN CERTIFICATE-----
10 MIIFdzCCBF+gAwIBAgIQE+oocFv07O0MNmMJgGFDNjANBgkqhkiG9w0BAQwFADBv
11 ...(snip)...
12 -----END CERTIFICATE-----

これの標準置き場…ってどこなのだろう? Unix では多分 /etc/ssl/certs/ca-certificates.crt と思うが、/etc/pki/tls/certs/ca-bundle.crt だったりもするみたい。MSYS は「Unixもどき」なので、似せておきたい、とすれば、どちらかだろう。

wget からこれを参照するのにコマンドラインオプション--ca-certificate=...を指定する…、のでは--no-check-certificateを指定する鬱陶しさと大差ない(よりウザい)ので、無条件に読み込む方法を。悔しいよな、http_proxyみたいな「デファクトスタンダードな」環境変数があれば良かったんだけど、そんなもんはなくて、wgetrc に書くしかない。MSYS の wget なら /etc/wgetrc。これに

1 ca_certificate=/etc/ssl/certs/ca-certificates.crt

もしくは

1 ca_certificate=/etc/pki/tls/certs/ca-bundle.crt

のように書き込んでおく。これで wget は https://en.wikipedia.org/wiki/Japan_Airlines のダウンロードで不平を言わなくなる。

wincertstore使って ca-certificates.crt の定期的なアップデーターとか書くと楽かもね。










なお、特定のサイトのサーバー証明書の取得だけなら、標準添付ライブラリだけでも出来ます:

あと、このネタ探ってる過程で見つけたここは、PowerShell で「wget的なことをするための Invoke-WebRequest + SSL」の話。ありがたや…。