ssl.py 23.8 KB
Newer Older
1 2 3
# Wrapper module for _ssl, providing some additional facilities
# implemented in Python.  Written by Bill Janssen.

4
"""This module provides some more Pythonic support for SSL.
5 6 7

Object types:

8
  SSLSocket -- subtype of socket.socket which does SSL over the socket
9 10 11

Exceptions:

12
  SSLError -- exception raised for I/O errors
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

Functions:

  cert_time_to_seconds -- convert time string used for certificate
                          notBefore and notAfter functions to integer
                          seconds past the Epoch (the time values
                          returned from time.time())

  fetch_server_certificate (HOST, PORT) -- fetch the certificate provided
                          by the server running on HOST at port PORT.  No
                          validation of the certificate is performed.

Integer constants:

SSL_ERROR_ZERO_RETURN
SSL_ERROR_WANT_READ
SSL_ERROR_WANT_WRITE
SSL_ERROR_WANT_X509_LOOKUP
SSL_ERROR_SYSCALL
SSL_ERROR_SSL
SSL_ERROR_WANT_CONNECT

SSL_ERROR_EOF
SSL_ERROR_INVALID_ERROR_CODE

The following group define certificate requirements that one side is
allowing/requiring from the other side:

CERT_NONE - no certificates from the other side are required (or will
            be looked at if provided)
CERT_OPTIONAL - certificates are not required, but if provided will be
                validated, and if validation fails, the connection will
                also fail
CERT_REQUIRED - certificates are required, and will be validated, and
                if validation fails, the connection will also fail

The following constants identify various SSL protocol variants:

PROTOCOL_SSLv2
PROTOCOL_SSLv3
PROTOCOL_SSLv23
PROTOCOL_TLSv1
"""

57
import textwrap
58
import re
59 60

import _ssl             # if we can't import it, let the error propagate
61

62
from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
63 64 65 66 67
from _ssl import _SSLContext
from _ssl import (
    SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
    SSLSyscallError, SSLEOFError,
    )
68
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
69 70
from _ssl import (
    OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1,
71
    OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE
72
    )
73 74 75 76
try:
    from _ssl import OP_NO_COMPRESSION
except ImportError:
    pass
77 78 79 80
try:
    from _ssl import OP_SINGLE_ECDH_USE
except ImportError:
    pass
81
from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
82 83 84 85 86 87 88 89 90 91 92
from _ssl import (
    SSL_ERROR_ZERO_RETURN,
    SSL_ERROR_WANT_READ,
    SSL_ERROR_WANT_WRITE,
    SSL_ERROR_WANT_X509_LOOKUP,
    SSL_ERROR_SYSCALL,
    SSL_ERROR_SSL,
    SSL_ERROR_WANT_CONNECT,
    SSL_ERROR_EOF,
    SSL_ERROR_INVALID_ERROR_CODE,
    )
93
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN
94 95
from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23,
                  PROTOCOL_TLSv1)
96 97
from _ssl import _OPENSSL_API_VERSION

98 99 100 101 102 103 104
_PROTOCOL_NAMES = {
    PROTOCOL_TLSv1: "TLSv1",
    PROTOCOL_SSLv23: "SSLv23",
    PROTOCOL_SSLv3: "SSLv3",
}
try:
    from _ssl import PROTOCOL_SSLv2
105
    _SSLv2_IF_EXISTS = PROTOCOL_SSLv2
106
except ImportError:
107
    _SSLv2_IF_EXISTS = None
108 109
else:
    _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2"
110 111

from socket import getnameinfo as _getnameinfo
Bill Janssen's avatar
Bill Janssen committed
112
from socket import error as socket_error
113
from socket import socket, AF_INET, SOCK_STREAM, create_connection
114
from socket import SOL_SOCKET, SO_TYPE
115
import base64        # for DER-to-PEM translation
116
import traceback
117
import errno
118

119 120 121 122
if _ssl.HAS_TLS_UNIQUE:
    CHANNEL_BINDING_TYPES = ['tls-unique']
else:
    CHANNEL_BINDING_TYPES = []
123

124 125 126 127
# Disable weak or insecure ciphers by default
# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
_DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'

128

129 130 131 132
class CertificateError(ValueError):
    pass


133 134 135 136 137
def _dnsname_match(dn, hostname, max_wildcards=1):
    """Matching according to RFC 6125, section 6.4.3

    http://tools.ietf.org/html/rfc6125#section-6.4.3
    """
