How to validate x509 certificates in Python3 - python

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.)?

Related

Can exe file Install pfx file in Windows Certificate Store With Python

I use Self Code sign Certificate and sign exe file using Windows SDK signtool.exe through PowerShell.
Exe file successfully signed. But for other systems This Sign Doesn't work. Because other system Doesn't have my
Code Sign Certificate in form of pfx file.
So what I do For that:
I have two options : Through the help of python script which includes an exe file. that performs a pfx file install in Windows certificate Store at time of exe file run as administrator and i get Publisher Name.
But how this thing is possible !!
Or Suggest Other ways to do this.
Simply I ask Self Sign Code Certificate run in all system.
Thank you.
Certificates are validated through trust certificate chain. The trust chain of the leading code signing providers are already present as a part of OS. Thus they don't require to distribute their certificates.
If at all, you want to use your own certificate (irrespective of which OS will trust it), you may create your own private CA (google for it) and then use your private CA certificate to sign your code signing certificate. You may distribute certificate of your private CA (certificate don't have private keys... contains only public keys...).
It's not good idea to distribute your pfx since it also contains private key and anybody can use the certificate which is again stealing your identity...
BTW, Certifying Authorities issuing code signing certificates, has some cost verifying your identity and that is the reason they are charging the amount (for some countries it becomes large amount though...!!)
I have found another way to do it. I created the setup of the executable file using Inno Setup Creator and after installation on the system it stopped displaying blue screen that this is an unrecognized program.

Use multiple CA certs with python requests

I'm in a corporate network and need to use a self-signed certificate for the requests library
in a docker image.
I installed it by putting it in /usr/local/shares/ca-certificates and calling update-ca-certificates like this:
COPY EDAG_Bundle.crt /usr/local/share/ca-certificates/my_cert.crt
RUN update-ca-certificates
ENV REQUESTS_CA_BUNDLE /usr/local/share/ca-certificates/my_cert.crt
Now I am able to access files on a Server in our corporate network without running in a certificate error.
Unfortunately this change caused pip to stop working. As pip is using requests too, it also now uses the self signed certificate instead of the one from certifi.
The requests documentation states the following:
You can pass verify the path to a CA_BUNDLE file with certificates of trusted CAs:
requests.get('https://github.com', verify='/path/to/certfile')
This list of trusted CAs can also be specified through the REQUESTS_CA_BUNDLE environment variable.
As I get this, I can define a List of trusted CAs, not just one.
How can I configure requests to use both CAs? (my self signed one and the one of certifi located in
/site-packages/certifi/cacert.pem).
Setting both in the environment variable by seperating the paths with a colon does not work.
Use /etc/ssl/certs/ca-certificates.crt as your REQUESTS_CA_BUNDLE.
requests.get('https://github.com', verify='/etc/ssl/certs/ca-certificates.crt')
When you put a self-issued CA certificate to /usr/local/shares/ca-certificates, then run update-ca-certificates, it will read those in and append to the global "ca trust file" (ca-certificates.crt). This will hold trust for both publicly trusted and your self-installed CA-s.
Note: Debuan/Ubuntu systems, CentOS/Alpine probably have this in a different location (ref).

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.)

https handshake with keystores in Python

I have an web server set up that denies connections without a valid .p12 certificate. I need to access a REST API that is running on the server in a Python script, but I can't find anything about how to do it. If anyone has a good tutorial on how to perform an SSL handshake using .p12 certificates in Python, please let me know.
The same methods described in the answers to this question, which asks about verifying a server certificate during the HTTPS connection (this is not done at all by default by urllib or httplib) should allow you to specify a client-certificate in addition to the CA certificate lists.
If you choose the option based on ssl.wrap_socket, pass a cerfile/keyfile parameter as described in the documentation.
Using PycURL, you should be able to call setopt(pycurl.SSLCERT, "/path/to/cert.pem") and setopt(pycurl.SSLKEY, "/path/to/key.pem"). The option names are based on the SSL and SECURITY OPTIONS section of the cURL documentation (there's an option for the password too).
It's likely that you will have to convert your PKCS#12 (.p12) file into PEM format. To do so:
# Extract the certificate:
openssl pkcs12 -in filename.p12 -nokeys -out certificate.pem
# Extract the private key:
openssl pkcs12 -in filename.p12 -nocerts -out privkey.pem

Categories