26
votes

In Python, ssl.wrap_socket can read certificates from files, ssl.wrap_socket require the certificate as a file path.

How can I start an SSL connection using a certificate read from string variables?

My host environment does not allow write to files, and tempfile module is not functional
I'm using Python 2.7.
I store the certificate inside MySQL and read as a string.

Edit: I gave up, this is basically require implement ssl by pure python code, this is beyond my current knowledge.

4
Surely your host environment allows you to store SSL certificates, somehow? even if they want to supply them themselves. - user207421
I saved the certificate data in mysql. my code need a ssl socket to another host to retrieve data. I read the certificate from database, but don't know how to create the ssl wrap. - kaala
Looking at the source, ssl.wrap_socket calls directly into the native code (openssl) function SSL_CTX_use_cert_chain_file which requires a path to a file, so what you are trying to do is not possible. You need to write the cert to a file for this to work. - cnelson
Perhaps it is practical to enter all possible CA certificates to a static file, direct SSL to use that file and check what CA was verified against after the connection is established? - Dima Tisnek
I have similar issue when I was developing backend for ios application. I didn't try yet - but maybe you can switch to pyOpenSSL instead of native ssl. For my case I would try this library pypi.python.org/pypi/apns-client/0.2.1 instead of this pypi.python.org/pypi/apns/2.0.1 - alexche8

4 Answers

28
votes

Looking at the source, ssl.wrap_socket calls directly into the native code (openssl) function SSL_CTX_use_cert_chain_file which requires a path to a file, so what you are trying to do is not possible.

For reference:

In ssl/init.py we see:

def wrap_socket(sock, keyfile=None, certfile=None,
                server_side=False, cert_reqs=CERT_NONE,
                ssl_version=PROTOCOL_SSLv23, ca_certs=None,
                do_handshake_on_connect=True):

    return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
                   server_side=server_side, cert_reqs=cert_reqs,
                   ssl_version=ssl_version, ca_certs=ca_certs,
                   do_handshake_on_connect=do_handshake_on_connect)

Points us to the SSLSocket constructor (which is in the same file) and we see the following happen:

self._sslobj = _ssl2.sslwrap(self._sock, server_side,
                                     keyfile, certfile,
                                     cert_reqs, ssl_version, ca_certs)

_ssl2 is implemented in C (_ssl2.c)

Looking at the sslwrap function, we see it's creating a new object:

    return (PyObject *) newPySSLObject(Sock, key_file, cert_file,
                                       server_side, verification_mode,
                                       protocol, cacerts_file);

Looking at the constructor for that object, we eventually see:

            ret = SSL_CTX_use_certificate_chain_file(self->ctx,
                                                     cert_file);

That function is defined in openssl, so now we need to switch to that codebase.

In ssl/ssl_rsa.c we eventually find in the function:

BIO_read_filename(in,file) 

If you dig far enough into the BIO code (part of openssl) you'll eventually come to a normal fopen():

fp=fopen(ptr,p);

So it looks like as it's currently written. It must be in a file openable by C's fopen().

Also, since python's ssl library so quickly jumps into C, I don't see a immediately obvious place to monkeypatch in a workaround either.

5
votes

Quick look though the ssl module source confirms what you want is not supported by the API: http://code.google.com/codesearch#2T6lfGELm_A/trunk/Modules/_ssl.c&q=sslwrap&type=cs

Which is not to say it is impossible, you could create a named pipe, feed one end from Python and give the filename to the ssl module.

For simpler, less secure use, dump cert from memory to mkstemp()'d file.

You could even mount FUSE volume and intercept file callback.

Finally, use ctypes to hack at ssl context at runtime and load cert/ket from a buffer following the C recipe Read certificate files from memory instead of a file using OpenSSL Things like these have been done before, but it's not for the faintest of heart.

It looks like you are trying to get out of e.g. app engine "jail," perhaps it is just not possible?

If you are not picky on ssl implementation, you can use M2Crypto, TLS Lite, pyOpenSSL or something else. The earlier is pure python, you can definitely hack it to use in-memory certificates/keys.

2
votes

From Python 3.4, you can use SSLContext#load_verify_locations:

context = ssl.SSLContext()
context.load_verify_locations(cadata=cert)  # Where cert is str.

From https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_verify_locations

-2
votes

You can treat strings like files with StringIO.