Python が 参照する証明書ストアを掘ってみた (Ubuntu 版)
Python で作ったプログラムが、あるサイトをのぞきに行けなったのだが、証明書ストアに中間証明書がなかったことが原因だった。という話の、まとめ。
結論としては、Ubuntuの証明書ストア管理には、ca-certificates という仕組みが用意されているので、それを使って、足りない中間証明書を追加してやった。という内容になります。
こんな環境に対して、つらつら書きます。
起こったこと
Python
Python で作ったプログラムが、ある時からサイトに接続できなくなった。エラーを見ると、次の内容で、SSL関連。どうやら、新たに追加されたサブドメインで、これまでとは異なる証明書が設定されているらしい。
SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:720)'),)
curl
適切かどうか(最終的には、半分適切だったが)わかんないけど、curl でも見に行ってみると、こんなエラー。
curl: (60) Peer certificate cannot be authenticated with known CA certificates More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
curl のバージョン。 https に対応していて OpenSSL が使われてる模様:
$ curl -V curl 7.35.0 (x86_64-pc-linux-gnu) libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smtp smtps telnet tftp Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP
openSSL の バージョン:
$ openssl version OpenSSL 1.0.1f 6 Jan 2014
調査
OpenSSL で確認
OpenSSLを使うと、該当サイトの証明書の詳細が確認できるので、やってみた。
$ openssl s_client -connect <DOMAIN>:443 -showcerts もしくは、 $ openssl s_client -port 443 -showcerts -host <DOMAIN>
まずは、該当サイト:
$ openssl s_client -connect kilasdaerah.kompas.com:443 -showcerts CONNECTED(00000003) depth=0 CN = *.kompas.com verify error:num=20:unable to get local issuer certificate verify return:1 depth=0 CN = *.kompas.com verify error:num=27:certificate not trusted verify return:1 depth=0 CN = *.kompas.com verify error:num=21:unable to verify the first certificate verify return:1 ... -----END CERTIFICATE----- --- Server certificate subject=/CN=*.kompas.com issuer=/C=US/O=thawte, Inc./OU=Domain Validated SSL/CN=thawte DV SSL CA - G2 ...
正常につながるサブドメイン側:
CONNECTED(00000003) --- Certificate chain 0 s:/CN=*.kompas.com i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=Thawte RSA CA 2018 -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=Thawte RSA CA 2018 i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- --- Server certificate subject=/CN=*.kompas.com issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=Thawte RSA CA 2018 ...
開発MacOSのブラウザで確認
該当サイトも、正常につながるサブドメインも、どちらも閲覧が可能。
もちろん、送ってくる証明書と、ルート証明書までのチェーンは異なる。
ただ、どちらも3階層の証明書であることは共通。あれ? OpenSSLのログからだと、該当サイトは証明書本体のみの送信で、正常につながるサブドメインについては、証明書+中間証明書の組み合わせが送信されているもよう。
わかったこと
この段階でわかったことは、4つ:
選択肢
- insecure として扱う -> 公開サイト見に行くのに、それはちょっとなぁ。本当に InSecure のサイトとの区別つけづらくなる
- 正常なサイトとして扱いたい -> 該当サイトの証明書を、信頼するルート証明書からチェーンできるようにすればいい
後者として扱うための方策を、各種実施していきました。
対応
Ubuntu の証明書ストア
Ubuntu の 証明書ってどこで管理されているのだろうか?いろいろググってたら発見。
/etc/ssl
誰が入れたのかを調べてみると、opensslっぽい。
$ apt-file search "/etc/ssl" dkimproxy: /etc/ssl/private/dkimproxy.key fsvs: /usr/share/doc/fsvs/examples/debian/etc/ssl/servers openssl: /etc/ssl/openssl.cnf python-neutron: /usr/lib/python2.7/dist-packages/neutron/tests/unit/bigswitch/etc/ssl/ca_certs/README python-neutron: /usr/lib/python2.7/dist-packages/neutron/tests/unit/bigswitch/etc/ssl/combined/README python-neutron: /usr/lib/python2.7/dist-packages/neutron/tests/unit/bigswitch/etc/ssl/host_certs/README python-ubuntu-sso-client: /etc/ssl/certs/UbuntuOne-Go_Daddy_CA.pem python-ubuntu-sso-client: /etc/ssl/certs/UbuntuOne-Go_Daddy_Class_2_CA.pem python-ubuntu-sso-client: /etc/ssl/certs/UbuntuOne-ValiCert_Class_2_VA.pem uoa-integration-tests: /etc/ssl/certs/uoa-test-server.pem
でも、どうやら上記のフォルダを直接触るのではなく、 ca-certificates という仕組みの乗っ取るらしい。
独自のルートCA証明書を追加する方法(Ubuntu, CentOS 7) | cloudpack.media
どれどれ。確かに、山盛り crt ファイルを /usr/share/ca-certificates/ にインストールした上で /etc/ssl/certs も気にしてる。
$ dpkg -L ca-certificates ... /usr/share/ca-certificates/mozilla/Security_Communication_EV_RootCA1.crt /usr/share/ca-certificates/mozilla/COMODO_Certification_Authority.crt ... /usr/sbin /usr/sbin/update-ca-certificates /etc /etc/ca-certificates /etc/ca-certificates/update.d /etc/ssl /etc/ssl/certs
$ apt-cache depends ca-certificates ca-certificates 依存: openssl |依存: debconf 依存: <debconf-2.0> cdebconf debconf 破壊: ca-certificates-java 破壊: <ca-certificates-java:i386> 拡張: openssl
Ubuntu の証明書ストア に 必要な中間証明書をインストール
今回必要だった中間証明書は、公式からダウンロード
- https://search.thawte.com/support/ssl-digital-certificates/index?page=content&id=INFO1384#links
- https://search.thawte.com/support/ssl-digital-certificates/index?page=content&id=INFO4671#links
それを thawte_SHA2SHA1_intermediateSSL123.crt として保存。次のコマンドで、証明書ストアに保管した。
$ sudo cp thawte_SHA2SHA1_intermediateSSL123.crt /usr/share/ca-certificates /etc/ca-certificates.confに、/usr/share/ce-certificetsからの相対パスで、ファイル名を追記する。 $ sudo vim /etc/ca-certificats.conf update-ca-certificetsを実行する。 $ sudo update-ca-certificates 無事 pem ファイルがストアに準備された。 $ ls /etc/ssl/certs/ | grep thawte thawte_Primary_Root_CA.pem thawte_Primary_Root_CA_-_G2.pem thawte_Primary_Root_CA_-_G3.pem thawte_SHA2SHA1_intermediateSSL123.cer.pem
その他
requests ライブラリを使っている場合には、こちらを参照して certifi ライブラリをチェックするといいかもしれません
MacOSの証明書ストアは、「キーチェーンアクセス」で確認できます。