如何使用 Python 的 ssl 库检索 OpenLDAP 服务器的 STARTTLS 证书?



我的目标是从在线服务中检索TLS证书,并使用Python计算证书过期的秒数。我已经为HTTPS:解决了这个问题

import socket
import ssl
from datetime import datetime
from pytz as pytz
from asn1crypto.x509 import Certificate
def https_ttl(host, port):
context = ssl.create_deafult_context()
# we just want to check the certificate expiry date, we do not need to validate the chain of trust
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port)) as tcp_socket:
with context.wrap_socket(tcp_socket, server_hostname=host) as ssl_socket:
# getpeercert(binary_form=False) returns empty dict if verification is disabled
cert = Certificate.load(ssl_socket.getpeercert(binary_form=True))
return (cert.not_valid_after - datetime.now(pytz.utc)).total_seconds()

当我使用https_ttl()从为STARTTLS配置的OpenLDAP服务器检索证书时,我收到以下错误消息:

my_code.py:21: in https_ttl
with self._context.wrap_socket(tcp_socket, server_hostname=host) as ssl_socket:
../../.pyenv/versions/3.8.12/lib/python3.8/ssl.py:500: in wrap_socket
return self.sslsocket_class._create(
../../.pyenv/versions/3.8.12/lib/python3.8/ssl.py:1040: in _create
self.do_handshake()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <ssl.SSLSocket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
block = False
@_sslcopydoc
def do_handshake(self, block=False):
self._check_connected()
timeout = self.gettimeout()
try:
if timeout == 0.0 and block:
self.settimeout(None)
>           self._sslobj.do_handshake()
E           ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:1131)
../../.pyenv/versions/3.8.12/lib/python3.8/ssl.py:1309: SSLEOFError

但是,我可以直接使用openssl检索证书:

$ echo | openssl s_client -connect localhost:8389 -starttls ldap 2> /dev/null | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p'
-----BEGIN CERTIFICATE-----
MIIXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXBgNV
BAMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMDAx
WhcXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZW1h
bHlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX9hHH
HR2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3VYt
yyPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1ogL
8UzXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXunIo
SGVXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXQO/u
PwCXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXdt9F
qcXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8XKs
wHiXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXQIsu
AtEXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXz0U
ct8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXJ3zc
ddcXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYWM9
UB7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXFJ+b
XCRXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXK7yw
ZrUXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXqJi3
L6fXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX133L
WQLXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXkMas
YuWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXcH3a
7rdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXDt0B
bdDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXydm7
f6FXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXvgXA
PNzXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXTiN/
+J7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXsbC
BNGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXqEjB
2x5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXPwVF
ffBXXXXXXXXXXXXXXXXXXXXXX3pm
-----END CERTIFICATE-----

如何使用Python的ssl库复制openssl调用?

这是我基于@user207421评论的解决方案:

import socket
import ssl
from datetime import datetime
from pytz as pytz
from asn1crypto.x509 import Certificate
LDAP_START_TLS_SUCCESS = 0
LDAP_START_TLS_RESP_LENGTH = 14
def ldap_starttls_ttl(host, port):
"""Initiates STARTTLS handshake with OpenLDAP server and returns the certificate's time to live (TTL) in seconds."""
context = ssl.create_deafult_context()
# we just want to check the certificate expiry date, we do not need to validate the chain of trust
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port)) as tcp_socket:
# send LDAP_START_TLS_OID (extracted from Wireshark)
tcp_socket.send(
b"x30x1dx02x01x01x77x18x80x16x31x2ex33x2ex36x2ex31"
b"x2ex34x2ex31x2ex31x34x36x36x2ex32x30x30x33x37"
)
extended_resp = tcp_socket.recv(LDAP_START_TLS_RESP_LENGTH)
result_code = extended_resp[9]
if result_code != LDAP_START_TLS_SUCCESS:
raise ValueError(f'resultCode of LDAP_START_TLS_OID response is {result_code} instead of '
f'{LDAP_START_TLS_SUCCESS}')
with context.wrap_socket(tcp_socket, server_hostname=host) as ssl_socket:
# getpeercert(binary_form=False) returns empty dict if verification is disabled
cert = Certificate.load(ssl_socket.getpeercert(binary_form=True))
return (cert.not_valid_after - datetime.now(pytz.utc)).total_seconds()

相关内容

最新更新