Python ssl get ciphers supported - python

I was wondering why isn't there a simple way to find the ciphers supported by a particular ssl context? (Or did I miss it)
I know we can use socket.cipher() to get the one being used for the connection for that particular socket, but why not something to get all supported ciphers?
Another things is,
I have a server which is running openssl library with the cipher string "HIGH+TLSv1.2:!MD5:!SHA1". The client is a python file using the ssl library, with default options, and after the connection is established the socket.cipher() shows the below tuple
('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
How is the connection established with TLSv1, when I explicitly mentioned TLSv1.2, and how is it using SHA384, when TLSv1 doesn't have support for anything higher then SHA1?

I was wondering why isn't there a simple way to find the ciphers supported by a particular ssl context? (Or did I miss it)
Only OpenSSL 1.1.0 added a function SSL_CTX_get_ciphers to access this list and this functionality is not yet available in Python.
('DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256)
How is the connection established with TLSv1, when I explicitly mentioned TLSv1.2, and how is it using SHA384, when TLSv1 doesn't have support for anything higher then SHA1?
According to the source code Python is using SSL_CIPHER_get_version to find out the version string. The matching OpenSSL documentation says for OpenSSL 1.0.2:
SSL_CIPHER_get_version() returns string which indicates the SSL/TLS protocol version that first defined the cipher. This is currently SSLv2 or TLSv1/SSLv3. In some cases it should possibly return "TLSv1.2" but does not; use SSL_CIPHER_description()
Thus it is a bug in OpenSSL which according to the documentation of the same function in OpenSSL 1.1.0 was fixed in the latest OpenSSL version.

Related

How to validate x509 certificates in Python3

Using OpenSSL I can check the validity of user X.509 certificates through the following command:
openssl verify -crl_check -CAfile $CRL_chain_path $PEM_path
with:
$CRL_chain_path: the path of a base64 file containing the trusted certificate chain and the CRL entries
$PEM_path: the path of my base64 certificate to be checked.
How could the same thing be achieved with Python3? So far I'm using the cryptography library. Unfortunately, according to this issue, the functionality is not yet included. Moreover, I would like to avoid bash command usage (subprocess, os, etc.).
This question only covers the chain of trust topic (which is unfortunately not enough). This one checks SSL/TLS sessions, which cannot be used in my context due to the fact that I focus on user PKI public keys.
Is there a simple way with Python to check the validity of X.509 certificates (date ok, chain of trust, no revocation, any other check performed by OpenSSL?, etc.)?

How to establish TLS session in python using PKCS11

I’m trying to establish TLS channel between a client and a web server that are under my control. Both the client and server authenticates themselves using certificates that I’ve created under private PKI scheme. Client key and certificate are stored on usb dongle type HSM. Python is the main application language.
I’m able to do all required crypto operations for my project using python-pkcs11 package such as AES encryption, HMAC signing, RSA signing, and etc. However, I couldn’t find a way to “bind” pkcs11 to any TLS library. What I mean is a “Pythonic” way of calling a function that handles pkcs11 layer and establishes a TLS channel. Requests does not support pkcs11. libcurl has support for pkcs11 but it’s not implemented in pycurl, neither pyopenssl.
I’m able to do it openssl’s s_client CLI tool using engine api:
openssl s_client -engine pkcs11 -verify 2 -CAfile path/to/CA.pem -keyform engine -key "pkcs11:...;object=rsa;type=private" -cert path/to/client-cert.pem -connect localhost:8443
An example of what I’m looking for:
do_tls_with_pkcs(key=’pkcs11:URL’, cert=’cert.pem’, verify=’CA-cert.pem’)
As far as I could search around, no such library exists yet. Now I’m looking for a workaround.
I have read that if openssl, libp11, and python are compiled in such a way it is possible to abstract all of this, hence simple requests calls would go through HSM, transparent to application code. Although, I couldn’t find any material on how to do it.
I faced a similar problem as I wanted to use a PKCS#11 token (YubiKey, PIV applet) along Python requests.
I came up with https://github.com/cedric-dufour/scriptisms/blob/master/misc/m2requests.py
It's imperfect, in the sense that it does not use connections pools and does not support HTTP streams or proxying - like requests's stock HTTPS adapter does - but it does the job for simple connections to backends that require mTLS.

Testing SSL v3 Support in Python

I’m extracting the SSL certificate from a website using the socket + ssl library in python. My understanding that it connects using the preferred method used by the server.
Using this method I’m able to identify what version of SSL is used to connect, but I also need to identify whether the website supports SSL v3, in the case when the default connection is TLS.
Is there a way to identify this information without manually testing multiple SSL connections?
I don't think sites advertise what they support. Rather, it's negotiated between client and server.
You could use the excellent server tester at www.ssllabs.com. It will try lots of configurations and report what the server in question supports. (Hopefully the site doesn't support SSL v3!)

SSL Client Authentication with Python requests

I'm using Python's requests library to perform client side authentication with certificates. The scenario is the following: CA1 has issued a certificate for an intermediate CA (CA2) and CA2 has issued my client's certificate CLIENT. The server I'm connecting to trusts CA1's cert (but does not have CA2's cert). When I use:
requests.get('https:..', cert=('/path/CLIENT.cert', '/path/CLIENT.key'))
I get an error "certificate verify failed". I assumed that's because the server can not retrieve CA2's cert.
However, I'm unable to find a way to send CA2's cert to the server. If I include it in CLIENT, I get an error about private key and cert mismatch. I have also tried to include the chain of certificates in the verify parameter but there does not seem to be any difference on the result (as far as I understand, certs in the verify parameter are used for server side authentication).
Although I think this must be a quite common scenario, I'm unable to find a solution...
PD: If I verify CLIENT's cert with openssl and the full chain of certificates the validation is successful (so there is no problem with the certificates themselves).
Requests recommends using certifi as a CA bundle. Have you tried installing certifi, adding CA1, and passing the certifi bundle path to requests?

Is there a Python API that will return precise cipher information about a TLS connection?

I'm trying to write a python (or Java) program that makes an https connection to a website and then returns properties of the https connection. I've been using python's ssl (http://docs.python.org/2/library/ssl.html), specifically the .cipher() method. My main issue with the output is that it isn't very specific:
('RC4-SHA', 'TLSv1/SSLv3', 128)
This is the output for www.amazon.com. But when I go into my browser and manually examine the connection, I can really see that it is: RC4 with SHA1 message authentication, RSA key exchange and TLS v1.0. In fact, the .cipher() method outputs TLSv1/SSLv3 for TLS versions 1.0, 1.1 and 1.2 and outputs SHA for SHA1 and SHA256.
Is there any Python (or Java) API that will give me more information about the https connection?
The Python ssl module is just a wrapper around OpenSSL, and it can't provide any more information than the library provides. But really, I don't think you're missing any information. It is being specific. You're just always getting TLSv1.0 and SHA1.
First, TLSv1/SSLv3 in OpenSSL 0.9 means TLSv1.0. It cannot mean 1.1 or 1.2, because OpenSSL 0.9 does not support those protocols. (And you are probably using OpenSSL 0.9. For example, the 3.3.0 64-bit Mac binary installer I just got off Python.org uses 0.9.8r.) You can check this from Python with ssl.OPENSSL_VERSION.
Second, RC4-SHA means SHA1, not SHA256. RC4-SHA is just the OpenSSL name for the TLSv1.0 cipher suite TLS_RSA_WITH_RC4_128_SHA. That's a complete specification of the cipher; there are different ciphers with SHA256 on the end of their names. You can see the list of cipher suites specified in RFC 2246 and its addenda for TLS 1.0 (it's RFC4346 for 1.1 and RFC 5246 for 1.2). I don't think the mapping from OpenSSL names to RFC names is specified anywhere except inside the code, but if you have the command-line OpenSSL tools you can type openssl ciphers to dump out the list of OpenSSL names for all suggested cipher suites it will send, and then you can match them up to the values sent in the handshake. (To see the handshake, openssl s_client -connect www.amazon.com:443 -msg, or try -debug or other flags instead of/in addition to -msg.)
So, why does your browser show TLSv1.2 or SHA256 for some of those same sites? Because your browser has a completely different SSL library (or a newer OpenSSL), and therefore does a completely different handshake with the server, and ends up agreeing on different cipher suites, and therefore it reports different information.
So, it's not that Python is negotiating TLSv1.2 or SHA256 and just hiding that from you; it's negotiating TLSv1.0 and SHA1 with the same server, and telling you what it's done.
If you want to use a different library that can handle things OpenSSL 0.9 can't, there are lots of choices. If you install OpenSSL 1.0.1 or later and build PyOpenSSL against that, I believe (I haven't tested) that you should be able to negotiate newer protocols and ciphers and find out that you've done so. It might even rework if you rebuild Python, or the standalone ssl module, against it. (If neither of those works, there are a zillion more OpenSSL wrappers and other SSL or TLS modules at PyPI, or you can just ctypes your favorite yourself.)

Categories