如何使用 python 检索远程主机的 TLS/SSL 对等证书?

发布于 2024-12-08 14:11:54 字数 467 浏览 2 评论 0原文

我需要扫描 IP 列表并从该 IP 上的证书检索通用名称(对于允许端口 443 连接的每个 IP)。我已经能够使用套接字和 ssl 模块成功地完成此操作。它适用于所有具有有效签名证书的 IP,但不适用于自签名证书。

如果我使用此方法,它需要一个由我的 CA 捆绑包验证的有效证书:

from socket import socket
import ssl

s = socket()
c = ssl.wrap_socket(s,cert_reqs=ssl.CERT_REQUIRED, ca_certs='ca-bundle.crt')
c.connect(('127.0.0.1', 443))

print c.getpeercert()

如果我删除 cert_reqs=ssl.CERT_REQUIRED ,则它会连接,但根本无法获取证书。

如何检索 IP 上证书的通用名称,无论它是否针对 ca 捆绑包进行验证?

I need to scan through a list of IPs and retrieve the common name from the certificate on that IP (for every IP that allows port 443 connections). I have been able to successfully do this using the sockets and ssl modules. It works for all IPs with valid, signed certificates but it isn't working for self-signed certificates.

If I use this method, it requires a valid cert that is verified by my CA-bundle:

from socket import socket
import ssl

s = socket()
c = ssl.wrap_socket(s,cert_reqs=ssl.CERT_REQUIRED, ca_certs='ca-bundle.crt')
c.connect(('127.0.0.1', 443))

print c.getpeercert()

If I remove the cert_reqs=ssl.CERT_REQUIRED then it connects but doesn't get the certificate at all.

How can I retrieve the common name for a certificate on an IP whether it validates against the ca-bundle or not?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雨落星ぅ辰 2024-12-15 14:11:54

python ssl 库似乎只会在具有有效签名的情况下为您解析证书。

    """Returns a formatted version of the data in the
    certificate provided by the other end of the SSL channel.
    Return None if no certificate was provided, {} if a
    certificate was provided, but not validated."""

您仍然可以使用 ssl.get_server_certificate() 函数获取服务器证书,但它以 PEM 格式返回。 (或者,您可以调用 c.getpeercert(True),它以二进制 DER 格式返回证书,无论是否经过验证。)

>>> print ssl.get_server_certificate(('server.test.com', 443))
-----BEGIN CERTIFICATE-----
MIID4zCCAsugAwIBA.....

从这里,我将使用 M2Crypto 或 OpenSSL 来读取证书并获取值:

# M2Crypto
cert = ssl.get_server_certificate(('www.google.com', 443))
x509 = M2Crypto.X509.load_cert_string(cert)
x509.get_subject().as_text()
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'

# OpenSSL
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
x509.get_subject().get_components()
#[('C', 'US'),
# ('ST', 'California'),
# ('L', 'Mountain View'),
# ('O', 'Google Inc'),
# ('CN', 'www.google.com')]

The python ssl library seems like it only parses out the cert for you if it has a valid signature.

    """Returns a formatted version of the data in the
    certificate provided by the other end of the SSL channel.
    Return None if no certificate was provided, {} if a
    certificate was provided, but not validated."""

You can still get the server certificate with the ssl.get_server_certificate() function, but it returns it in PEM format. (Alternatively, you could call c.getpeercert(True), which returns the cert in binary DER format, whether it's validated or not.)

>>> print ssl.get_server_certificate(('server.test.com', 443))
-----BEGIN CERTIFICATE-----
MIID4zCCAsugAwIBA.....

From here, I would use M2Crypto or OpenSSL to read the cert and get values:

# M2Crypto
cert = ssl.get_server_certificate(('www.google.com', 443))
x509 = M2Crypto.X509.load_cert_string(cert)
x509.get_subject().as_text()
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'

# OpenSSL
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
x509.get_subject().get_components()
#[('C', 'US'),
# ('ST', 'California'),
# ('L', 'Mountain View'),
# ('O', 'Google Inc'),
# ('CN', 'www.google.com')]
狼性发作 2024-12-15 14:11:54

使用 SNI 支持检索和解析、解析日期并显示扩展数据(如 subjectAltName):

import ssl
import socket
import OpenSSL
from pprint import pprint
from datetime import datetime


def get_certificate(host, port=443, timeout=10):
    context = ssl.create_default_context()
    conn = socket.create_connection((host, port))
    sock = context.wrap_socket(conn, server_hostname=host)
    sock.settimeout(timeout)
    try:
        der_cert = sock.getpeercert(True)
    finally:
        sock.close()
    return ssl.DER_cert_to_PEM_cert(der_cert)


certificate = get_certificate('example.com')
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, certificate)

