Python SSL to trust self signed certificates [duplicate] - python

I am trying to get Certificate issuer information (Common Name), but the code in the link doesn't work with some URLs.
How can i get Certificate issuer information in python?
import ssl, socket
hostname = 'google.com'
ctx = ssl.create_default_context()
s = ctx.wrap_socket(socket.socket(), server_hostname=hostname)
s.connect((hostname, 443))
cert = s.getpeercert()
subject = dict(x[0] for x in cert['subject'])
issued_to = subject['commonName']
>>> issued_to
u'*.google.com'
For example, I tried hostname "cds.ca", it says
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)
but I could still get the Common name using Internet Explorer (*.cds.ca)
So I think I should use my own certificate(.cer) instead of using getpeercert(), then how should I change that line?
Or, is there any other way to achieve CN with my own certificate file?

If you just want to get the CN or other certificate details no matter if certificate validation succeeds or not, you have to disable the verification. Unfortunately a simple sock.getpeercert() will by design only return an empty dictionary if certificate validation is disabled. That's why one has to use sock.getpeercert(True) to get the binary representation of the certificate and extract the CN using OpenSSL.crypto from it:
import socket
import ssl
import OpenSSL.crypto as crypto
dst = ('cds.ca',443)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(dst)
# upgrade the socket to SSL without checking the certificate
# !!!! don't transfer any sensitive data over this socket !!!!
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(s, server_hostname=dst[0])
# get certificate
cert_bin = s.getpeercert(True)
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1,cert_bin)
print("CN=" + x509.get_subject().CN)

Related

getting error using self certificate verification in python ssl

Getting the following error :
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1045)
I'm using self-signed certificates between many servers, now need to integrate python in the system but unable to verify self-signed certificates.
The code I'm using
context = ssl.create_default_context()
context.load_verify_locations("/var/certs.crt")
context.load_cert_chain(certfile=cert_path, keyfile=key_path)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_REQ
resp = urllib.request.urlopen(url_string, context=ctx)
var/certs.crt containing the certificate of the specific server I'm starting an ssl connection with.
cert_path & key_path are my own cert and private key to establish 2 way ssl.
Things I've checked :
1.I can see my certs being loaded after load_cert_chain in context.get_ca_certs()
2.I tried context.verify_flags |= 0x80000 but it didn't work.
If ctx.verify_mode = False then I'm able to connect properly but it will not be secured.
Since the best existing answer on StackOverflow is to use ctx.verify = False and it's not the way, I'm hoping this time to find someone who actually fixed it.
Thanks
After checking in wireshark I saw that python throwing the wrong error. the problem wasn't with the self certificate but was "Certificate Unknown" and the SSL handshake failed.
So it can be done with ssl.CERT_REQ

How to check OCSP client certificate revocation using Python Requests library?

How do I make a simple request for certificate revocation status to an EJBCA OSCP Responder using the Python requests library?
Example:
# Determine if certificate has been revoked
ocsp_url = req_cert.extensions[2].value[0].access_location.value
ocsp_headers = {"whatGoes: here?"}
ocsp_body = {"What goes here?"}
ocsp_response = requests.get(ocsp_url, ocsp_headers, ocsp_body)
if (ocsp_response == 'revoked'):
return func.HttpResponse(
"Certificate is not valid (Revoked)."
)
Basically it involves the following steps:
retrieve the corresponding cert for a hostname
if a corresponding entry is contained in the certificate, you can query the extensions via AuthorityInformationAccessOID.CA_ISSUERS, which will provide you with a link to the issuer certificate if successful
retrieve the issuer cert with this link
similarly you get via AuthorityInformationAccessOID.OCSP the corresponding OCSP server
with this information about the current cert, the issuer_cert and the ocsp server you can feed OCSPRequestBuilder to create an OCSP request
use requests.get to get the OCSP response
from the OCSP response retrieve the certificate_status
To retrieve a cert for a hostname and port, you can use this fine answer: https://stackoverflow.com/a/49132495. The OCSP handling in Python is documented here: https://cryptography.io/en/latest/x509/ocsp.html.
Code
If you convert the above points into a self-contained example, it looks something like this:
import base64
import ssl
import requests
from urllib.parse import urljoin
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.x509 import ocsp
from cryptography.x509.ocsp import OCSPResponseStatus
from cryptography.x509.oid import ExtensionOID, AuthorityInformationAccessOID
def get_cert_for_hostname(hostname, port):
conn = ssl.create_connection((hostname, port))
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sock = context.wrap_socket(conn, server_hostname=hostname)
certDER = sock.getpeercert(True)
certPEM = ssl.DER_cert_to_PEM_cert(certDER)
return x509.load_pem_x509_certificate(certPEM.encode('ascii'), default_backend())
def get_issuer(cert):
aia = cert.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS).value
issuers = [ia for ia in aia if ia.access_method == AuthorityInformationAccessOID.CA_ISSUERS]
if not issuers:
raise Exception(f'no issuers entry in AIA')
return issuers[0].access_location.value
def get_ocsp_server(cert):
aia = cert.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS).value
ocsps = [ia for ia in aia if ia.access_method == AuthorityInformationAccessOID.OCSP]
if not ocsps:
raise Exception(f'no ocsp server entry in AIA')
return ocsps[0].access_location.value
def get_issuer_cert(ca_issuer):
issuer_response = requests.get(ca_issuer)
if issuer_response.ok:
issuerDER = issuer_response.content
issuerPEM = ssl.DER_cert_to_PEM_cert(issuerDER)
return x509.load_pem_x509_certificate(issuerPEM.encode('ascii'), default_backend())
raise Exception(f'fetching issuer cert failed with response status: {issuer_response.status_code}')
def get_oscp_request(ocsp_server, cert, issuer_cert):
builder = ocsp.OCSPRequestBuilder()
builder = builder.add_certificate(cert, issuer_cert, SHA256())
req = builder.build()
req_path = base64.b64encode(req.public_bytes(serialization.Encoding.DER))
return urljoin(ocsp_server + '/', req_path.decode('ascii'))
def get_ocsp_cert_status(ocsp_server, cert, issuer_cert):
ocsp_resp = requests.get(get_oscp_request(ocsp_server, cert, issuer_cert))
if ocsp_resp.ok:
ocsp_decoded = ocsp.load_der_ocsp_response(ocsp_resp.content)
if ocsp_decoded.response_status == OCSPResponseStatus.SUCCESSFUL:
return ocsp_decoded.certificate_status
else:
raise Exception(f'decoding ocsp response failed: {ocsp_decoded.response_status}')
raise Exception(f'fetching ocsp cert status failed with response status: {ocsp_resp.status_code}')
def get_cert_status_for_host(hostname, port):
print(' hostname:', hostname, "port:", port)
cert = get_cert_for_hostname(hostname, port)
ca_issuer = get_issuer(cert)
print(' issuer ->', ca_issuer)
issuer_cert = get_issuer_cert(ca_issuer)
ocsp_server = get_ocsp_server(cert)
print(' ocsp_server ->', ocsp_server)
return get_ocsp_cert_status(ocsp_server, cert, issuer_cert)
Test 1: Good Certificate
A test call like the following with a good certificate
status = get_cert_status_for_host('software7.com', 443)
print('software7.com:', status, '\n')
results in the following output:
hostname: software7.com port: 443
issuer -> http://cacerts.digicert.com/EncryptionEverywhereDVTLSCA-G1.crt
ocsp_server -> http://ocsp.digicert.com
software7.com: OCSPCertStatus.GOOD
Test 2: Revoked Certificate
Of course you also have to do a counter test with a revoked cert. Here revoked.badssl.com is the first choice:
status = get_cert_status_for_host('revoked.badssl.com', 443)
print('revoked.badssl.com:', status, '\n')
This gives as output:
hostname: revoked.badssl.com port: 443
issuer -> http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
ocsp_server -> http://ocsp.digicert.com
revoked.badssl.com: OCSPCertStatus.REVOKED
AIA Retrieval of the Issuer Certificate
A typical scenario for a certificate relationship looks as follows:
The server provides the server certificate and usually one or more intermediate certificates during the TLS handshake. The word 'usually' is used intentionally: some servers are configured not to deliver intermediate certificates. The browsers then use AIA fetching to build the certification chain.
Up to two entries can be present in the Certificate Authority Information Access extension: The entry for downloading the issuer certificate and the link to the OCSP server.
These entries may also be missing, but a short test script that checks the certs of the 100 most popular servers shows that these entries are usually included in certificates issued by public certification authorities.
The CA Issuers entry may also be missing, but while the information about an OCSP server is available, it can be tested e.g. with OpenSSL using a self-signed certificate:
In this case you would have to determine the issuer certificate from the chain in the TLS handshake, it is the certificate that comes directly after the server certificate in the chain, see also the figure above.
Just for the sake of completeness: There is another case that can sometimes occur especially in conjunction with self-signed certificates: If no intermediate certificates are used, the corresponding root certificate (e.g. available in the local trust store) must be used as issuer certificate.

Unable to connect sockets with SSL certifcate signed by CA

I have a Socket Server running on Ubuntu 18.04 which I am trying to run with SSL/TLS. First I used a self-signed certificate and the code works fine but now I am trying to use a 90-day free SSL certificate signed by ZeroSSL and it doesn't work. Below are the error messages I received on the server and client side upon connection request.
Server side error: ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:852)
Client side error: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1076)
Server Code:
import ssl
import socket
IP = "0.0.0.0"
PORT = 2021
server_cert = "/home/ubuntu/chandral/ssl_socket_test/Certificates/Ubuntu/certificate.crt"
#server_cert = "/home/ubuntu/chandral/ssl_socket_test/Certificates/Ubuntu/ca_bundle.crt"
server_key = "/home/ubuntu/chandral/SSL/server.key"
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile=server_cert, keyfile=server_key)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
secure_server_socket = context.wrap_socket(server_socket, server_side=True)
secure_server_socket.bind((IP, PORT))
secure_server_socket.listen(5)
print("listening for connections")
while True:
client_socket, address = secure_server_socket.accept()
print("IP/URL of client:", address)
Client Code:
import ssl
import socket
IP = "192.168.1.1" # IP changed in this post for confidentiality
PORT = 2021
server_sni_hostname = "example.com" # Domain changed in this post for confidentiality
context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
secure_client_socket = context.wrap_socket(client_socket, server_side=False, server_hostname=server_sni_hostname)
secure_client_socket.connect((IP, PORT))
secure_client_socket.send(b"Hello World")
server_cert = "/home/ubuntu/chandral/ssl_socket_test/Certificates/Ubuntu/certificate.crt"
#server_cert = "/home/ubuntu/chandral/ssl_socket_test/Certificates/Ubuntu/ca_bundle.crt"
ZeroSSL provides three files: certificate.crt is the leaf certificate of the server, private.key the private key (server.key in your code) and ca_bundle.crt the intermediate certificates and the root certificates.
A server must provide both the leaf certificate and all chain certificates so that the client can build the trust chain to the local root CA certificate. To create such setup you need to concatenate the contents of certificate.crt with ca_bundle.crt and use the result with context.load_cert_chain(...). Note that it might be necessary to add an explicite newline after the certificate.crt in case this is missing at the end of the file. Note also that the self-signed root CA (the last certificate in ca_bundle.crt) does not need to be included since it will be ignored by the client anyway and instead the local root CA will be used. It is actually recommended to leave this out.

How to allow python to trust my server's TLS self-signed certificate: ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

This is not a duplicate for this post. I tried the solutions there and nothing works in my case.
I am using Windows and Python 3.6.5. I have a python script for a TLS client. The server I need to connect to uses a self-signed certificate. When I try to connect to it using my script, I get this error:
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
I need to parse the certificate. I tried to add my server's certificate .pem content to file named: cacert.pem which is in: C:\Python36\Lib\site-packages\certifi and run the program again. Nothing change. Here is my scripot. Please help me to make the client make exception for this server as I trust its certificate.
import socket, ssl
import itertools
context = ssl.SSLContext()
context.verify_mode = ssl.CERT_OPTIONAL
context.check_hostname = False
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
domain="192.168.56.3" # my server
ssl_sock = context.wrap_socket(s, server_hostname=domain)
ssl_sock.connect((domain, 443))
print("====== peer's certificate ======")
try:
cert = ssl_sock.getpeercert()
print(cert)
except SSLError as e:
print("Error: ",e)
ssl_sock.close()
This question has been idle for a while, but in case somebody is still struggling with connecting to a server with a self-signed certificate via Python ssl library:
You can use the load_verify_locations method of SSLContext to specify custom self-signed root certs (see Python Docs for load_verify_locations).
The forementioned code could be extended as follows:
...
context = ssl.SSLContext()
context.verify_mode = ssl.CERT_OPTIONAL
context.check_hostname = False
context.load_verify_locations(cafile='/path/to/your/cacert.pem')
...
Be aware that you also need to include public root certs in case you want to connect to other servers with public certificates with the same client/context. (you could for example append your cacert.pem content to the certifi root certificates and reference that folder / path instead).
See also this Python docs paragraph for more information: client-side operations.

Python Failed to Verify any CRLs for SSL/TLS connections

In Python 3.4, a verify_flags that can be used to check if a certificate was revoked against CRL, by set it to VERIFY_CRL_CHECK_LEAF or VERIFY_CRL_CHECK_CHAIN.
I wrote a simple program for testing. But on my systems, this script failed to verify ANY connections even if it's perfectly valid.
import ssl
import socket
def tlscheck(domain, port):
addr = domain
ctx = ssl.create_default_context()
ctx.options &= ssl.CERT_REQUIRED
ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
ctx.check_hostname = True
#ctx.load_default_certs()
#ctx.set_default_verify_paths()
#ctx.load_verify_locations(cafile="/etc/ssl/certs/ca-certificates.crt")
sock = ctx.wrap_socket(socket.socket(), server_hostname=addr)
sock.connect((addr, port))
import pprint
print("TLS Ceritificate:")
pprint.pprint(sock.getpeercert())
print("TLS Version:", sock.version())
print("TLS Cipher:", sock.cipher()[0])
exit()
tlscheck("stackoverflow.com", 443)
My code always quits with ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:645).
First I suspected that the certificate database was not loaded properly. But after I tried load_default_certs(), set_default_verify_paths(), load_verify_locations(cafile="/etc/ssl/certs/ca-certificates.crt"), and none of them worked.
Also, ctx.options &= ssl.CERT_REQUIRED works as expected, it can tell if a certificate chain is trusted or not. But not for CRLs... It also indicates that my CAs are correct.
I know "/etc/ssl/certs/ca-certificates.crt" contains valid CAs. What is the problem?
To check against CRL you have to manually download the CRL and put them in the right place so that the underlying OpenSSL library will find it. There is no automatic downloading of CRL and specifying the place where to look for the CRL is not intuitive either. What you can do:
get the CRL distribution points from the certificate. For stackoverflow.com one is http://crl3.digicert.com/sha2-ha-server-g5.crl
download the current CRL from there
convert it from the DER format to PEM format, because this is what is expected in the next step:
openssl crl -inform der -in sha2-ha-server-g5.crl > sha2-ha-server-g5.crl.pem
add the location to the verify_locations:
ctx.load_verify_locations(cafile="./sha2-ha-server-g5.crl.pem")
This way you can verify the certificate against the CRL.

Categories