Skip to content

Latest commit

 

History

History
184 lines (136 loc) · 7.02 KB

File metadata and controls

184 lines (136 loc) · 7.02 KB

pyOpenSSL

A thin Python wrapper over OpenSSL that exposes TLS connection handling and X.509 certificate operations for tasks the high-level cryptography recipes don't cover.

Overview

pyOpenSSL bridges Python to the OpenSSL library, giving direct access to SSL/TLS contexts, handshakes, and certificate/key objects. It is useful for grabbing and inspecting certificates straight off a live TLS service, generating self-signed certs for lab servers, and probing protocol/cipher behaviour. Much of the low-level crypto has migrated to cryptography, but pyOpenSSL remains the practical choice for live-connection certificate retrieval.

Installation

pip install pyOpenSSL

Basic Usage

import socket

from OpenSSL import SSL

context = SSL.Context(SSL.TLS_METHOD)
connection = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM))

try:
    connection.connect(("127.0.0.1", 443))
    connection.set_tlsext_host_name(b"localhost")     # SNI
    connection.do_handshake()

    cert = connection.get_peer_certificate()
    print("subject:", dict(cert.get_subject().get_components()))
    print("expired:", cert.has_expired())
finally:
    connection.close()

Note

For most work the standard library's [[SSL-Module|ssl]] module or cryptography is the better choice. pyOpenSSL is for cases needing direct OpenSSL access.

Important APIs

API Purpose
SSL.Context(method) TLS context; set verification and ciphers
SSL.Connection(context, socket) Wrap a socket
connection.do_handshake() Perform the TLS handshake
connection.set_tlsext_host_name(bytes) Set SNI — required for virtual hosts
connection.get_peer_certificate() The server's certificate
connection.get_peer_cert_chain() The full presented chain
cert.get_subject() / get_issuer() Distinguished names
cert.has_expired() Validity check against now
cert.get_notAfter() Expiry as YYYYMMDDHHMMSSZ bytes
cert.digest("sha256") Certificate fingerprint
crypto.dump_certificate(FILETYPE_PEM, cert) Serialise to PEM
context.set_verify(mode, callback) Configure peer verification

Example

Retrieve and inspect a certificate from a live TLS service (authorized target):

import socket
from OpenSSL import SSL

ctx = SSL.Context(SSL.TLS_CLIENT_METHOD)
conn = SSL.Connection(ctx, socket.socket())
conn.set_tlsext_host_name(b"example.com")   # SNI
conn.connect(("example.com", 443))
conn.do_handshake()

cert = conn.get_peer_certificate()
print("subject:", cert.get_subject().CN)
print("issuer :", cert.get_issuer().CN)
print("expires:", cert.get_notAfter().decode())
conn.close()

Output

subject: example.com
issuer : DigiCert Global G2 TLS RSA SHA256 2020 CA1
expires: 20261101000000Z

Generate a self-signed certificate for a lab server:

from OpenSSL import crypto

key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, 2048)

cert = crypto.X509()
cert.get_subject().CN = "lab.local"
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(365 * 24 * 3600)
cert.set_issuer(cert.get_subject())          # self-signed
cert.set_pubkey(key)
cert.sign(key, "sha256")

print(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode()[:64], "...")

Output

-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgI...

Check certificate expiry and flag near-expiry certs:

from datetime import datetime, timezone
from OpenSSL import crypto

with open("server.crt", "rb") as fh:
    cert = crypto.load_certificate(crypto.FILETYPE_PEM, fh.read())

not_after = datetime.strptime(cert.get_notAfter().decode(), "%Y%m%d%H%M%SZ")
not_after = not_after.replace(tzinfo=timezone.utc)
days_left = (not_after - datetime.now(timezone.utc)).days

print(f"days until expiry: {days_left}")
print("EXPIRED!" if cert.has_expired() else "valid")

Output

days until expiry: 102
valid

Security Use Cases

  • Certificate harvesting — pull certs directly off live HTTPS/SMTPS services to enumerate SANs and discover related hostnames.
  • TLS posture review — inspect issuer, key size, signature algorithm, and validity during an infrastructure assessment.
  • Lab cert generation — mint self-signed certs for test servers, MITM proxies, and interception labs you control.
  • Expiry monitoring — automate alerts for certificates nearing expiry across an estate.
  • SNI / vhost probing — set the SNI name to reveal per-host certificates behind shared IPs.

Common Mistakes

  • Forgetting SNI. Without set_tlsext_host_name() a virtual host returns the wrong certificate, and your audit reports the wrong result.
  • Not calling do_handshake() explicitly, so certificate methods return None.
  • Assuming has_expired() validates the certificate — it checks dates only, not the chain, hostname, or revocation.
  • Misparsing get_notAfter() — it returns bytes in YYYYMMDDHHMMSSZ form, not a datetime.
  • Leaving verification disabled because it made the handshake succeed.
  • Reaching for pyOpenSSL when ssl or cryptography would do — it is a lower-level, more error-prone API.
  • Confusing the packagespyOpenSSL is the pip name, OpenSSL the import name.

Security Considerations

[!warning] Authorized use only Connect only to hosts you own or are authorized to test.

  • Parsing a certificate is not validating it. A full check requires chain building to a trusted root, hostname matching, validity dates, and revocation status. has_expired() covers one of those.
  • Disabling verification defeats TLS entirely. If a handshake fails, fix the trust store — do not turn verification off.
  • Certificates leak infrastructure detail. SANs frequently enumerate internal hostnames, which is useful reconnaissance and should be treated as sensitive.
  • Keep the library and OpenSSL current. TLS libraries are a high-value target and ship security fixes regularly.
  • Prefer the maintained alternatives. The standard library's [[SSL-Module|ssl]] module handles most auditing needs, and cryptography covers X.509 parsing with a safer API.
  • Never use production private keys in course exercises; generate throwaway keys for the lab.

Best Practices

  • Prefer the cryptography library for new pure-crypto code; reach for pyOpenSSL when you need live TLS connections or OpenSSL objects.
  • Set the SNI host name (set_tlsext_host_name) or you may receive a default/wrong certificate.
  • Generate at least 2048-bit RSA (or ECDSA) keys and sign with SHA-256 or better.
  • Keep private keys for lab certs out of version control.
  • Handle handshake exceptions to distinguish protocol/cipher failures from network errors.

References

Related Topics

  • [[Cryptography]] — the modern library pyOpenSSL increasingly defers to
  • [[requests]] — the client whose TLS endpoints you inspect here
  • [[Readme|Python for Security Professionals]] — course home