Python Urllib2 SSL错误

abj*_*ngs 18 python ssl urllib2

Python 2.7.9现在对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)
Run Code Online (Sandbox Code Playgroud)

即使在第4行正确打印出根CA,我仍然会收到CERTIFICATE_VERIFY_FAILED错误.

openssl可以很好地连接到这台服务器.实际上,这是我用来获取CA证书的命令:

openssl s_client -showcerts -connect bucket.s3.amazonaws.com:443 < /dev/null
Run Code Online (Sandbox Code Playgroud)

我拿了链中的最后一个证书并把它放在一个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
Run Code Online (Sandbox Code Playgroud)

任何想法如何启用验证?

Ste*_*ich 32

总结有关问题原因的评论,并更详细地解释实际问题:

如果检查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
Run Code Online (Sandbox Code Playgroud)

第一个证书[0]是服务器发送的叶证书.以下证书[1]和[2]是服务器发送的链证书.最后一个证书[OT]是受信任的根证书,它不是由服务器发送的,而是在受信任CA的本地存储中.链中的每个证书都由下一个证书签名,最后一个证书[OT]是可信的,因此信任链已完成.

如果您通过浏览器(例如使用NSS库的Google Chrome)检查信任链,则会获得以下链:

 [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
Run Code Online (Sandbox Code Playgroud)

此处[0]和[1]再次由服务器发送,但[NT]是受信任的根证书.虽然这看起来与链条证书[2]完全相同,但指纹表明证书不同.如果您仔细查看证书[2]和[NT],您会看到证书中的公钥是相同的,因此[2]和[NT]都可用于验证[[]和[NT]的签名.因此可以用来构建信任链.

这意味着,虽然服务器在所有情况下都发送相同的证书链,但有多种方法可以验证链到受信任的根证书.如何完成取决于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)
Run Code Online (Sandbox Code Playgroud)

但问题仍然存在,为什么你的验证不成功.您所做的是将浏览器使用的受信任的根证书(Verisign G5 4E:B6:D5:78:49)与OpenSSL一起使用.但浏览器(NSS)和OpenSSL中的验证工作略有不同:

  • NSS:从服务器发送的证书构建信任链.当我们获得由任何本地受信任的根证书签名的证书时,停止构建链.
  • OpenSSL_从服务器发送的证书构建信任链.完成此操作后,检查我们是否拥有签署链中最新证书的受信任根证书.

由于这种细微差别,OpenSSL无法验证链[0],[1],[2]对根证书[NT],因为此证书不签署链[2]中的最新元素,而是[1] .如果服务器只发送一个[0],[1]链,那么验证就会成功.

这是一个众所周知的bug,并且存在补丁,如果最终在OpenSSL 1.0.2中通过引入该X509_V_FLAG_TRUSTED_FIRST选项进行解决,则希望存在这个问题.