2015-01-06 92 views
14

Python 2.7.9現在對SSL證書驗證的要求更爲嚴格。真棒!Python Urllib2 SSL錯誤

我並不感到驚訝的是,之前工作的程序現在正在獲取CERTIFICATE_VERIFY_FAILED錯誤。但我似乎無法讓他們工作(不完全禁用證書驗證)。

一個程序使用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) 

,我仍然得到CERTIFICATE_VERIFY_FAILED錯誤,即使根CA在4號線正確地打印出來。

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 

任何想法如何讓這與啓用驗證工作?

+0

哪個證書在verisign.pem中?確保它是一個帶有A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B的SHA1指紋的證書'(Class 3 Public Primary Certification Authority)而不是'4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44' :A5:E5'(VeriSign Class 3公共主要認證機構 - G5) –

+0

我終於找到了一個,它的工作原理,謝謝! 你怎麼知道哪個根證書被使用?我正在使用openssl x509命令轉儲鏈中的每個證書。他們中的大多數人說他們簽了哪個證書,但是我沒有看到最後一個證書在哪裏說明簽名的是哪個根證書。 – 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]是服務器發送的葉證書。以下認證[1]和[2]是服務器發送的鏈式證書。最後一個證書[OT]是受信任的根證書,不是由服務器發送的,而是位於受信任CA的本地存儲器中。鏈中的每個證書都由下一個證書籤名,最後一個證書[OT]是可信的,因此信任鏈是完整的。

如果您不是瀏覽器(如谷歌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]的鏈,那麼驗證將會成功。

這是一個long known bug並且存在patches,並且希望如果最終在OpenSSL 1.0.2中引入了X509_V_FLAG_TRUSTED_FIRST選項,則會出現問題。

+0

這是一個很好的答案,我已經標記爲正確。謝謝!我的實際錯誤是傳遞了cert [2]並希望驗證鏈(也試過[0]和[1])。我認爲[2]是一個根證書。我不知道尋找[OT]或在哪裏找到它。一旦你告訴我指紋,我在/usr/local/share/certs/ca-root-nss.crt(FreeBSD)中找到它,但我也可以在Firefox中找到它或從Verisign下載它。但你怎麼知道哪個根證書是正確的?瀏覽器是否通過發行者字符串來識別它們? – abjennings

+0

OpenSSL按發行者字符串進行搜索,然後檢查簽名是否匹配。 –