Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(46671)

Side by Side Diff: Lib/ssl.py

Issue 19689: ssl.create_default_context()
Patch Set: Created 5 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Lib/smtplib.py ('k') | Lib/test/test_ssl.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Wrapper module for _ssl, providing some additional facilities 1 # Wrapper module for _ssl, providing some additional facilities
2 # implemented in Python. Written by Bill Janssen. 2 # implemented in Python. Written by Bill Janssen.
3 3
4 """This module provides some more Pythonic support for SSL. 4 """This module provides some more Pythonic support for SSL.
5 5
6 Object types: 6 Object types:
7 7
8 SSLSocket -- subtype of socket.socket which does SSL over the socket 8 SSLSocket -- subtype of socket.socket which does SSL over the socket
9 9
10 Exceptions: 10 Exceptions:
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE 85 ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE
86 ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE 86 ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE
87 ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY 87 ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
88 """ 88 """
89 89
90 import textwrap 90 import textwrap
91 import re 91 import re
92 import sys 92 import sys
93 import os 93 import os
94 from collections import namedtuple 94 from collections import namedtuple
95 import enum as _enum
95 96
96 import _ssl # if we can't import it, let the error propagate 97 import _ssl # if we can't import it, let the error propagate
97 98
98 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION 99 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
99 from _ssl import _SSLContext 100 from _ssl import _SSLContext
100 from _ssl import ( 101 from _ssl import (
101 SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, 102 SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
102 SSLSyscallError, SSLEOFError, 103 SSLSyscallError, SSLEOFError,
103 ) 104 )
104 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED 105 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 def fromnid(cls, nid): 290 def fromnid(cls, nid):
290 """Create _ASN1Object from OpenSSL numeric ID 291 """Create _ASN1Object from OpenSSL numeric ID
291 """ 292 """
292 return super().__new__(cls, *_nid2obj(nid)) 293 return super().__new__(cls, *_nid2obj(nid))
293 294
294 @classmethod 295 @classmethod
295 def fromname(cls, name): 296 def fromname(cls, name):
296 """Create _ASN1Object from short name, long name or OID 297 """Create _ASN1Object from short name, long name or OID
297 """ 298 """
298 return super().__new__(cls, *_txt2obj(name, name=True)) 299 return super().__new__(cls, *_txt2obj(name, name=True))
300
301
302 class PurposeEKU(_ASN1Object, _enum.Enum):
303 """X509v3 Extended Key Usage objects
304 """
305 serverAuth = '1.3.6.1.5.5.7.3.1'
306 clientAuth = '1.3.6.1.5.5.7.3.2'
307 #codeSigning = '1.3.6.1.5.5.7.3.3'
308 #emailProtection = '1.3.6.1.5.5.7.3.4'
309 #ipsecEndSystem = '1.3.6.1.5.5.7.3.5'
310 #ipsecTunnel = '1.3.6.1.5.5.7.3.6'
311 #ipsecUser = '1.3.6.1.5.5.7.3.7'
312 #timeStamping = '1.3.6.1.5.5.7.3.8'
313 #OCSPSigning = '1.3.6.1.5.5.7.3.9'
314 #DVCS = '1.3.6.1.5.5.7.3.10'
315 #nsSGC = '2.16.840.1.113730.4.1'
316 #msSGC = '1.3.6.1.4.1.311.10.3.3'
299 317
300 318
301 class SSLContext(_SSLContext): 319 class SSLContext(_SSLContext):
302 """An SSLContext holds various SSL-related configuration options and 320 """An SSLContext holds various SSL-related configuration options and
303 data, such as certificates and possibly a private key.""" 321 data, such as certificates and possibly a private key."""
304 322
305 __slots__ = ('protocol', '__weakref__') 323 __slots__ = ('protocol', '__weakref__')
306 324
307 def __new__(cls, protocol, *args, **kwargs): 325 def __new__(cls, protocol, *args, **kwargs):
308 self = _SSLContext.__new__(cls, protocol) 326 self = _SSLContext.__new__(cls, protocol)
(...skipping 17 matching lines...) Expand all
326 def set_npn_protocols(self, npn_protocols): 344 def set_npn_protocols(self, npn_protocols):
327 protos = bytearray() 345 protos = bytearray()
328 for protocol in npn_protocols: 346 for protocol in npn_protocols:
329 b = bytes(protocol, 'ascii') 347 b = bytes(protocol, 'ascii')
330 if len(b) == 0 or len(b) > 255: 348 if len(b) == 0 or len(b) > 255:
331 raise SSLError('NPN protocols must be 1 to 255 in length') 349 raise SSLError('NPN protocols must be 1 to 255 in length')
332 protos.append(len(b)) 350 protos.append(len(b))
333 protos.extend(b) 351 protos.extend(b)
334 352
335 self._set_npn_protocols(protos) 353 self._set_npn_protocols(protos)
354
355 def load_default_certs(self, purpose):
356 if not isinstance(purpose, _ASN1Object):
357 raise TypeError(purpose)
358 self.set_default_verify_paths()
359
360
361 def create_default_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None,
362 purpose=PurposeEKU.serverAuth,
363 certfile=None, keyfile=None,
364 cafile=None, capath=None, cadata=None):
365 """Create a SSLContext object with default settings.
366
367 NOTE: The protocol and settings may change anytime without prior
368 deprecation. The values represent a fair balance between maximum
369 compatibility and security.
370 """
371 if not isinstance(purpose, _ASN1Object):
372 raise TypeError(purpose)
373
374 context = SSLContext(protocol)
375 # SSLv2 considered harmful.
376 context.options |= OP_NO_SSLv2
377
378 if cert_reqs is None:
379 if purpose == PurposeEKU.serverAuth:
380 # authenticate a TLS web server (for client sockets). The default
381 # setting may change in the future.
382 cert_reqs = CERT_NONE
383 elif purpose == PurposeEKU.clientAuth:
384 # authenticate a TLS web client (for server sockets). The default
385 # setting is guaranteed to be stable and will never change.
386 cert_reqs = CERT_NONE
387 else:
388 # other (code signing, S/MIME, IPSEC, ...), default may change.
389 cert_reqs = CERT_NONE
390 context.verify_mode = cert_reqs
391
392 # load cert file and key file
393 if certfile or keyfile:
394 context.load_cert_chain(certfile, keyfile)
395
396 # load CA root certs
397 if cafile or capath or cadata:
398 context.load_verify_locations(cafile, capath, cadata)
399 elif cert_reqs != CERT_NONE and purpose is not None:
400 # no explicit cafile, capath or cadata but the verify mode is
401 # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system
402 # root CA certificates for the given purpose. This may fail silently.
403 context.load_default_certs(purpose)
404
405 return context
336 406
337 407
338 class SSLSocket(socket): 408 class SSLSocket(socket):
339 """This class implements a subtype of socket.socket that wraps 409 """This class implements a subtype of socket.socket that wraps
340 the underlying OS socket in an SSL context when necessary, and 410 the underlying OS socket in an SSL context when necessary, and
341 provides read and write methods over that channel.""" 411 provides read and write methods over that channel."""
342 412
343 def __init__(self, sock=None, keyfile=None, certfile=None, 413 def __init__(self, sock=None, keyfile=None, certfile=None,
344 server_side=False, cert_reqs=CERT_NONE, 414 server_side=False, cert_reqs=CERT_NONE,
345 ssl_version=PROTOCOL_SSLv23, ca_certs=None, 415 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
(...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after
752 822
753 if not pem_cert_string.startswith(PEM_HEADER): 823 if not pem_cert_string.startswith(PEM_HEADER):
754 raise ValueError("Invalid PEM encoding; must start with %s" 824 raise ValueError("Invalid PEM encoding; must start with %s"
755 % PEM_HEADER) 825 % PEM_HEADER)
756 if not pem_cert_string.strip().endswith(PEM_FOOTER): 826 if not pem_cert_string.strip().endswith(PEM_FOOTER):
757 raise ValueError("Invalid PEM encoding; must end with %s" 827 raise ValueError("Invalid PEM encoding; must end with %s"
758 % PEM_FOOTER) 828 % PEM_FOOTER)
759 d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)] 829 d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
760 return base64.decodebytes(d.encode('ASCII', 'strict')) 830 return base64.decodebytes(d.encode('ASCII', 'strict'))
761 831
762 def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None): 832 def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None,
833 cert_reqs=None):
763 """Retrieve the certificate from the server at the specified address, 834 """Retrieve the certificate from the server at the specified address,
764 and return it as a PEM-encoded string. 835 and return it as a PEM-encoded string.
765 If 'ca_certs' is specified, validate the server cert against it. 836 If 'ca_certs' is specified, validate the server cert against it.
766 If 'ssl_version' is specified, use it in the connection attempt.""" 837 If 'ssl_version' is specified, use it in the connection attempt."""
767 838
768 host, port = addr 839 host, port = addr
769 if (ca_certs is not None): 840 if ca_certs is not None:
770 cert_reqs = CERT_REQUIRED 841 cert_reqs = CERT_REQUIRED
771 else: 842 context = create_default_context(ssl_version,
772 cert_reqs = CERT_NONE 843 cert_reqs=cert_reqs,
773 s = create_connection(addr) 844 cafile=ca_certs)
774 s = wrap_socket(s, ssl_version=ssl_version, 845 with create_connection(addr) as sock:
775 cert_reqs=cert_reqs, ca_certs=ca_certs) 846 with context.wrap_socket(sock) as sslsock:
776 dercert = s.getpeercert(True) 847 dercert = sslsock.getpeercert(True)
777 s.close()
778 return DER_cert_to_PEM_cert(dercert) 848 return DER_cert_to_PEM_cert(dercert)
779 849
780 def get_protocol_name(protocol_code): 850 def get_protocol_name(protocol_code):
781 return _PROTOCOL_NAMES.get(protocol_code, '<unknown>') 851 return _PROTOCOL_NAMES.get(protocol_code, '<unknown>')
OLDNEW
« no previous file with comments | « Lib/smtplib.py ('k') | Lib/test/test_ssl.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+