138
    pats = []
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    if not dn:
        return False

    leftmost, *remainder = dn.split(r'.')

    wildcards = leftmost.count('*')
    if wildcards > max_wildcards:
        # Issue #17980: avoid denials of service by refusing more
        # than one wildcard per fragment.  A survery of established
        # policy among SSL implementations showed it to be a
        # reasonable choice.
        raise CertificateError(
            "too many wildcards in certificate DNS name: " + repr(dn))

    # speed up common case w/o wildcards
    if not wildcards:
        return dn.lower() == hostname.lower()

    # RFC 6125, section 6.4.3, subitem 1.
    # The client SHOULD NOT attempt to match a presented identifier in which
    # the wildcard character comprises a label other than the left-most label.
    if leftmost == '*':
        # When '*' is a fragment by itself, it matches a non-empty dotless
        # fragment.
        pats.append('[^.]+')
    elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
        # RFC 6125, section 6.4.3, subitem 3.
        # The client SHOULD NOT attempt to match a presented identifier
        # where the wildcard character is embedded within an A-label or
        # U-label of an internationalized domain name.
        pats.append(re.escape(leftmost))
    else:
        # Otherwise, '*' matches any dotless string, e.g. www*
        pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))

    # add the remaining fragments, ignore any wildcards
    for frag in remainder:
        pats.append(re.escape(frag))

    pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
    return pat.match(hostname)
180 181 182 183


def match_hostname(cert, hostname):
    """Verify that *cert* (in decoded format as returned by
184 185
    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125
    rules are followed, but IP addresses are not accepted for *hostname*.
186 187 188 189 190 191 192 193 194 195

    CertificateError is raised on failure. On success, the function
    returns nothing.
    """
    if not cert:
        raise ValueError("empty or no certificate")
    dnsnames = []
    san = cert.get('subjectAltName', ())
    for key, value in san:
        if key == 'DNS':
196
            if _dnsname_match(value, hostname):
197 198
                return
            dnsnames.append(value)
199 200 201
    if not dnsnames:
        # The subject is only checked when there is no dNSName entry
        # in subjectAltName
202 203 204 205 206
        for sub in cert.get('subject', ()):
            for key, value in sub:
                # XXX according to RFC 2818, the most specific Common Name
                # must be used.
                if key == 'commonName':
207
                    if _dnsname_match(value, hostname):
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
                        return
                    dnsnames.append(value)
    if len(dnsnames) > 1:
        raise CertificateError("hostname %r "
            "doesn't match either of %s"
            % (hostname, ', '.join(map(repr, dnsnames))))
    elif len(dnsnames) == 1:
        raise CertificateError("hostname %r "
            "doesn't match %r"
            % (hostname, dnsnames[0]))
    else:
        raise CertificateError("no appropriate commonName or "
            "subjectAltName fields were found")


223 224 225 226 227 228 229
class SSLContext(_SSLContext):
    """An SSLContext holds various SSL-related configuration options and
    data, such as certificates and possibly a private key."""

    __slots__ = ('protocol',)

    def __new__(cls, protocol, *args, **kwargs):
230 231 232 233
        self = _SSLContext.__new__(cls, protocol)
        if protocol != _SSLv2_IF_EXISTS:
            self.set_ciphers(_DEFAULT_CIPHERS)
        return self
234 235 236 237 238 239

    def __init__(self, protocol):
        self.protocol = protocol

    def wrap_socket(self, sock, server_side=False,
                    do_handshake_on_connect=True,
240 241
                    suppress_ragged_eofs=True,
                    server_hostname=None):
242 243 244
        return SSLSocket(sock=sock, server_side=server_side,
                         do_handshake_on_connect=do_handshake_on_connect,
                         suppress_ragged_eofs=suppress_ragged_eofs,
245
                         server_hostname=server_hostname,
246 247
                         _context=self)

248 249 250 251 252 253 254 255 256 257 258
    def set_npn_protocols(self, npn_protocols):
        protos = bytearray()
        for protocol in npn_protocols:
            b = bytes(protocol, 'ascii')
            if len(b) == 0 or len(b) > 255:
                raise SSLError('NPN protocols must be 1 to 255 in length')
            protos.append(len(b))
            protos.extend(b)

        self._set_npn_protocols(protos)

259 260

