web-dev-qa-db-ja.com

Python Urllib2 SSLエラー

Python 2.7.9は、SSL証明書の検証に関してより厳密になりました。驚くばかり!

以前に動作していたプログラムにCERTIFICATE_VERIFY_FAILEDエラーが表示されるようになったことは驚くことではありません。しかし、証明書の検証を完全に無効にすることなく、それらを機能させることはできません。

1つのプログラムは、urllib2を使用してhttps経由でAmazon S3に接続していました。

ルートCA証明書を「verisign.pem」というファイルにダウンロードして、これを試してみます。

import urllib2, ssl
context = ssl.create_default_context()
context.load_verify_locations(cafile = "./verisign.pem")
print context.get_ca_certs()
urllib2.urlopen("https://bucket.s3.amazonaws.com/", context=context)

ルートCAが4行目で正しく印刷されていても、CERTIFICATE_VERIFY_FAILEDエラーが表示されます。

opensslはこのサーバーに正常に接続できます。実際、CA証明書を取得するために使用したコマンドは次のとおりです。

openssl s_client -showcerts -connect bucket.s3.amazonaws.com:443 < /dev/null

チェーン内の最後の証明書を取得し、それをPEMファイルに入れました。opensslで問題なく読み取れます。これは、次のものを含むVerisign証明書です。

Serial number: 35:97:31:87:f3:87:3a:07:32:7e:ce:58:0c:9b:7e:da
Subject key identifier: 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
SHA1 fingerprint: F4:A8:0A:0C:D1:E6:CF:19:0B:8C:BC:6F:BC:99:17:11:D4:82:C9:D0

検証を有効にしてこれを機能させる方法はありますか?

18
abjennings

問題の原因に関するコメントを要約し、実際の問題をより詳細に説明するには:

OpenSSLクライアントの信頼チェーンを確認すると、次の結果が得られます。

 [0] 54:7D:B3:AC:BF:... /CN=*.s3.amazonaws.com 
 [1] 5D:EB:8F:33:9E:... /CN=VeriSign Class 3 Secure Server CA - G3
 [2] F4:A8:0A:0C:D1:... /CN=VeriSign Class 3 Public Primary Certification Authority - G5
[OT] A1:DB:63:93:91:... /C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority

最初の証明書[0]は、サーバーによって送信されたリーフ証明書です。次の証明書[1]および[2]は、サーバーによって送信されるチェーン証明書です。最後の証明書[OT]は信頼されたルート証明書であり、サーバーによって送信されませんが、信頼されたCAのローカルストレージにあります。チェーン内の各証明書は次の証明書によって署名され、最後の証明書[OT]が信頼されているため、信頼チェーンは完了しています。

ブラウザで代わりにトラストチェーンを確認すると(例:Google Chrome NSSライブラリを使用))、次のチェーンを取得します。

 [0] 54:7D:B3:AC:BF:... /CN=*.s3.amazonaws.com 
 [1] 5D:EB:8F:33:9E:... /CN=VeriSign Class 3 Secure Server CA - G3
[NT] 4E:B6:D5:78:49:... /CN=VeriSign Class 3 Public Primary Certification Authority - G5

ここでは、[0]と[1]がサーバーによって再度送信されますが、[NT]は信頼されたルート証明書です。これはサブジェクトからはチェーン証明書とまったく同じように見えますが[2]、指紋は証明書が異なると言います。証明書[2]と[NT]を詳しく見ると、証明書内の公開鍵が同じであるため、[2]と[NT]の両方を使用して[ 1]したがって、信頼チェーンを構築するために使用できます。

つまり、サーバーはすべての場合に同じ証明書チェーンを送信しますが、信頼されたルート証明書までチェーンを検証する方法は複数あります。これを行う方法は、SSLライブラリと既知の信頼されたルート証明書に依存します。

                          [0] (*.s3.amazonaws.com)
                           |
                          [1] (Verisign G3) --------------------------\
                           |                                          |
      /------------------ [2] (Verisign G5 F4:A8:0A:0C:D1...)         |
      |                                                               |
      |              certificates sent by server                      |
 .....|...............................................................|................
      |              locally trusted root certificates                |
      |                                                               |
     [OT] Public Primary Certification Authority        [NT] Verisign G5 4E:B6:D5:78:49
     OpenSSL library                                    Google Chrome (NSS library)

ただし、検証が失敗した理由は疑問のままです。あなたがしたことは、ブラウザ(Verisign G5 4E:B6:D5:78:49)で使用される信頼されたルート証明書をOpenSSLと一緒に取得することでした。ただし、ブラウザ(NSS)とOpenSSLでの検証はわずかに異なります。

  • NSS:サーバーから送信された証明書から信頼チェーンを構築します。ローカルで信頼されたルート証明書のいずれかによって署名された証明書を取得したら、チェーンの構築を停止します。
  • OpenSSL_は、サーバーから送信された証明書から信頼チェーンを構築します。これが完了したら、チェーン内の最新の証明書に署名する信頼できるルート証明書があるかどうかを確認します。

この微妙な違いにより、OpenSSLはチェーン[0]、[1]、[2]をルート証明書[NT]に対して検証できません。この証明書はチェーン[2]の最新の要素に署名せず、代わりに[1] 。サーバーが[0]、[1]のチェーンのみを送信する場合、検証は成功します。

これは 長い既知のバグ であり、 パッチ が存在します。OpenSSL1.0.2でX509_V_FLAG_TRUSTED_FIRSTオプション。

32
Steffen Ullrich