1 # Wrapper module for _ssl, providing some additional facilities
2 # implemented in Python. Written by Bill Janssen.
5 This module provides some more Pythonic support for SSL.
9 SSLSocket -- subtype of socket.socket which does SSL over the socket
13 SSLError -- exception raised for I/O errors
17 cert_time_to_seconds -- convert time string used for certificate
18 notBefore and notAfter functions to integer
19 seconds past the Epoch (the time values
20 returned from time.time())
22 fetch_server_certificate (HOST, PORT) -- fetch the certificate provided
23 by the server running on HOST at port PORT. No
24 validation of the certificate is performed.
31 SSL_ERROR_WANT_X509_LOOKUP
34 SSL_ERROR_WANT_CONNECT
37 SSL_ERROR_INVALID_ERROR_CODE
39 The following group define certificate requirements that one side is
40 allowing/requiring from the other side:
42 CERT_NONE - no certificates from the other side are required (or will
43 be looked at if provided)
44 CERT_OPTIONAL - certificates are not required, but if provided will be
45 validated, and if validation fails, the connection will
47 CERT_REQUIRED - certificates are required, and will be validated, and
48 if validation fails, the connection will also fail
50 The following constants identify various SSL protocol variants:
60 import _ssl
# if we can't import it, let the error propagate
62 from _ssl
import OPENSSL_VERSION_NUMBER
, OPENSSL_VERSION_INFO
, OPENSSL_VERSION
63 from _ssl
import SSLError
64 from _ssl
import CERT_NONE
, CERT_OPTIONAL
, CERT_REQUIRED
65 from _ssl
import RAND_status
, RAND_egd
, RAND_add
67 SSL_ERROR_ZERO_RETURN
, \
68 SSL_ERROR_WANT_READ
, \
69 SSL_ERROR_WANT_WRITE
, \
70 SSL_ERROR_WANT_X509_LOOKUP
, \
73 SSL_ERROR_WANT_CONNECT
, \
75 SSL_ERROR_INVALID_ERROR_CODE
76 from _ssl
import PROTOCOL_SSLv3
, PROTOCOL_SSLv23
, PROTOCOL_TLSv1
78 PROTOCOL_TLSv1
: "TLSv1",
79 PROTOCOL_SSLv23
: "SSLv23",
80 PROTOCOL_SSLv3
: "SSLv3",
83 from _ssl
import PROTOCOL_SSLv2
87 _PROTOCOL_NAMES
[PROTOCOL_SSLv2
] = "SSLv2"
89 from socket
import socket
, _fileobject
, _delegate_methods
, error
as socket_error
90 from socket
import getnameinfo
as _getnameinfo
91 import base64
# for DER-to-PEM translation
94 class SSLSocket(socket
):
96 """This class implements a subtype of socket.socket that wraps
97 the underlying OS socket in an SSL context when necessary, and
98 provides read and write methods over that channel."""
100 def __init__(self
, sock
, keyfile
=None, certfile
=None,
101 server_side
=False, cert_reqs
=CERT_NONE
,
102 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
103 do_handshake_on_connect
=True,
104 suppress_ragged_eofs
=True, ciphers
=None):
105 socket
.__init
__(self
, _sock
=sock
._sock
)
106 # The initializer for socket overrides the methods send(), recv(), etc.
107 # in the instancce, which we don't need -- but we want to provide the
108 # methods defined in SSLSocket.
109 for attr
in _delegate_methods
:
112 except AttributeError:
115 if certfile
and not keyfile
:
117 # see if it's connected
119 socket
.getpeername(self
)
120 except socket_error
, e
:
121 if e
.errno
!= errno
.ENOTCONN
:
123 # no, no connection yet
124 self
._connected
= False
127 # yes, create the SSL object
128 self
._connected
= True
129 self
._sslobj
= _ssl
.sslwrap(self
._sock
, server_side
,
131 cert_reqs
, ssl_version
, ca_certs
,
133 if do_handshake_on_connect
:
135 self
.keyfile
= keyfile
136 self
.certfile
= certfile
137 self
.cert_reqs
= cert_reqs
138 self
.ssl_version
= ssl_version
139 self
.ca_certs
= ca_certs
140 self
.ciphers
= ciphers
141 self
.do_handshake_on_connect
= do_handshake_on_connect
142 self
.suppress_ragged_eofs
= suppress_ragged_eofs
143 self
._makefile
_refs
= 0
145 def read(self
, len=1024):
147 """Read up to LEN bytes and return them.
148 Return zero-length string on EOF."""
151 return self
._sslobj
.read(len)
153 if x
.args
[0] == SSL_ERROR_EOF
and self
.suppress_ragged_eofs
:
158 def write(self
, data
):
160 """Write DATA to the underlying SSL channel. Returns
161 number of bytes of DATA actually transmitted."""
163 return self
._sslobj
.write(data
)
165 def getpeercert(self
, binary_form
=False):
167 """Returns a formatted version of the data in the
168 certificate provided by the other end of the SSL channel.
169 Return None if no certificate was provided, {} if a
170 certificate was provided, but not validated."""
172 return self
._sslobj
.peer_certificate(binary_form
)
179 return self
._sslobj
.cipher()
181 def send(self
, data
, flags
=0):
185 "non-zero flags not allowed in calls to send() on %s" %
189 v
= self
._sslobj
.write(data
)
191 if x
.args
[0] == SSL_ERROR_WANT_READ
:
193 elif x
.args
[0] == SSL_ERROR_WANT_WRITE
:
200 return self
._sock
.send(data
, flags
)
202 def sendto(self
, data
, flags_or_addr
, addr
=None):
204 raise ValueError("sendto not allowed on instances of %s" %
207 return self
._sock
.sendto(data
, flags_or_addr
)
209 return self
._sock
.sendto(data
, flags_or_addr
, addr
)
211 def sendall(self
, data
, flags
=0):
215 "non-zero flags not allowed in calls to sendall() on %s" %
219 while (count
< amount
):
220 v
= self
.send(data
[count
:])
224 return socket
.sendall(self
, data
, flags
)
226 def recv(self
, buflen
=1024, flags
=0):
230 "non-zero flags not allowed in calls to recv() on %s" %
232 return self
.read(buflen
)
234 return self
._sock
.recv(buflen
, flags
)
236 def recv_into(self
, buffer, nbytes
=None, flags
=0):
237 if buffer and (nbytes
is None):
244 "non-zero flags not allowed in calls to recv_into() on %s" %
246 tmp_buffer
= self
.read(nbytes
)
248 buffer[:v
] = tmp_buffer
251 return self
._sock
.recv_into(buffer, nbytes
, flags
)
253 def recvfrom(self
, buflen
=1024, flags
=0):
255 raise ValueError("recvfrom not allowed on instances of %s" %
258 return self
._sock
.recvfrom(buflen
, flags
)
260 def recvfrom_into(self
, buffer, nbytes
=None, flags
=0):
262 raise ValueError("recvfrom_into not allowed on instances of %s" %
265 return self
._sock
.recvfrom_into(buffer, nbytes
, flags
)
269 return self
._sslobj
.pending()
275 s
= self
._sslobj
.shutdown()
279 raise ValueError("No SSL wrapper around " + str(self
))
281 def shutdown(self
, how
):
283 socket
.shutdown(self
, how
)
286 if self
._makefile
_refs
< 1:
290 self
._makefile
_refs
-= 1
292 def do_handshake(self
):
294 """Perform a TLS/SSL handshake."""
296 self
._sslobj
.do_handshake()
298 def _real_connect(self
, addr
, return_errno
):
299 # Here we assume that the socket is client-side, and not
300 # connected at the time of the call. We connect it, then wrap it.
302 raise ValueError("attempt to connect already-connected SSLSocket!")
303 self
._sslobj
= _ssl
.sslwrap(self
._sock
, False, self
.keyfile
, self
.certfile
,
304 self
.cert_reqs
, self
.ssl_version
,
305 self
.ca_certs
, self
.ciphers
)
307 socket
.connect(self
, addr
)
308 if self
.do_handshake_on_connect
:
310 except socket_error
as e
:
316 self
._connected
= True
319 def connect(self
, addr
):
320 """Connects to remote ADDR, and then wraps the connection in
322 self
._real
_connect
(addr
, False)
324 def connect_ex(self
, addr
):
325 """Connects to remote ADDR, and then wraps the connection in
327 return self
._real
_connect
(addr
, True)
331 """Accepts a new connection from a remote client, and returns
332 a tuple containing that new connection wrapped with a server-side
333 SSL channel, and the address of the remote client."""
335 newsock
, addr
= socket
.accept(self
)
336 return (SSLSocket(newsock
,
337 keyfile
=self
.keyfile
,
338 certfile
=self
.certfile
,
340 cert_reqs
=self
.cert_reqs
,
341 ssl_version
=self
.ssl_version
,
342 ca_certs
=self
.ca_certs
,
343 ciphers
=self
.ciphers
,
344 do_handshake_on_connect
=self
.do_handshake_on_connect
,
345 suppress_ragged_eofs
=self
.suppress_ragged_eofs
),
348 def makefile(self
, mode
='r', bufsize
=-1):
350 """Make and return a file-like object that
351 works with the SSL connection. Just use the code
352 from the socket module."""
354 self
._makefile
_refs
+= 1
355 # close=True so as to decrement the reference count when done with
356 # the file-like object.
357 return _fileobject(self
, mode
, bufsize
, close
=True)
361 def wrap_socket(sock
, keyfile
=None, certfile
=None,
362 server_side
=False, cert_reqs
=CERT_NONE
,
363 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
364 do_handshake_on_connect
=True,
365 suppress_ragged_eofs
=True, ciphers
=None):
367 return SSLSocket(sock
, keyfile
=keyfile
, certfile
=certfile
,
368 server_side
=server_side
, cert_reqs
=cert_reqs
,
369 ssl_version
=ssl_version
, ca_certs
=ca_certs
,
370 do_handshake_on_connect
=do_handshake_on_connect
,
371 suppress_ragged_eofs
=suppress_ragged_eofs
,
375 # some utility functions
377 def cert_time_to_seconds(cert_time
):
379 """Takes a date-time string in standard ASN1_print form
380 ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
381 a Python time value in seconds past the epoch."""
384 return time
.mktime(time
.strptime(cert_time
, "%b %d %H:%M:%S %Y GMT"))
386 PEM_HEADER
= "-----BEGIN CERTIFICATE-----"
387 PEM_FOOTER
= "-----END CERTIFICATE-----"
389 def DER_cert_to_PEM_cert(der_cert_bytes
):
391 """Takes a certificate in binary DER format and returns the
392 PEM version of it as a string."""
394 if hasattr(base64
, 'standard_b64encode'):
395 # preferred because older API gets line-length wrong
396 f
= base64
.standard_b64encode(der_cert_bytes
)
397 return (PEM_HEADER
+ '\n' +
398 textwrap
.fill(f
, 64) + '\n' +
401 return (PEM_HEADER
+ '\n' +
402 base64
.encodestring(der_cert_bytes
) +
405 def PEM_cert_to_DER_cert(pem_cert_string
):
407 """Takes a certificate in ASCII PEM format and returns the
408 DER-encoded version of it as a byte sequence"""
410 if not pem_cert_string
.startswith(PEM_HEADER
):
411 raise ValueError("Invalid PEM encoding; must start with %s"
413 if not pem_cert_string
.strip().endswith(PEM_FOOTER
):
414 raise ValueError("Invalid PEM encoding; must end with %s"
416 d
= pem_cert_string
.strip()[len(PEM_HEADER
):-len(PEM_FOOTER
)]
417 return base64
.decodestring(d
)
419 def get_server_certificate(addr
, ssl_version
=PROTOCOL_SSLv3
, ca_certs
=None):
421 """Retrieve the certificate from the server at the specified address,
422 and return it as a PEM-encoded string.
423 If 'ca_certs' is specified, validate the server cert against it.
424 If 'ssl_version' is specified, use it in the connection attempt."""
427 if (ca_certs
is not None):
428 cert_reqs
= CERT_REQUIRED
430 cert_reqs
= CERT_NONE
431 s
= wrap_socket(socket(), ssl_version
=ssl_version
,
432 cert_reqs
=cert_reqs
, ca_certs
=ca_certs
)
434 dercert
= s
.getpeercert(True)
436 return DER_cert_to_PEM_cert(dercert
)
438 def get_protocol_name(protocol_code
):
439 return _PROTOCOL_NAMES
.get(protocol_code
, '<unknown>')
442 # a replacement for the old socket.ssl function
444 def sslwrap_simple(sock
, keyfile
=None, certfile
=None):
446 """A replacement for the old socket.ssl function. Designed
447 for compability with Python 2.5 and earlier. Will disappear in
450 if hasattr(sock
, "_sock"):
453 ssl_sock
= _ssl
.sslwrap(sock
, 0, keyfile
, certfile
, CERT_NONE
,
454 PROTOCOL_SSLv23
, None)
458 # no, no connection yet
461 # yes, do the handshake
462 ssl_sock
.do_handshake()