Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[BUG] extract_peer_certificate() raises AttributeError with pyOpenSSL >= 26.2.0 when the peer certificate has extensions

Đang mở
#2,014 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@oberstet đang làm issue này rồi.

Từ ngày 20/10/2026.

  • #2015 của @oberstet — đang mở

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
cryptography, python
Lĩnh vực
api, authentication, backend

Hướng nghiên cứu

Start with extract_peer_certificate() and create_transport_details() in the Twisted and asyncio paths, then read twisted/websocket.py and twisted/rawsocket.py at their handshake callbacks. The issue identifies pyOpenSSL's removed get_extension() call and describes the expected certificate fields and transport behavior. Done means certificate extraction works with current pyOpenSSL, peer_cert is populated after TLS handshakes, and the listed real-certificate tests pass.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

bug wamp websocket

Analysis across autobahn-python (master b7c3646d) and Crossbar.io (master 4d2a46fb + fix_2048), with pyOpenSSL 26.4.0, Twisted 26.4.0, cryptography 50.0.2.

Two bugs, not one

A. extract_peer_certificate() raises on every real certificate (this issue). It calls X509.get_extension(i), removed in pyOpenSSL 26.2.0 (get_extension_count() is still there, so the loop is entered). It also calls X509.get_subject() / get_issuer(), deprecated in pyOpenSSL 26.x ("in preparation for deprecating pyOpenSSL's X509 entirely"). Only a certificate without any extensions gets through, which in practice is none (SAN, key usage, basic constraints).

B. On WebSocket and RawSocket (Twisted), peer_cert is never filled. create_transport_details() runs in connectionMade(), and Twisted calls the wrapped protocol's connectionMade() on TCP connect, before the TLS handshake. After the handshake, _onOpen() (twisted/websocket.py) and _on_handshake_complete() (twisted/rawsocket.py) refresh channel_id, but not peer_cert. Bug B hides bug A on these transports. Checked with a TLS server that requires a client certificate, on both TLS 1.2 and TLS 1.3:

connectionMade     peer_cert: None   channel_id: 00000000...
handshakeCompleted EXC AttributeError 'X509' object has no attribute 'get_extension'
dataReceived       EXC AttributeError 'X509' object has no attribute 'get_extension'

Both bugs date from bbdadcb5 (#1552, 2022-04-27, "add TLS extract_peer_certificate helper; fill in transport details"). Before that, Crossbar.io read the client certificate itself.

Who is affected

Caller Effect
autobahn twisted/websocket.py, twisted/rawsocket.py → create_transport_details() in connectionMade() peer_cert always None (bug B)
autobahn asyncio/util.py create_transport_details() peer_cert hard-coded None (asyncio never extracts it)
Crossbar.io router/auth/tls.py (PendingAuthTLS, authmethod tls), reads transport_details.peer_cert["sha1"] over WebSocket / RawSocket: always "no client certificate presented" (bug B), since 22.x
Crossbar.io dynamic authenticators: details["transport"]["peer_cert"] (via TransportDetails.marshal(), router/auth/pending.py) same, always null
Crossbar.io MQTT bridge WampTransport (bridge/mqtt/wamp.py, crossbar #2048): builds its transport details after the handshake AttributeError, the connection fails (bug A); the two MQTT TLS tests are skipped for now

So TLS client-certificate authentication in Crossbar.io does not work on any transport today, and no test catches it: neither project has a test with a real TLS client certificate. Crossbar.io's test_authorize.py::test_authextra_tls uses mocks.

Other pyOpenSSL X509 uses in Crossbar.io are unaffected: crypto.load_certificate() in common/twisted/endpoint.py and common/twisted/tlsctx.py still exists.

Recommended fix (autobahn 26.10.2)

  1. Re-implement the extraction on cryptography.x509 (a core autobahn dependency, cryptography>=50), as a backend-neutral helper that takes a cryptography.x509.Certificate:

    • Twisted: transport.getPeerCertificate().to_cryptography() (to_cryptography() exists since pyOpenSSL 17.1, the floor is 20.0.1).
    • asyncio: ssl_object.getpeercert(binary_form=True) → x509.load_der_x509_certificate(). This fills peer_cert for asyncio too, so both backends behave the same.
  2. Keep the returned dict as it is. It is an interface: dynamic authenticators get it, and Crossbar.io's tls authmethod keys on "sha1". Field by field, checked against the current pyOpenSSL output:

    field replacement same as before?
    md5, sha1, sha256 ":".join(f"{b:02X}" for b in cert.fingerprint(hashes.SHA1())) yes, byte-identical
    serial, version cert.serial_number, cert.version.value yes
    not_before, not_after cert.not_valid_before_utc.strftime("%Y%m%d%H%M%SZ") yes
    expired now > cert.not_valid_after_utc yes
    signature_algorithm cert.signature_algorithm_oid._name yes for RSA and ECDSA; ed25519 instead of ED25519
    subject, issuer per attribute: rfc4514_attribute_name (C, ST, L, O, OU, CN), else (emailAddress has no RFC 4514 name) oid._name; lowercased yes, including emailaddress
    hash X509.subject_name_hash() (OpenSSL X509_NAME_hash; not deprecated, no cryptography equivalent) yes on Twisted; None on asyncio
    extensions[].name, .critical ext.oid._name, ext.critical yes for the standard extensions
    extensions[].value was OpenSSL's text (DNS:localhost, IP Address:127.0.0.1); cryptography has no such printer changes: SAN can be rendered OpenSSL-style, other extensions as str(ext.value)
  3. Never raise on the peer's certificate. This runs on data the peer controls, before authentication. cryptography rejects for example duplicate extensions when cert.extensions is read; catch that, log it, and return the other fields with extensions: [], so the connection still gets to authentication.

  4. Fix bug B: in _onOpen() (WebSocket) and _on_handshake_complete() (RawSocket) set peer_cert next to channel_id. TLS application data only arrives after the handshake, so the certificate is there by then.

  5. Tests with real TLS client certificates, generated at test time: the extraction on a certificate with SAN, key usage, EKU and an email address, comparing the dict field by field; WebSocket and RawSocket over TLS deliver peer_cert to the session; a malformed certificate does not raise; the asyncio path.

Then Crossbar.io 26.10.1

  • Raise the floor to autobahn>=26.10.2, relock.
  • Un-skip the two MQTT TLS tests (#2048).
  • Add a router test: authmethod tls over RawSocket and over WebSocket with a real client certificate, both accepted and denied, so this cannot regress unnoticed again.
  • Changelog: TLS client-certificate authentication works again on all transports.

Not in scope: tls-unique is not defined for TLS 1.3 (RFC 9266 tls-exporter), see crossbar #2037.

This analysis was done with AI assistance (Claude Code).

Ngôn ngữ chính
Python
Star
2.5k
Fork
767
Merge trung bình
1 ngày 1 giờ
Pull request đã merge (30 ngày)
23

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của crossbario/autobahn-python

Tất cả issue của crossbario/autobahn-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.