class SSLSocket(socket):
261 262 263 264
    """This class implements a subtype of socket.socket that wraps
    the underlying OS socket in an SSL context when necessary, and
    provides read and write methods over that channel."""

Bill Janssen's avatar
Bill Janssen committed
265
    def __init__(self, sock=None, keyfile=None, certfile=None,
266
                 server_side=False, cert_reqs=CERT_NONE,
Bill Janssen's avatar
Bill Janssen committed
267 268 269
                 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
                 do_handshake_on_connect=True,
                 family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
270
                 suppress_ragged_eofs=True, npn_protocols=None, ciphers=None,
271
                 server_hostname=None,
272
                 _context=None):
Bill Janssen's avatar
Bill Janssen committed
273

274 275 276
        if _context:
            self.context = _context
        else:
277 278 279
            if server_side and not certfile:
                raise ValueError("certfile must be specified for server-side "
                                 "operations")
280 281
            if keyfile and not certfile:
                raise ValueError("certfile must be specified")
282 283 284 285 286 287 288 289
            if certfile and not keyfile:
                keyfile = certfile
            self.context = SSLContext(ssl_version)
            self.context.verify_mode = cert_reqs
            if ca_certs:
                self.context.load_verify_locations(ca_certs)
            if certfile:
                self.context.load_cert_chain(certfile, keyfile)
290 291
            if npn_protocols:
                self.context.set_npn_protocols(npn_protocols)
292 293 294 295 296 297 298 299
            if ciphers:
                self.context.set_ciphers(ciphers)
            self.keyfile = keyfile
            self.certfile = certfile
            self.cert_reqs = cert_reqs
            self.ssl_version = ssl_version
            self.ca_certs = ca_certs
            self.ciphers = ciphers
300 301 302 303
        # Can't use sock.type as other flags (such as SOCK_NONBLOCK) get
        # mixed in.
        if sock.getsockopt(SOL_SOCKET, SO_TYPE) != SOCK_STREAM:
            raise NotImplementedError("only stream sockets are supported")
304 305 306
        if server_side and server_hostname:
            raise ValueError("server_hostname can only be specified "
                             "in client mode")
307
        self.server_side = server_side
308
        self.server_hostname = server_hostname
309 310
        self.do_handshake_on_connect = do_handshake_on_connect
        self.suppress_ragged_eofs = suppress_ragged_eofs
311
        connected = False
Bill Janssen's avatar
Bill Janssen committed
312
        if sock is not None:
313 314 315 316
            socket.__init__(self,
                            family=sock.family,
                            type=sock.type,
                            proto=sock.proto,
317
                            fileno=sock.fileno())
318
            self.settimeout(sock.gettimeout())
319 320 321 322 323 324 325 326
            # see if it's connected
            try:
                sock.getpeername()
            except socket_error as e:
                if e.errno != errno.ENOTCONN:
                    raise
            else:
                connected = True
327
            sock.detach()
Bill Janssen's avatar
Bill Janssen committed
328 329 330 331 332
        elif fileno is not None:
            socket.__init__(self, fileno=fileno)
        else:
            socket.__init__(self, family=family, type=type, proto=proto)

333 334
        self._closed = False
        self._sslobj = None
335
        self._connected = connected
336 337
        if connected:
            # create the SSL object
Bill Janssen's avatar
Bill Janssen committed
338
            try:
339 340
                self._sslobj = self.context._wrap_socket(self, server_side,
                                                         server_hostname)
Bill Janssen's avatar
Bill Janssen committed
341
                if do_handshake_on_connect:
342 343 344 345
                    timeout = self.gettimeout()
                    if timeout == 0.0:
                        # non-blocking
                        raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
Bill Janssen's avatar
Bill Janssen committed
346
                    self.do_handshake()
347

Bill Janssen's avatar
Bill Janssen committed
348 349 350 351
            except socket_error as x:
                self.close()
                raise x

352 353 354 355
    def dup(self):
        raise NotImplemented("Can't dup() %s instances" %
                             self.__class__.__name__)

Bill Janssen's avatar
Bill Janssen committed
356 357 358
    def _checkClosed(self, msg=None):
        # raise an exception here if you wish to check for spurious closes
        pass
359

360
    def read(self, len=0, buffer=None):
361 362 363
        """Read up to LEN bytes and return them.
        Return zero-length string on EOF."""

Bill Janssen's avatar
Bill Janssen committed
364 365
        self._checkClosed()
        try:
