4

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?

Sean
  • 489
  • 1
  • 8
  • 29
  • Are you asking on how to make sure that the certificate validation succeeds in order to get the CN or how to get the CN even if the certificate validation fails? – Steffen Ullrich Aug 03 '17 at 08:55
  • Either would be fine. I tried latter one (looked up many questions about ignoring certificate validation) but I failed :( – Sean Aug 03 '17 at 08:58
  • Getting CN from the website(same as Internet Explorer shows us) is all I want. – Sean Aug 03 '17 at 09:00
  • Related [answer](https://stackoverflow.com/a/74349032/1438906) with more details and several options. – wombatonfire Nov 07 '22 at 15:46

1 Answers1

12

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)
Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • 1
    THIS is the answer I've been searching for. It works, even if the certificate has expired or is invalid for several other reasons. (Not _all_ reasons, but most.) NB: you may want to provide a timeout with `s.settimeout(5)` before connecting, otherwise the socket will block forever if the host is unreachable. – Nick K9 Apr 20 '20 at 14:58
  • @NickK9 This can also be used `socket.setdefaulttimeout(5)` – Neeraj Sonaniya Jul 09 '21 at 06:58