web-dev-qa-db-ja.com

PythonでX509証明書の信頼チェーンを検証/検証する方法は?

APIを利用したWebアプリケーションの実装に取り​​組んでいます。応答中に、APIサーバーはX509証明書(署名証明書とルートCA証明書への1つ以上の中間証明書で構成されるPEM形式)へのリンクを送信します。これをダウンロードして、さらに検証するために使用する必要があります。

証明書を使用する前に、チェーン内のすべての証明書を組み合わせて、信頼されたルート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/49845 )を使用しても機能しませんどちらか。

この問題に関する別のスレッド( 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/49845 John Matthewsはそれを行うパッチが書かれていると主張していますが、残念ながらパッチリンクは今は死んでいます-そしてとにかく、パッチがopenssl0.9.8eで動作しなかったことを示すコメントがそのスレッドにあります。

pythonの信頼の証明書チェーンの検証に関連するすべての回答は、デッドパッチにリンクしているか、m2extに戻っているようです。

Pythonで信頼の証明書チェーンを検証するための簡単で簡単な方法はありますか?

10
speznot

Avi Dasの応答は、単一のリーフ証明書を使用して単一のトラストアンカーを検証するという些細なケースでは有効ですが、中間証明書に信頼を置きます。つまり、中間体とクライアント証明書が送信される場合、チェーン全体が信頼されます。

これを行わないでください。pyOpenSSLのテストで見つかったコードに欠陥があります!

このスレッドはPythonのcryptography-devメーリングリスト(この回答にリンクしています)で見つかりました: https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html

このコードでは、root_certと中間体を区別していないことがわかります。ドキュメントを見ると、add_cert自体がtrusted証明書を追加します(おそらくadd_trusted_certの方が適切な名前ですか?)。

これがひどい考えである理由の例が含まれています。これを十分に強調することはできません:中間体を信頼してチェーンを検証することは、チェックをまったく実行しないことと似ています


そうは言っても、Pythonで証明書チェーンをどのようにdo検証しますか?私が見つけた最良の選択肢は https://github.com/wbond/certvalidator で、これでうまくいくようです。

いくつかの欠陥のある選択肢もあります:

これは、いくつかの立派なPython暗号化ライブラリの現在の状態です:

現時点では、両方のスレッドが古くなっているようです。

これが質問のケースと一致しないことはわかっていますが、TLSソケットで証明書検証を使用して何かを構築している場合は、Pythonですでに利用可能なモジュールを使用してください。特に暗号化に関して。暗号は難しいです。それについての唯一の簡単なことはそれを台無しにすることです。

7
ralphje

Pyopensslライブラリを調べたところ、証明書チェーンの検証用にこれが見つかりました。次の例は 彼らのテスト からのものであり、信頼されたルート証明書への信頼のチェーンを検証するという、あなたが望むことをしているようです。 X509Storeおよび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())
2
Avi Das