366 367
            if buffer is not None:
                v = self._sslobj.read(len, buffer)
Bill Janssen's avatar
Bill Janssen committed
368
            else:
369 370
                v = self._sslobj.read(len or 1024)
            return v
Bill Janssen's avatar
Bill Janssen committed
371 372
        except SSLError as x:
            if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
373
                if buffer is not None:
374 375 376
                    return 0
                else:
                    return b''
Bill Janssen's avatar
Bill Janssen committed
377 378
            else:
                raise
379 380

    def write(self, data):
381 382 383
        """Write DATA to the underlying SSL channel.  Returns
        number of bytes of DATA actually transmitted."""

Bill Janssen's avatar
Bill Janssen committed
384
        self._checkClosed()
385 386
        return self._sslobj.write(data)

387
    def getpeercert(self, binary_form=False):
388 389 390 391 392
        """Returns a formatted version of the data in the
        certificate provided by the other end of the SSL channel.
        Return None if no certificate was provided, {} if a
        certificate was provided, but not validated."""

Bill Janssen's avatar
Bill Janssen committed
393
        self._checkClosed()
394 395
        return self._sslobj.peer_certificate(binary_form)

396 397 398 399 400 401 402
    def selected_npn_protocol(self):
        self._checkClosed()
        if not self._sslobj or not _ssl.HAS_NPN:
            return None
        else:
            return self._sslobj.selected_npn_protocol()

403
    def cipher(self):
Bill Janssen's avatar
Bill Janssen committed
404
        self._checkClosed()
405 406 407 408
        if not self._sslobj:
            return None
        else:
            return self._sslobj.cipher()
409

410 411 412 413 414 415 416
    def compression(self):
        self._checkClosed()
        if not self._sslobj:
            return None
        else:
            return self._sslobj.compression()

417
    def send(self, data, flags=0):
Bill Janssen's avatar
Bill Janssen committed
418
        self._checkClosed()
419 420 421 422 423
        if self._sslobj:
            if flags != 0:
                raise ValueError(
                    "non-zero flags not allowed in calls to send() on %s" %
                    self.__class__)
Bill Janssen's avatar
Bill Janssen committed
424 425 426 427 428 429 430 431 432 433 434 435
            while True:
                try:
                    v = self._sslobj.write(data)
                except SSLError as x:
                    if x.args[0] == SSL_ERROR_WANT_READ:
                        return 0
                    elif x.args[0] == SSL_ERROR_WANT_WRITE:
                        return 0
                    else:
                        raise
                else:
                    return v
436 437
        else:
            return socket.send(self, data, flags)
438

439
    def sendto(self, data, flags_or_addr, addr=None):
Bill Janssen's avatar
Bill Janssen committed
440
        self._checkClosed()
441
        if self._sslobj:
442
            raise ValueError("sendto not allowed on instances of %s" %
443
                             self.__class__)
444 445
        elif addr is None:
            return socket.sendto(self, data, flags_or_addr)
446
        else:
447
            return socket.sendto(self, data, flags_or_addr, addr)
448

449 450 451 452 453 454
    def sendmsg(self, *args, **kwargs):
        # Ensure programs don't send data unencrypted if they try to
        # use this method.
        raise NotImplementedError("sendmsg not allowed on instances of %s" %
                                  self.__class__)

455
    def sendall(self, data, flags=0):
Bill Janssen's avatar
Bill Janssen committed
456
        self._checkClosed()
457
        if self._sslobj:
458 459 460 461
            if flags != 0:
                raise ValueError(
                    "non-zero flags not allowed in calls to sendall() on %s" %
                    self.__class__)
Bill Janssen's avatar
Bill Janssen committed
462 463 464 465 466 467
            amount = len(data)
            count = 0
            while (count < amount):
                v = self.send(data[count:])
                count += v
            return amount
468 469
        else:
            return socket.sendall(self, data, flags)
470

471
    def recv(self, buflen=1024, flags=0):
Bill Janssen's avatar
Bill Janssen committed
472
        self._checkClosed()
473 474 475
        if self._sslobj:
            if flags != 0:
                raise ValueError(
476 477 478
                    "non-zero flags not allowed in calls to recv() on %s" %
                    self.__class__)
            return self.read(buflen)
479 480
        else:
            return socket.recv(self, buflen, flags)
481

482
    def recv_into(self, buffer, nbytes=None, flags=0):
Bill Janssen's avatar
Bill Janssen committed
483 484 485 486 487 488 489 490
        self._checkClosed()
        if buffer and (nbytes is None):
            nbytes = len(buffer)
        elif nbytes is None:
            nbytes = 1024
        if self._sslobj:
            if flags != 0:
                raise ValueError(
491 492
                  "non-zero flags not allowed in calls to recv_into() on %s" %
                  self.__class__)
493
            return self.read(nbytes, buffer)
Bill Janssen's avatar
Bill Janssen committed
494 495 496
        else:
            return socket.recv_into(self, buffer, nbytes, flags)

497
    def recvfrom(self, buflen=1024, flags=0):
Bill Janssen's avatar
Bill Janssen committed
498
        self._checkClosed()
499
        if self._sslobj:
500
            raise ValueError("recvfrom not allowed on instances of %s" %
501 502
                             self.__class__)
        else:
503
            return socket.recvfrom(self, buflen, flags)
504

505 506 507 508 509 510 511 512
    def recvfrom_into(self, buffer, nbytes=None, flags=0):
        self._checkClosed()
        if self._sslobj:
            raise ValueError("recvfrom_into not allowed on instances of %s" %
                             self.__class__)
        else:
            return socket.recvfrom_into(self, buffer, nbytes, flags)

513 514 515 516 517 518 519 520
    def recvmsg(self, *args, **kwargs):
        raise NotImplementedError("recvmsg not allowed on instances of %s" %
                                  self.__class__)

    def recvmsg_into(self, *args, **kwargs):
        raise NotImplementedError("recvmsg_into not allowed on instances of "
                                  "%s" % self.__class__)

521
    def pending(self):
Bill Janssen's avatar
Bill Janssen committed
522 523 524 525 526 527
        self._checkClosed()
        if self._sslobj:
            return self._sslobj.pending()
        else:
            return 0

528
    def shutdown(self, how):
Bill Janssen's avatar
Bill Janssen committed
529
        self._checkClosed()
530
        self._sslobj = None
531
        socket.shutdown(self, how)
532

533
    def unwrap(self):
534 535 536 537 538 539 540
        if self._sslobj:
            s = self._sslobj.shutdown()
            self._sslobj = None
            return s
        else:
            raise ValueError("No SSL wrapper around " + str(self))

541
    def _real_close(self):
542
        self._sslobj = None
Bill Janssen's avatar
Bill Janssen committed
543
        # self._closed = True
544
        socket._real_close(self)
Bill Janssen's avatar
Bill Janssen committed
545

546
    def do_handshake(self, block=False):
Bill Janssen's avatar
Bill Janssen committed
547 548
        """Perform a TLS/SSL handshake."""

549
        timeout = self.gettimeout()
Bill Janssen's avatar
Bill Janssen committed
550
        try:
551 552
            if timeout == 0.0 and block:
                self.settimeout(None)
Bill Janssen's avatar
Bill Janssen committed
553
            self._sslobj.do_handshake()
554 555
        finally:
            self.settimeout(timeout)
556

557
    def _real_connect(self, addr, connect_ex):
558 559
        if self.server_side:
            raise ValueError("can't connect in server-side mode")
560 561
        # Here we assume that the socket is client-side, and not
        # connected at the time of the call.  We connect it, then wrap it.
562
        if self._connected:
563
            raise ValueError("attempt to connect already-connected SSLSocket!")
564
        self._sslobj = self.context._wrap_socket(self, False, self.server_hostname)
565
        try:
566 567
            if connect_ex:
                rc = socket.connect_ex(self, addr)
568
            else:
569 570 571 572 573 574 575 576 577 578
                rc = None
                socket.connect(self, addr)
            if not rc:
                if self.do_handshake_on_connect:
                    self.do_handshake()
                self._connected = True
            return rc
        except socket_error:
            self._sslobj = None
            raise
579 580 581 582 583 584 585 586 587 588

    def connect(self, addr):
        """Connects to remote ADDR, and then wraps the connection in
        an SSL channel."""
        self._real_connect(addr, False)

    def connect_ex(self, addr):
        """Connects to remote ADDR, and then wraps the connection in
        an SSL channel."""
        return self._real_connect(addr, True)
589 590

    def accept(self):
