2015-01-06 20 views
14

Python 2.7.9では、SSL証明書の検証がより厳しくなりました。驚くばかり!Python Urllib2 SSLエラー

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

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

私は「verisign.pem」と呼ばれるファイルにルートCA証明書をダウンロードし、この試してみてください。ルートCAは、4行目で正しく印刷されていても、

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) 

をし、私はまだCERTIFICATE_VERIFY_FAILEDエラーが発生します。

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

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

私はチェーン内の最後の証明書を取り、OpenSSLが読み込めるPEMファイルにそれを置きます。 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 

この検証を有効にする方法はありますか?

+0

どの証明書がverisign.pemにありますか? SHA1の証明書であることを確認してください。A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B 3C:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44(クラス3パブリックプライマリ認証局) :A5:E5'(VeriSignクラス3公開プライマリ認証局 - G5) –

+0

私はついにそれが見つかった、ありがとう! どのルート証明書が使用されているかをどのように知ることができますか?私はopenssl x509コマンドを使ってチェーン内の各certをダンプしています。彼らのほとんどは署名された証明書を言っていますが、どこに最後の証明書が署名されたのかは分かりません。 – abjennings

答えて

26

問題の原因についてのコメントを要約し、より詳細に本当の問題を説明するために:

あなたは、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]はサーバーによって送信されたリーフ証明書です。次のcertifcates [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_サーバーから送信された証明書から信頼チェーンを構築します。これが完了したら、チェーン内の最新の証明書に署名した信頼できるルート証明書があるかどうかを確認します。

この証明書はチェーン[2]の最新の要素に署名しないため、OpenSSLはルート証明書[NT]に対してチェーン[0]、[1]、[2] ]代わりに[1]。サーバーが代わりに[0]、[1]のチェーンを送信しただけであれば、検証は成功します。

long known bugです。patchesが存在し、最終的にOpenSSL 1.0.2でX509_V_FLAG_TRUSTED_FIRSTオプションが導入されていれば問題があることを願っています。

+0

これは優れた答えです。私はこれを正しいものとしてマークしました。ありがとうございました!私の実際の間違いは、証明書[2]を渡してチェーンを検証することを期待していました([0]と[1]も試しました)。私は[2]がルート証明書であると思った。私は[OT]やそれを見つける場所を探すことを知らなかった。指紋を私に教えたら、/usr/local/share/certs/ca-root-nss.crt(FreeBSD)で見つけましたが、FirefoxでもVerisignからダウンロードできました。しかし、どのルート証明書が正しいものなのか、どのように分かりましたか?ブラウザは発行者の文字列でそれらを識別しますか? – abjennings

+0

OpenSSLは、発行者の文字列で検索し、署名が一致するかどうかを確認します。 –

関連する問題