Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X509 cert class for ssl module #62569

Open
tiran opened this issue Jul 5, 2013 · 17 comments
Open

X509 cert class for ssl module #62569

tiran opened this issue Jul 5, 2013 · 17 comments
Assignees
Labels
3.8 only security fixes extension-modules C modules in the Modules dir topic-SSL type-feature A feature request or enhancement

Comments

@tiran
Copy link
Member

tiran commented Jul 5, 2013

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']

    @tiran
    Copy link
    Member Author

    tiran commented Jul 5, 2013

    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.

    @tiran tiran added extension-modules C modules in the Modules dir type-feature A feature request or enhancement labels Jul 5, 2013
    @pitrou
    Copy link
    Member

    pitrou commented Jul 5, 2013

    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 :-))

    @tiran
    Copy link
    Member Author

    tiran commented Jul 5, 2013

    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?

    @pitrou
    Copy link
    Member

    pitrou commented Jul 5, 2013

    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.

    @underrun
    Copy link
    Mannequin

    underrun mannequin commented Jul 26, 2013

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

    @underrun
    Copy link
    Mannequin

    underrun mannequin commented Jul 30, 2013

    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.

    @tiran
    Copy link
    Member Author

    tiran commented Oct 21, 2013

    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.

    @tiran tiran self-assigned this Oct 21, 2013
    @tiran
    Copy link
    Member Author

    tiran commented Nov 17, 2013

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

    @BreamoreBoy
    Copy link
    Mannequin

    BreamoreBoy mannequin commented May 5, 2015

    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/ ?

    @tiran
    Copy link
    Member Author

    tiran commented May 5, 2015

    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.

    @tiran tiran removed their assignment Jun 12, 2016
    @tiran tiran added 3.7 (EOL) end of life topic-SSL labels Sep 8, 2016
    @tiran tiran self-assigned this Sep 15, 2016
    @tiran
    Copy link
    Member Author

    tiran commented Jan 12, 2018

    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)

    @tiran
    Copy link
    Member Author

    tiran commented Jan 12, 2018

    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'

    @ned-deily
    Copy link
    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.

    @ned-deily ned-deily added 3.8 only security fixes deferred-blocker labels Jan 28, 2018
    @tiran
    Copy link
    Member Author

    tiran commented Feb 25, 2018

    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

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @dekimsey
    Copy link

    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.

    @andir
    Copy link

    andir commented Jul 24, 2023

    @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.

    @erlend-aasland
    Copy link
    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
    Labels
    3.8 only security fixes extension-modules C modules in the Modules dir topic-SSL type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants