如何在Python中验证/验证X509证书信任链?

时间:2015-06-08 02:03:33

标签: python validation ssl m2crypto

我正在努力实现一个使用API​​的Web应用程序。在响应期间,API服务器通过链接发送X509证书(PEM格式,由签名证书和一个或多个中间证书组成到根CA证书),我必须下载并用于进一步验证。

在使用证书之前,我需要确保链中的所有证书组合在一起,以创建对受信任的根CA证书的信任链(以检测并避免任何恶意请求)。我在python中很难做到这一点,而我对这个主题的研究并没有产生任何有用的东西。

使用请求和M2Crypto轻松获取和加载证书

import requests
from M2Crypto import RSA, X509

mypem = requests.get('https://server.com/my_certificate.pem')   
cert = X509.load_cert_string(str(mypem.text), X509.FORMAT_PEM)

但是,验证证书链是一个问题。我不可能将证书写入磁盘以便通过类似子进程的方式使用命令行实用程序(如openssl),因此必须通过python完成。我也没有任何打开的连接,因此使用基于连接的验证解决方案(如本答案/主题中提到的那样:https://stackoverflow.com/a/1088224/4984533)也不起作用。

关于这个问题的另一个线程(在https://stackoverflow.com/a/4427081)abbot解释说m2crypto无法进行此验证,并说他已经编写了一个允许验证的扩展(使用模块m2ext)但他的补丁永远不会起作用,即使我知道它是有效的,也总是返回false:

from m2ext import SSL
ctx = SSL.Context()
ctx.load_verify_locations(capath='/etc/ssl/certs/') # I have run c_rehash in this directory to generate a list of cert files with signature based names
if not ctx.validate_certificate(cert): # always happens
    print('Invalid certificate!') 

这里有一个类似的帖子https://stackoverflow.com/a/9007764/4984533也有这个答案,其中约翰马修斯声称有一个补丁写的会做,但不幸的是补丁链接现在已经死了 - 无论如何有一个评论线程说明补丁不适用于openssl 0.9.8e。

与在python中验证证书信任链相关的所有答案似乎都链接到死亡补丁或返回m2ext

是否有一种简单,直接的方法来验证我在Python中的证书信任链?

2 个答案:

答案 0 :(得分:5)

虽然Avi Das的响应对于使用单个叶证书验证单个信任锚的简单情况有效,但将信任置于中间证书。这意味着在发送中间件的情况下,以及客户端证书,整个链都是可信的。

不要这样做。 pyOpenSSL测试中的代码存在缺陷!

我在Python的cryptography-dev邮件列表中找到了这个帖子(链接回到这个答案):https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html

  

我们看到这段代码没有区分root_cert和   中间。如果我们查看文档,add_cert本身会添加一个   可信证书(也许add_trusted_cert会是一个更好的名字?)。

它包括为什么这是一个可怕的想法的例子。我不能强调这一点:通过信任中间体验证你的链类似于根本不执行任何检查

话虽如此,你如何验证Python中的证书链?我找到的最好的选择是https://github.com/wbond/certvalidator,它似乎可以完成这项工作。

还有一些有缺陷的选择:

这是一些受人尊敬的Python加密库的当前状态:

这两个线程此时似乎都是陈旧的。

我知道这与问题的情况不符,但是:如果你在TLS套接字中使用证书验证构建一些东西,只需使用Python中已有的模块。永远不要重新发明轮子,特别是关于密码学。加密很难;关于它的唯一容易的事就是弄乱它。

答案 1 :(得分:2)

我查看了pyopenssl库并发现了这个用于证书链验证。以下示例来自their tests,似乎可以执行您想要的操作,即验证对受信任根证书的信任链。以下是X509Store and X509StoreContext

的相关文档
from OpenSSL.crypto import load_certificate, load_privatekey
from OpenSSL.crypto import X509Store, X509StoreContext
from six import u, b, binary_type, PY3
root_cert_pem = b("""-----BEGIN CERTIFICATE-----
MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
-----END CERTIFICATE-----
""")
intermediate_cert_pem = b("""-----BEGIN CERTIFICATE-----
MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw
WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw
DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw
ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT
MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmK
FGIbljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT
21H2qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwID
AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAPIWSkLX
QRMApOjjyC+tMxumT5e2pMqChHmxobQK4NMdrf2VCx+cRT6EmY8sK3/Xl/X8UBQ+
9n5zXb1ZwhW/sTWgUvmOceJ4/XVs9FkdWOOn1J0XBch9ZIiFe/s5ASIgG7fUdcUF
9mAWS6FK2ca3xIh5kIupCXOFa0dPvlw/YUFT
-----END CERTIFICATE-----
""")
intermediate_server_cert_pem = b("""-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw
ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV
BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
biBEaWVnbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqpJZygd+w1faLOr1
iOAmbBhx5SZWcTCZ/ZjHQTJM7GuPT624QkqsixFghRKdDROwpwnAP7gMRukLqiy4
+kRuGT5OfyGggL95i2xqA+zehjj08lSTlvGHpePJgCyTavIy5+Ljsj4DKnKyuhxm
biXTRrH83NDgixVkObTEmh/OVK0CAwEAATANBgkqhkiG9w0BAQ0FAAOBgQBa0Npw
UkzjaYEo1OUE1sTI6Mm4riTIHMak4/nswKh9hYup//WVOlr/RBSBtZ7Q/BwbjobN
3bfAtV7eSAqBsfxYXyof7G1ALANQERkq3+oyLP1iVt08W1WOUlIMPhdCF/QuCwy6
x9MJLhUCGLJPM+O2rAPWVD9wCmvq10ALsiH3yA==
-----END CERTIFICATE-----
""")

root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
intermediate_server_cert = load_certificate(FILETYPE_PEM, intermediate_server_cert_pem)
store = X509Store()
store.add_cert(root_cert)
store.add_cert(intermediate_cert)
store_ctx = X509StoreContext(store, intermediate_server_cert)
print(store_ctx.verify_certificate())