2015-01-06 93 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按发行者字符串进行搜索,然后检查签名是否匹配。 –