result = {
    'subject': dict(x509.get_subject().get_components()),
    'issuer': dict(x509.get_issuer().get_components()),
    'serialNumber': x509.get_serial_number(),
    'version': x509.get_version(),
    'notBefore': datetime.strptime(x509.get_notBefore(), '%Y%m%d%H%M%SZ'),
    'notAfter': datetime.strptime(x509.get_notAfter(), '%Y%m%d%H%M%SZ'),
}

extensions = (x509.get_extension(i) for i in range(x509.get_extension_count()))
extension_data = {e.get_short_name(): str(e) for e in extensions}
result.update(extension_data)
pprint(result)

输出:

{'authorityInfoAccess': 'OCSP - URI:http://ocsp.digicert.com\nCA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt\n',
 'authorityKeyIdentifier': 'keyid:0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2\n',
 'basicConstraints': 'CA:FALSE',
 'certificatePolicies': 'Policy: 2.16.840.1.114412.1.1\n  CPS: https://www.digicert.com/CPS\nPolicy: 2.23.140.1.2.2\n',
 'crlDistributionPoints': '\nFull Name:\n  URI:http://crl3.digicert.com/ssca-sha2-g6.crl\n\nFull Name:\n  URI:http://crl4.digicert.com/ssca-sha2-g6.crl\n',
 'ct_precert_scts': 'Signed Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : A4:B9:09:90:B4:18:58:14:87:BB:13:A2:CC:67:70:0A:\n                3C:35:98:04:F9:1B:DF:B8:E3:77:CD:0E:C8:0D:DC:10\n    Timestamp : Nov 28 21:20:12.614 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:46:02:21:00:84:64:81:B7:21:1D:FA:1A:48:F5:76:\n                AE:4B:E8:46:86:57:27:17:B0:7B:E9:3B:B7:4A:57:42:\n                6C:A2:84:C4:6C:02:21:00:BB:93:B5:FE:30:C4:64:E4:\n                16:4C:7C:6E:58:53:57:EE:EC:7F:AA:45:4F:BF:0E:46:\n                8E:FE:70:FD:FD:8E:42:42\nSigned Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : 87:75:BF:E7:59:7C:F8:8C:43:99:5F:BD:F3:6E:FF:56:\n                8D:47:56:36:FF:4A:B5:60:C1:B4:EA:FF:5E:A0:83:0F\n    Timestamp : Nov 28 21:20:12.821 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:45:02:20:6F:AA:77:D2:1C:A7:94:C0:63:2D:2E:B3:\n                86:DD:41:8B:40:8A:1A:2F:7F:AE:66:C1:93:5F:73:1F:\n                48:93:50:11:02:21:00:D2:F9:9D:48:86:05:1E:A0:97:\n                44:25:0B:3C:EA:CE:FA:2B:19:7C:81:FF:27:7B:9E:DB:\n                58:B6:DC:E8:F0:4A:4E\nSigned Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : 6F:53:76:AC:31:F0:31:19:D8:99:00:A4:51:15:FF:77:\n                15:1C:11:D9:02:C1:00:29:06:8D:B2:08:9A:37:D9:13\n    Timestamp : Nov 28 21:20:12.956 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:45:02:21:00:E4:79:FB:43:84:8E:CA:A1:E4:4F:E9:\n                03:B0:7A:BB:92:EE:F3:44:3B:8C:EC:FE:14:0D:7D:9F:\n                B7:63:29:9F:2D:02:20:4D:77:5A:DC:49:01:4A:F4:68:\n                04:85:61:9F:D7:8D:20:0C:31:FA:C1:D3:F4:71:0A:5B:\n                D6:56:CB:3D:2C:72:8C',
 'extendedKeyUsage': 'TLS Web Server Authentication, TLS Web Client Authentication',
 'issuer': {'C': 'US',
            'CN': 'DigiCert SHA2 Secure Server CA',
            'O': 'DigiCert Inc'},
 'keyUsage': 'Digital Signature, Key Encipherment',
 'notAfter': datetime.datetime(2020, 12, 2, 12, 0),
 'notBefore': datetime.datetime(2018, 11, 28, 0, 0),
 'serialNumber': 21020869104500376438182461249190639870L,
 'subject': {'C': 'US',
             'CN': 'www.example.org',
             'L': 'Los Angeles',
             'O': 'Internet Corporation for Assigned Names and Numbers',
             'OU': 'Technology',
             'ST': 'California'},
 'subjectAltName': 'DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net',
 'subjectKeyIdentifier': '66:98:62:02:E0:09:91:A7:D9:E3:36:FB:76:C6:B0:BF:A1:6D:A7:BE',
 'version': 2}

