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 SSLError
63 from _ssl
import CERT_NONE
, CERT_OPTIONAL
, CERT_REQUIRED
64 from _ssl
import PROTOCOL_SSLv2
, PROTOCOL_SSLv3
, PROTOCOL_SSLv23
, PROTOCOL_TLSv1
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
77 from socket
import socket
, _fileobject
, error
as socket_error
78 from socket
import getnameinfo
as _getnameinfo
79 import base64
# for DER-to-PEM translation
81 class SSLSocket (socket
):
83 """This class implements a subtype of socket.socket that wraps
84 the underlying OS socket in an SSL context when necessary, and
85 provides read and write methods over that channel."""
87 def __init__(self
, sock
, keyfile
=None, certfile
=None,
88 server_side
=False, cert_reqs
=CERT_NONE
,
89 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
90 do_handshake_on_connect
=True,
91 suppress_ragged_eofs
=True):
92 socket
.__init
__(self
, _sock
=sock
._sock
)
93 # the initializer for socket trashes the methods (tsk, tsk), so...
94 self
.send
= lambda data
, flags
=0: SSLSocket
.send(self
, data
, flags
)
95 self
.sendto
= lambda data
, addr
, flags
=0: SSLSocket
.sendto(self
, data
, addr
, flags
)
96 self
.recv
= lambda buflen
=1024, flags
=0: SSLSocket
.recv(self
, buflen
, flags
)
97 self
.recvfrom
= lambda addr
, buflen
=1024, flags
=0: SSLSocket
.recvfrom(self
, addr
, buflen
, flags
)
98 self
.recv_into
= lambda buffer, nbytes
=None, flags
=0: SSLSocket
.recv_into(self
, buffer, nbytes
, flags
)
99 self
.recvfrom_into
= lambda buffer, nbytes
=None, flags
=0: SSLSocket
.recvfrom_into(self
, buffer, nbytes
, flags
)
101 if certfile
and not keyfile
:
103 # see if it's connected
105 socket
.getpeername(self
)
107 # no, no connection yet
110 # yes, create the SSL object
111 self
._sslobj
= _ssl
.sslwrap(self
._sock
, server_side
,
113 cert_reqs
, ssl_version
, ca_certs
)
114 if do_handshake_on_connect
:
115 timeout
= self
.gettimeout()
117 self
.settimeout(None)
120 self
.settimeout(timeout
)
121 self
.keyfile
= keyfile
122 self
.certfile
= certfile
123 self
.cert_reqs
= cert_reqs
124 self
.ssl_version
= ssl_version
125 self
.ca_certs
= ca_certs
126 self
.do_handshake_on_connect
= do_handshake_on_connect
127 self
.suppress_ragged_eofs
= suppress_ragged_eofs
128 self
._makefile
_refs
= 0
130 def read(self
, len=1024):
132 """Read up to LEN bytes and return them.
133 Return zero-length string on EOF."""
136 return self
._sslobj
.read(len)
138 if x
.args
[0] == SSL_ERROR_EOF
and self
.suppress_ragged_eofs
:
143 def write(self
, data
):
145 """Write DATA to the underlying SSL channel. Returns
146 number of bytes of DATA actually transmitted."""
148 return self
._sslobj
.write(data
)
150 def getpeercert(self
, binary_form
=False):
152 """Returns a formatted version of the data in the
153 certificate provided by the other end of the SSL channel.
154 Return None if no certificate was provided, {} if a
155 certificate was provided, but not validated."""
157 return self
._sslobj
.peer_certificate(binary_form
)
164 return self
._sslobj
.cipher()
166 def send (self
, data
, flags
=0):
170 "non-zero flags not allowed in calls to send() on %s" %
174 v
= self
._sslobj
.write(data
)
176 if x
.args
[0] == SSL_ERROR_WANT_READ
:
178 elif x
.args
[0] == SSL_ERROR_WANT_WRITE
:
185 return socket
.send(self
, data
, flags
)
187 def sendto (self
, data
, addr
, flags
=0):
189 raise ValueError("sendto not allowed on instances of %s" %
192 return socket
.sendto(self
, data
, addr
, flags
)
194 def sendall (self
, data
, flags
=0):
198 "non-zero flags not allowed in calls to sendall() on %s" %
202 while (count
< amount
):
203 v
= self
.send(data
[count
:])
207 return socket
.sendall(self
, data
, flags
)
209 def recv (self
, buflen
=1024, flags
=0):
213 "non-zero flags not allowed in calls to sendall() on %s" %
217 return self
.read(buflen
)
219 if x
.args
[0] == SSL_ERROR_WANT_READ
:
224 return socket
.recv(self
, buflen
, flags
)
226 def recv_into (self
, buffer, nbytes
=None, flags
=0):
227 if buffer and (nbytes
is None):
234 "non-zero flags not allowed in calls to recv_into() on %s" %
238 tmp_buffer
= self
.read(nbytes
)
240 buffer[:v
] = tmp_buffer
242 except SSLError
as x
:
243 if x
.args
[0] == SSL_ERROR_WANT_READ
:
248 return socket
.recv_into(self
, buffer, nbytes
, flags
)
250 def recvfrom (self
, addr
, buflen
=1024, flags
=0):
252 raise ValueError("recvfrom not allowed on instances of %s" %
255 return socket
.recvfrom(self
, addr
, buflen
, flags
)
257 def recvfrom_into (self
, buffer, nbytes
=None, flags
=0):
259 raise ValueError("recvfrom_into not allowed on instances of %s" %
262 return socket
.recvfrom_into(self
, buffer, nbytes
, flags
)
266 return self
._sslobj
.pending()
272 s
= self
._sslobj
.shutdown()
276 raise ValueError("No SSL wrapper around " + str(self
))
278 def shutdown (self
, how
):
280 socket
.shutdown(self
, how
)
283 if self
._makefile
_refs
< 1:
287 self
._makefile
_refs
-= 1
289 def do_handshake (self
):
291 """Perform a TLS/SSL handshake."""
293 self
._sslobj
.do_handshake()
295 def connect(self
, addr
):
297 """Connects to remote ADDR, and then wraps the connection in
300 # Here we assume that the socket is client-side, and not
301 # connected at the time of the call. We connect it, then wrap it.
303 raise ValueError("attempt to connect already-connected SSLSocket!")
304 socket
.connect(self
, addr
)
305 self
._sslobj
= _ssl
.sslwrap(self
._sock
, False, self
.keyfile
, self
.certfile
,
306 self
.cert_reqs
, self
.ssl_version
,
308 if self
.do_handshake_on_connect
:
313 """Accepts a new connection from a remote client, and returns
314 a tuple containing that new connection wrapped with a server-side
315 SSL channel, and the address of the remote client."""
317 newsock
, addr
= socket
.accept(self
)
318 return (SSLSocket(newsock
,
319 keyfile
=self
.keyfile
,
320 certfile
=self
.certfile
,
322 cert_reqs
=self
.cert_reqs
,
323 ssl_version
=self
.ssl_version
,
324 ca_certs
=self
.ca_certs
,
325 do_handshake_on_connect
=self
.do_handshake_on_connect
,
326 suppress_ragged_eofs
=self
.suppress_ragged_eofs
),
329 def makefile(self
, mode
='r', bufsize
=-1):
331 """Make and return a file-like object that
332 works with the SSL connection. Just use the code
333 from the socket module."""
335 self
._makefile
_refs
+= 1
336 return _fileobject(self
, mode
, bufsize
)
340 def wrap_socket(sock
, keyfile
=None, certfile
=None,
341 server_side
=False, cert_reqs
=CERT_NONE
,
342 ssl_version
=PROTOCOL_SSLv23
, ca_certs
=None,
343 do_handshake_on_connect
=True,
344 suppress_ragged_eofs
=True):
346 return SSLSocket(sock
, keyfile
=keyfile
, certfile
=certfile
,
347 server_side
=server_side
, cert_reqs
=cert_reqs
,
348 ssl_version
=ssl_version
, ca_certs
=ca_certs
,
349 do_handshake_on_connect
=do_handshake_on_connect
,
350 suppress_ragged_eofs
=suppress_ragged_eofs
)
353 # some utility functions
355 def cert_time_to_seconds(cert_time
):
357 """Takes a date-time string in standard ASN1_print form
358 ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
359 a Python time value in seconds past the epoch."""
362 return time
.mktime(time
.strptime(cert_time
, "%b %d %H:%M:%S %Y GMT"))
364 PEM_HEADER
= "-----BEGIN CERTIFICATE-----"
365 PEM_FOOTER
= "-----END CERTIFICATE-----"
367 def DER_cert_to_PEM_cert(der_cert_bytes
):
369 """Takes a certificate in binary DER format and returns the
370 PEM version of it as a string."""
372 if hasattr(base64
, 'standard_b64encode'):
373 # preferred because older API gets line-length wrong
374 f
= base64
.standard_b64encode(der_cert_bytes
)
375 return (PEM_HEADER
+ '\n' +
376 textwrap
.fill(f
, 64) +
379 return (PEM_HEADER
+ '\n' +
380 base64
.encodestring(der_cert_bytes
) +
383 def PEM_cert_to_DER_cert(pem_cert_string
):
385 """Takes a certificate in ASCII PEM format and returns the
386 DER-encoded version of it as a byte sequence"""
388 if not pem_cert_string
.startswith(PEM_HEADER
):
389 raise ValueError("Invalid PEM encoding; must start with %s"
391 if not pem_cert_string
.strip().endswith(PEM_FOOTER
):
392 raise ValueError("Invalid PEM encoding; must end with %s"
394 d
= pem_cert_string
.strip()[len(PEM_HEADER
):-len(PEM_FOOTER
)]
395 return base64
.decodestring(d
)
397 def get_server_certificate (addr
, ssl_version
=PROTOCOL_SSLv3
, ca_certs
=None):
399 """Retrieve the certificate from the server at the specified address,
400 and return it as a PEM-encoded string.
401 If 'ca_certs' is specified, validate the server cert against it.
402 If 'ssl_version' is specified, use it in the connection attempt."""
405 if (ca_certs
is not None):
406 cert_reqs
= CERT_REQUIRED
408 cert_reqs
= CERT_NONE
409 s
= wrap_socket(socket(), ssl_version
=ssl_version
,
410 cert_reqs
=cert_reqs
, ca_certs
=ca_certs
)
412 dercert
= s
.getpeercert(True)
414 return DER_cert_to_PEM_cert(dercert
)
416 def get_protocol_name (protocol_code
):
417 if protocol_code
== PROTOCOL_TLSv1
:
419 elif protocol_code
== PROTOCOL_SSLv23
:
421 elif protocol_code
== PROTOCOL_SSLv2
:
423 elif protocol_code
== PROTOCOL_SSLv3
:
429 # a replacement for the old socket.ssl function
431 def sslwrap_simple (sock
, keyfile
=None, certfile
=None):
433 """A replacement for the old socket.ssl function. Designed
434 for compability with Python 2.5 and earlier. Will disappear in
437 if hasattr(sock
, "_sock"):
440 ssl_sock
= _ssl
.sslwrap(sock
, 0, keyfile
, certfile
, CERT_NONE
,
441 PROTOCOL_SSLv23
, None)
445 # no, no connection yet
448 # yes, do the handshake
449 ssl_sock
.do_handshake()