2015-06-08 151 views
7

我正在實施一個使用API​​的Web應用程序。在響應期間,API服務器通過鏈接發送X509證書(PEM格式,由簽名證書和一個或多箇中間證書組成的根證書),我必須下載該證書並使用該證書進行進一步驗證。如何在Python中驗證/驗證X509證書信任鏈?

在使用證書之前,我需要確保鏈中的所有證書都組合起來,爲受信任的根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)住持解釋說m2crypto是不能這樣做的驗證,而另一個線程說,他寫的擴展,允許驗證(使用該模塊m2ext),但他的補丁似乎永遠不會工作,總是返回假的,即使我知道它是有效的:

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中?

+0

順便說一句pyopenssl包裝OpenSSL的,所以你就不需要在命令行中使用它的相關文檔。如果你還沒有,也值得一讀https://www.python.org/dev/peps/pep-0476/ –

+0

感謝鏈接AVI,我以前沒有見過。然而,它並不能幫助我解決當前的困境(特別是關於http客戶)。如果有必要,我沒有使用命令行程序進行驗證的問題 - 但如果我需要在每次請求時將pem文件寫入磁盤,則不會有問題。如果openssl verify命令可以使用原始字符串,那麼我可以使用它(儘管它看起來像是一種我認爲在Python中微不足道的冒險解決方法)。 – speznot

回答

4

我看着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()) 
+0

所以,我無法得到這個例子工作 - 我錯過了什麼? verify_cert調用總是返回'None'(對於您的示例中提供的證書以及我測試過的我自己的證書)。我必須在頂部添加'FILETYPE_PEM'導入以及從OpenSSL.crypto導入的其他導入。我也嘗試在證書字符串中刪除多餘的換行符(在「END CERTIFICATE」行之後),但它仍然沒有返回。非常感謝您對此的看法和時間! – speznot

+0

啊!我錯過了直接在該文檔中直接面對我的評論,說沒有一個有效的迴應 - 我的道歉。我一直在嘗試使用自己的證書進行測試,並且「無法獲取[本地]頒發者證書」錯誤。在命令行上,我使用類似這樣的方式來驗證成功:'openssl verify -untrusted intermediate_cert.pem -CAfile rootcert.pem tovalidate.pem'(沒有-untrusted開關,它失敗時出現類似的錯誤,我看到了) - 是它在你的例子中''''intermediate_server_cert'是否是我正在驗證的證書? – speznot

+0

正確,intermediate_server_cert是在此示例中獲得驗證的證書。我認爲這個錯誤通常意味着特定的證書在鏈中的某處丟失。這很奇怪,但我嘗試過一個不同的例子,它確實爲我解決了問題。 –