Retrieving and parsing with SNI support, parsing dates and showing extension data (like subjectAltName):

import ssl
import socket
import OpenSSL
from pprint import pprint
from datetime import datetime


def get_certificate(host, port=443, timeout=10):
    context = ssl.create_default_context()
    conn = socket.create_connection((host, port))
    sock = context.wrap_socket(conn, server_hostname=host)
    sock.settimeout(timeout)
    try:
        der_cert = sock.getpeercert(True)
    finally:
        sock.close()
    return ssl.DER_cert_to_PEM_cert(der_cert)


certificate = get_certificate('example.com')
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, certificate)

result = {
    'subject': dict(x509.get_subject().get_components()),
    'issuer': dict(x509.get_issuer().get_components()),
    'serialNumber': x509.get_serial_number(),
    'version': x509.get_version(),
    'notBefore': datetime.strptime(x509.get_notBefore(), '%Y%m%d%H%M%SZ'),
    'notAfter': datetime.strptime(x509.get_notAfter(), '%Y%m%d%H%M%SZ'),
}

extensions = (x509.get_extension(i) for i in range(x509.get_extension_count()))
extension_data = {e.get_short_name(): str(e) for e in extensions}
result.update(extension_data)
pprint(result)

Output:

{'authorityInfoAccess': 'OCSP - URI:http://ocsp.digicert.com\nCA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt\n',
 'authorityKeyIdentifier': 'keyid:0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2\n',
 'basicConstraints': 'CA:FALSE',
 'certificatePolicies': 'Policy: 2.16.840.1.114412.1.1\n  CPS: https://www.digicert.com/CPS\nPolicy: 2.23.140.1.2.2\n',
 'crlDistributionPoints': '\nFull Name:\n  URI:http://crl3.digicert.com/ssca-sha2-g6.crl\n\nFull Name:\n  URI:http://crl4.digicert.com/ssca-sha2-g6.crl\n',
 'ct_precert_scts': 'Signed Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : A4:B9:09:90:B4:18:58:14:87:BB:13:A2:CC:67:70:0A:\n                3C:35:98:04:F9:1B:DF:B8:E3:77:CD:0E:C8:0D:DC:10\n    Timestamp : Nov 28 21:20:12.614 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:46:02:21:00:84:64:81:B7:21:1D:FA:1A:48:F5:76:\n                AE:4B:E8:46:86:57:27:17:B0:7B:E9:3B:B7:4A:57:42:\n                6C:A2:84:C4:6C:02:21:00:BB:93:B5:FE:30:C4:64:E4:\n                16:4C:7C:6E:58:53:57:EE:EC:7F:AA:45:4F:BF:0E:46:\n                8E:FE:70:FD:FD:8E:42:42\nSigned Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : 87:75:BF:E7:59:7C:F8:8C:43:99:5F:BD:F3:6E:FF:56:\n                8D:47:56:36:FF:4A:B5:60:C1:B4:EA:FF:5E:A0:83:0F\n    Timestamp : Nov 28 21:20:12.821 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:45:02:20:6F:AA:77:D2:1C:A7:94:C0:63:2D:2E:B3:\n                86:DD:41:8B:40:8A:1A:2F:7F:AE:66:C1:93:5F:73:1F:\n                48:93:50:11:02:21:00:D2:F9:9D:48:86:05:1E:A0:97:\n                44:25:0B:3C:EA:CE:FA:2B:19:7C:81:FF:27:7B:9E:DB:\n                58:B6:DC:E8:F0:4A:4E\nSigned Certificate Timestamp:\n    Version   : v1 (0x0)\n    Log ID    : 6F:53:76:AC:31:F0:31:19:D8:99:00:A4:51:15:FF:77:\n                15:1C:11:D9:02:C1:00:29:06:8D:B2:08:9A:37:D9:13\n    Timestamp : Nov 28 21:20:12.956 2018 GMT\n    Extensions: none\n    Signature : ecdsa-with-SHA256\n                30:45:02:21:00:E4:79:FB:43:84:8E:CA:A1:E4:4F:E9:\n                03:B0:7A:BB:92:EE:F3:44:3B:8C:EC:FE:14:0D:7D:9F:\n                B7:63:29:9F:2D:02:20:4D:77:5A:DC:49:01:4A:F4:68:\n                04:85:61:9F:D7:8D:20:0C:31:FA:C1:D3:F4:71:0A:5B:\n                D6:56:CB:3D:2C:72:8C',
 'extendedKeyUsage': 'TLS Web Server Authentication, TLS Web Client Authentication',
 'issuer': {'C': 'US',
            'CN': 'DigiCert SHA2 Secure Server CA',
            'O': 'DigiCert Inc'},
 'keyUsage': 'Digital Signature, Key Encipherment',
 'notAfter': datetime.datetime(2020, 12, 2, 12, 0),
 'notBefore': datetime.datetime(2018, 11, 28, 0, 0),
 'serialNumber': 21020869104500376438182461249190639870L,
 'subject': {'C': 'US',
             'CN': 'www.example.org',
             'L': 'Los Angeles',
             'O': 'Internet Corporation for Assigned Names and Numbers',
             'OU': 'Technology',
             'ST': 'California'},
 'subjectAltName': 'DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net',
 'subjectKeyIdentifier': '66:98:62:02:E0:09:91:A7:D9:E3:36:FB:76:C6:B0:BF:A1:6D:A7:BE',
 'version': 2}
薆情海 2024-12-15 14:11:54

在 Mac 上,您需要安装 swig 和 M2Crypto

在终端上运行:

brew install swig

然后:

sudo pip install m2crypto

然后您可以运行上面的代码:

from socket import socket
import ssl
import M2Crypto
import OpenSSL

# M2Crypto
cert = ssl.get_server_certificate(('www.google.com', 443))
x509 = M2Crypto.X509.load_cert_string(cert)
print x509.get_subject().as_text()
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'

# OpenSSL
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
print x509.get_subject().get_components()
#[('C', 'US'),
# ('ST', 'California'),
# ('L', 'Mountain View'),
# ('O', 'Google Inc'),
# ('CN', 'www.google.com')]

On Mac you need to install swig and M2Crypto

On terminal run:

brew install swig

And then:

sudo pip install m2crypto

Then you can run the code above:

from socket import socket
import ssl
import M2Crypto
import OpenSSL

# M2Crypto
cert = ssl.get_server_certificate(('www.google.com', 443))
x509 = M2Crypto.X509.load_cert_string(cert)
print x509.get_subject().as_text()
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'

# OpenSSL
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
print x509.get_subject().get_components()
#[('C', 'US'),
# ('ST', 'California'),
# ('L', 'Mountain View'),
# ('O', 'Google Inc'),
# ('CN', 'www.google.com')]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文