Skip to content

X509 cert class for ssl module #62569

Open

Description

@tiran
BPO 18369
Nosy @pitrou, @tiran, @ned-deily, @mmaker
PRs
  • gh-62569: Add certificate and private key types #5162
  • Files
  • ssl_pyx509cert.patch
  • ssl_pyx509cert_match_hostname_fix.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/tiran'
    closed_at = None
    created_at = <Date 2013-07-05.20:10:37.355>
    labels = ['extension-modules', 'expert-SSL', 'type-feature', '3.8']
    title = 'X509 cert class for ssl module'
    updated_at = <Date 2018-02-25.20:14:01.308>
    user = 'https://github.com/tiran'

    bugs.python.org fields:

    activity = <Date 2018-02-25.20:14:01.308>
    actor = 'christian.heimes'
    assignee = 'christian.heimes'
    closed = False
    closed_date = None
    closer = None
    components = ['Extension Modules', 'SSL']
    creation = <Date 2013-07-05.20:10:37.355>
    creator = 'christian.heimes'
    dependencies = []
    files = ['30783', '31047']
    hgrepos = []
    issue_num = 18369
    keywords = ['patch']
    message_count = 14.0
    messages = ['192353', '192354', '192361', '192362', '193762', '193940', '200762', '203166', '242605', '242606', '309842', '309844', '310991', '312850']
    nosy_count = 6.0
    nosy_names = ['pitrou', 'christian.heimes', 'ned.deily', 'cvrebert', 'maker', 'underrun']
    pr_nums = ['5162']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue18369'
    versions = ['Python 3.8']

    Activity

    1. tiran commented on Jul 5, 2013

      @tiran
      MemberAuthor

      I'm working on a X509 certificate class for the SSL module. Eventually methods like getpeercert() are going to return X509 instances and the Python interface can decide if it should return a dict, DER bytes or whatever. IMHO it's a mandatory requirement for OCSP support, too.

      The patch contains a very real proof of concept.

    2. pitrou commented on Jul 5, 2013

      @pitrou
      Member

      Yeah, this is probably inevitable. Major concern is how to maintain compatibility with getpeercert() currently returning a dict. Should we make X509 a dict subclass? (yikes :-))

    3. tiran commented on Jul 5, 2013

      @tiran
      MemberAuthor

      A dict subclass? Oh heck ...

      I have slightly different plans. But first, do you agree that the _ssl C extension and all its methods are consider an internal API? How about the _ssl module's method returns X509 objects and the Python module calls methods on the X509 object like get_info() -> dict or get_der() -> bytes?

    4. pitrou commented on Jul 5, 2013

      @pitrou
      Member

      I have slightly different plans. But first, do you agree that the _ssl
      C extension and all its methods are consider an internal API? How
      about the _ssl module's method returns X509 objects and the Python
      module calls methods on the X509 object like get_info() -> dict or
      get_der() -> bytes?

      Sounds fine, yes.

    5. underrun commented on Jul 26, 2013

      underrunmannequin
      Mannequin

      For ssl.match_hostname to work with this, you need to get the info dict first. I've attached at patch for it.

    6. underrun commented on Jul 30, 2013

      underrunmannequin
      Mannequin

      actually, i suppose rather than change a bunch of existing functions/methods to handle X509 certs it would make more sense to add new methods to the X509 cert class (like match_hostname) so that old stuff doesn't break.

    7. tiran commented on Oct 21, 2013

      @tiran
      MemberAuthor

      Bump up my priority. I'd like to get the feature into 3.4 as a foundation for some of my other improvements of the SSL module.

    8. self-assigned this
      on Oct 21, 2013
    9. tiran commented on Nov 17, 2013

      @tiran
      MemberAuthor

      The feature won't be ready for 3.4. I'll work on a PEP for 3.5

    10. BreamoreBoy commented on May 5, 2015

      BreamoreBoymannequin
      Mannequin

      Presumably too late for 3.5 so do we bump this to 3.6? Alternatively could the Derek Wilson patch make 3.5, there's nearly three weeks until beta 1 is due on 24th May according to https://www.python.org/dev/peps/pep-0478/ ?

    11. tiran commented on May 5, 2015

      @tiran
      MemberAuthor

      I've a mostly working prototype at https://github.com/tiran/cpython/tree/feature/x509cert . It's missing documentation, more tests and I have to port it to argument clinic.

    12. removed their assignment
      on Jun 12, 2016
    13. self-assigned this
      on Sep 15, 2016
    14. tiran commented on Jan 12, 2018

      @tiran
      MemberAuthor

      API example:

      >> import ssl

      >>> chain = ssl.Certificate.chain_from_file("Lib/test/ssl_cert.pem")
      >>> cas = ssl.Certificate.bundle_from_file("Lib/test/pycacert.pem")
      >>> pkey = ssl.PrivateKey.from_file("Lib/test/ssl_key.passwd.pem")
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      ssl.SSLError: [PEM: BAD_PASSWORD_READ] bad password read (_ssl.c:58)
      >>> pkey = ssl.PrivateKey.from_file("Lib/test/ssl_key.passwd.pem", password="somepass")
      
      >>> chain
      (<_ssl.Certificate '/C=XY/L=Castle Anthrax/O=Python Software Foundation/CN=localhost'>,)
      >>> cas
      [<_ssl.Certificate '/C=XY/O=Python Software Foundation CA/CN=our-ca-server'>]

      >> ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
      >> ctx.load_cert_chain(chain, pkey)
      >> ctx.load_verify_locations(cadata=cas)

    15. tiran commented on Jan 12, 2018

      @tiran
      MemberAuthor

      More examples:

      >>> import ssl, socket, pprint
      >>> ctx = ssl.create_default_context()
      >>> sock = ctx.wrap_socket(socket.socket(), server_hostname="www.python.org")
      >>> sock.connect(("www.python.org", 443))
      >>> pprint.pprint(sock._sslobj._sslobj.verified_chain())
      (<_ssl.Certificate '/businessCategory=Private Organization/jurisdictionC=US/jurisdictionST=Delaware/serialNumber=3359300/street=16 Allen Rd/postalCode=03894-4801/C=US/ST=New Hampshire/L=Wolfeboro/O=Python Software Foundation/CN=www.python.org'>,
       <_ssl.Certificate '/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 Extended Validation Server CA'>,
       <_ssl.Certificate '/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA'>)
      
      >>> eecert = sock._sslobj._sslobj.verified_chain()[0]
      >>> eecert.check_hostname('www.python.org')
      'www.python.org'
      >>> eecert.check_hostname('www.python.com')
      False
      
      >>> cert = ssl.Certificate.from_file('wildcards-combined.rsa.pem')
      >>> pprint.pprint(cert.get_info())
      {'OCSP': ('http://testca.pythontest.net/ca/ocsp/pysubca',),
       'caIssuers': ('http://testca.pythontest.net/ca/pysubca.cer',),
       'crlDistributionPoints': ('http://testca.pythontest.net/ca/pysubca.crl',),
       'issuer': ((('countryName', 'XZ'),),
                  (('stateOrProvinceName', 'Holy Grail'),),
                  (('organizationName', 'Castle Anthrax'),),
                  (('organizationalUnitName', 'Python Software Foundation'),),
                  (('commonName', 'Python Tests Intermediate CA'),)),
       'notAfter': 'Jan  1 12:00:00 2027 GMT',
       'notBefore': 'Jan  1 12:00:00 2017 GMT',
       'serialNumber': '0A',
       'subject': ((('countryName', 'XZ'),),
                   (('stateOrProvinceName', 'Holy Grail'),),
                   (('organizationName', 'Castle Anthrax'),),
                   (('organizationalUnitName', 'Python Software Foundation'),),
                   (('commonName', 'Wildcards in SAN'),)),
       'subjectAltName': (('DNS', '*.wildcard.pythontest.net'),
                          ('DNS', 'www*.wildcard-www.pythontest.net'),
                          ('DNS', 'x*.wildcard-x.pythontest.net')),
       'version': 3}
      >>> cert.check_hostname('www.wildcard.pythontest.net')
      '*.wildcard.pythontest.net'
    16. ned-deily commented on Jan 28, 2018

      @ned-deily
      Member

      At Christian's request and considering the importance of the ssl module, I'm going to allow an extension for landing of this feature until 3.7.0b2, currently scheduled for 2018-02-26. If anyone else can help Christian get this in before b2, that would be great.

    17. tiran commented on Feb 25, 2018

      @tiran
      MemberAuthor

      I won't be able to land this in time for b2. It's most done but not production ready. I have only a limited amount of time and will use it to fix TLS 1.3 bits and pieces.

      Rescheduling for 3.8

    18. transferred this issue fromon Apr 10, 2022
    19. dekimsey commented on Oct 27, 2022

      @dekimsey

      Since this didn't happen for 3.8, what would need to happen to get it reconsidered for 3.11 (or a future revision)?

      To work-around this, I've had to:

      import ssl
      import tempfile
      import pprint
      
      with tempfile.NamedTemporaryFile() as tmp:
          tmp.write(pem_cert)
          tmp.flush()
          info = ssl._ssl._test_decode_cert(tmp.name)
          pprint.pprint(info)

      Doable but pretty clunky, especially since I'm accessing these internal functions.

    20. andir commented on Jul 24, 2023

      @andir

      @tiran any chance to revive this? I'd love to help finish this work you started here.

      Having access to X509 classes (and being able to parse and then pass them on from files or bytes) is a rather important feature that isn't currently possible with the standard ssl module. As you wrote many years ago this would open up the possibility to implement e.g. OCSP which is also on my bucket list of things to look into.

    21. erlend-aasland commented on Jan 4, 2024

      @erlend-aasland
      Contributor

      The linked PR seems to be based on the withdrawn PEP-543. Since that PEP is withdrawn, is this issue still relevant?

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions