Python Urllib2 SSL 错误
Python Urllib2 SSL error
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)
我仍然得到 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
关于如何在启用验证的情况下让它工作的任何想法?
总结有关问题原因的评论并更详细地解释真正的问题:
如果您检查 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],您会发现证书中的 public 密钥是相同的,因此 [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 无法根据根证书 [NT] 验证链 [0]、[1]、[2],因为该证书未签署链 [2] 中的最新元素而是 [1]。如果服务器只发送 [0]、[1] 链,则验证会成功。
这是一个 long known bug and there exist patches,如果最终在 OpenSSL 1.0.2 中通过引入 X509_V_FLAG_TRUSTED_FIRST
选项解决了这个问题。
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)
我仍然得到 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
关于如何在启用验证的情况下让它工作的任何想法?
总结有关问题原因的评论并更详细地解释真正的问题:
如果您检查 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],您会发现证书中的 public 密钥是相同的,因此 [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 无法根据根证书 [NT] 验证链 [0]、[1]、[2],因为该证书未签署链 [2] 中的最新元素而是 [1]。如果服务器只发送 [0]、[1] 链,则验证会成功。
这是一个 long known bug and there exist patches,如果最终在 OpenSSL 1.0.2 中通过引入 X509_V_FLAG_TRUSTED_FIRST
选项解决了这个问题。