591 592 593 594 595
        """Accepts a new connection from a remote client, and returns
        a tuple containing that new connection wrapped with a server-side
        SSL channel, and the address of the remote client."""

        newsock, addr = socket.accept(self)
596 597 598 599 600
        newsock = self.context.wrap_socket(newsock,
                    do_handshake_on_connect=self.do_handshake_on_connect,
                    suppress_ragged_eofs=self.suppress_ragged_eofs,
                    server_side=True)
        return newsock, addr
601

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    def get_channel_binding(self, cb_type="tls-unique"):
        """Get channel binding data for current connection.  Raise ValueError
        if the requested `cb_type` is not supported.  Return bytes of the data
        or None if the data is not available (e.g. before the handshake).
        """
        if cb_type not in CHANNEL_BINDING_TYPES:
            raise ValueError("Unsupported channel binding type")
        if cb_type != "tls-unique":
            raise NotImplementedError(
                            "{0} channel binding type not implemented"
                            .format(cb_type))
        if self._sslobj is None:
            return None
        return self._sslobj.tls_unique_cb()

617

618 619
def wrap_socket(sock, keyfile=None, certfile=None,
                server_side=False, cert_reqs=CERT_NONE,
Bill Janssen's avatar
Bill Janssen committed
620
                ssl_version=PROTOCOL_SSLv23, ca_certs=None,
621
                do_handshake_on_connect=True,
622 623
                suppress_ragged_eofs=True,
                ciphers=None):
624

Bill Janssen's avatar
Bill Janssen committed
625
    return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile,
626
                     server_side=server_side, cert_reqs=cert_reqs,
Bill Janssen's avatar
Bill Janssen committed
627
                     ssl_version=ssl_version, ca_certs=ca_certs,
628
                     do_handshake_on_connect=do_handshake_on_connect,
629 630
                     suppress_ragged_eofs=suppress_ragged_eofs,
                     ciphers=ciphers)
631

632 633 634
# some utility functions

def cert_time_to_seconds(cert_time):
635 636 637 638
    """Takes a date-time string in standard ASN1_print form
    ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return
    a Python time value in seconds past the epoch."""

639 640 641
    import time
    return time.mktime(time.strptime(cert_time, "%b %d %H:%M:%S %Y GMT"))

642 643 644 645 646 647 648
PEM_HEADER = "-----BEGIN CERTIFICATE-----"
PEM_FOOTER = "-----END CERTIFICATE-----"

def DER_cert_to_PEM_cert(der_cert_bytes):
    """Takes a certificate in binary DER format and returns the
    PEM version of it as a string."""

Bill Janssen's avatar
Bill Janssen committed
649 650 651 652
    f = str(base64.standard_b64encode(der_cert_bytes), 'ASCII', 'strict')
    return (PEM_HEADER + '\n' +
            textwrap.fill(f, 64) + '\n' +
            PEM_FOOTER + '\n')
653 654 655 656 657 658 659 660 661 662 663 664

def PEM_cert_to_DER_cert(pem_cert_string):
    """Takes a certificate in ASCII PEM format and returns the
    DER-encoded version of it as a byte sequence"""

    if not pem_cert_string.startswith(PEM_HEADER):
        raise ValueError("Invalid PEM encoding; must start with %s"
                         % PEM_HEADER)
    if not pem_cert_string.strip().endswith(PEM_FOOTER):
        raise ValueError("Invalid PEM encoding; must end with %s"
                         % PEM_FOOTER)
    d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
Georg Brandl's avatar
Georg Brandl committed
665
    return base64.decodebytes(d.encode('ASCII', 'strict'))
666

667
def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
668 669 670 671 672 673 674 675 676 677
    """Retrieve the certificate from the server at the specified address,
    and return it as a PEM-encoded string.
    If 'ca_certs' is specified, validate the server cert against it.
    If 'ssl_version' is specified, use it in the connection attempt."""

    host, port = addr
    if (ca_certs is not None):
        cert_reqs = CERT_REQUIRED
    else:
        cert_reqs = CERT_NONE
678 679
    s = create_connection(addr)
    s = wrap_socket(s, ssl_version=ssl_version,
680 681 682 683 684
                    cert_reqs=cert_reqs, ca_certs=ca_certs)
    dercert = s.getpeercert(True)
    s.close()
    return DER_cert_to_PEM_cert(dercert)

685
def get_protocol_name(protocol_code):
686
    return _PROTOCOL_NAMES.get(protocol_code, '<unknown>')