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
Comments
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. |
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 :-)) |
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? |
Sounds fine, yes. |
For ssl.match_hostname to work with this, you need to get the info dict first. I've attached at patch for it. |
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. |
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. |
The feature won't be ready for 3.4. I'll work on a PEP for 3.5 |
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/ ? |
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. |
API example:
>>> 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'>]
|
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' |
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. |
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 |
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. |
@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 |
The linked PR seems to be based on the withdrawn PEP-543. Since that PEP is withdrawn, is this issue still relevant? |
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:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: