How to open ssl socket using certificate stored in

2019-01-09 05:15发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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



回答4:

You can treat strings like files with StringIO.