Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ssl.get_server_certificate for sites with SNI (Server Name Indication)

I am trying to get the server certificate of badssl.com subdomains (ex. https://expired.badssl.com).

import ssl
ssl.get_server_certificate(('expired.badssl.com', 443))

But when examining the above generated certificate I see that the certificate has

Identity: badssl-fallback-unknown-subdomain-or-no-sni

which means SNI is failing. How can I get the server certificate of different subdomains of badssl.com? (I am using python 2.7.12)

like image 521
vishal Avatar asked Mar 06 '18 13:03

vishal


People also ask

What is SNI in SSL certificate?

SNI is an extension for the TLS protocol (formerly known as the SSL protocol), which is used in HTTPS. It's included in the TLS/SSL handshake process in order to ensure that client devices are able to see the correct SSL certificate for the website they are trying to reach.

Why do we need server name indication SNI )?

Server Name Indication (SNI) is an extension to the TLS protocol. It allows a client or browser to indicate which hostname it is trying to connect to at the start of the TLS handshake. This allows the server to present multiple certificates on the same IP address and port number.

What is SNI on a site?

Server Name Indication, often abbreviated SNI, is an extension to TLS that allows multiple hostnames to be served over HTTPS from the same IP address.

How do I activate my SNI?

To enable SNI support, add the notes. ini setting ENABLE_SNI=1 to the server and restart the HTTP task. Note the following things to consider before enabling SNI support: SNI is supported only for inbound HTTP requests.


2 Answers

Found the answer.

import ssl
hostname = "expired.badssl.com"
port = 443
conn = ssl.create_connection((hostname, port))
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sock = context.wrap_socket(conn, server_hostname=hostname)
certificate = ssl.DER_cert_to_PEM_cert(sock.getpeercert(True))
like image 151
vishal Avatar answered Oct 07 '22 01:10

vishal


Searching for "Python ssl.get_server_certificate SNI" brought me easily to this answer. Although the OP himself answer is correct, I would like to provide a little more insight for future reference.

With some [hostname]s the fallowing call using Python 3.7:

ssl.get_server_certificate(("example.com", 443)

will complain with a traceback that ends with:

ssl.SSLError: [SSL: TLSV1_ALERT_INTERNAL_ERROR] tlsv1 alert internal error (_ssl.c:1045)

Doing some further investigation, making use of the openssl s_client utility, allows to discover that those same [hostname]s which made get_server_certificate to fail, also makes the fallowing command:

openssl s_client -showcerts -connect example.com:443

to fail with this error:

SSL23_GET_SERVER_HELLO:tlsv1 alert internal error:s23_clnt.c:802

Note that the error message is similar to the one returned by the python code.

Using the -servername switch did the trick:

openssl s_client -showcerts -connect example.com:443 -servername example.com

leading to the conclusion that the investigated hostname refers to a secure server that makes use of SNI (a good explanation on what that means is given by the SNI Wikipedia article).

So, switching again to Python and looking at the get_server_certificate method, examining the ssl module source (here for convenience), you can discover that the function includes this call:

context.wrap_socket(sock)

without the server_hostname=hostname key argument, which of course should mean that get_server_certificate cannot be used querying a SNI server. A little more effort is required:

hostname = "example.com"
port = 443

context = ssl.create_default_context()

with socket.create_connection((hostname, port)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as sslsock:

        der_cert = sslsock.getpeercert(True)

        # from binary DER format to PEM
        pem_cert = ssl.DER_cert_to_PEM_cert(der_cert)          
        print(pem_cert)
like image 41
wiredolphin Avatar answered Oct 07 '22 01:10

wiredolphin