Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / Python
Tip/Trick

Python 3 - How to Download, View and Save Certificates

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
23 Feb 2019CPOL2 min read 22.1K   3   4
The code below is a sample Python snippet that will connect to host (eg. any www.host.com) at specified port (eg. 443), download certificate chain from host, and store them on the specified cert_file_pathname location.

Introduction

I wrote this article to pass on my knowledge to other developers who might have stumbled upon a different version of Python (Python 2.7.x VS Python 3.7.x) when using OpenSSL to download, view, and save certificates.

Background

While doing POC, I stumbled upon the versioning conflict of Python 2.7.x and Python 3.7.x.

I searched through lots of public articles and lots of Q & A on this topic. Not much information was found on this topic. My purpose is to retrieve certificate chain and store the certificates on my local drive which can be further used in other modules.

Using the Code

The code below is a sample Python snippet that will connect to host (e.g., any www.host.com) at specified port (e.g., 443), download certificate chain from host, and store the certificates on the specified cert_file_pathname (e.g., c:\testfolder\certfile). In the code snippet, I iterate through the certificate list and retrieve the certificate's CN, then print out the CN string. But you can retrieve other fields as needed.

Notice that I manipulate cert_file_pathname and append the index of the certificate to certfile in the code so that I can store all the downloaded certificate with the same prefix. You can modify the code to serve your purpose as needed.

I would like to share this code with everyone as I realized that there aren't many article about this topic. I have learned the feature through articles and Q & A along with OpenSSL and Python documentation.

Python
def get_certificate(host, port, cert_file_pathname):
    s = socket()
    context = SSL.Context(SSL.TLSv1_2_METHOD)
    print('Connecting to {0} to get certificate...'.format(host))
    conn = SSL.Connection(context, s)
    certs = []

    try:
        conn.connect((host, port))
        conn.do_handshake()
        certs = conn.get_peer_cert_chain()

    except SSL.Error as e:
        print('Error: {0}'.format(str(e)))
        exit(1)

    try:
        for index, cert in enumerate(certs):
            cert_components = dict(cert.get_subject().get_components())
            if(sys.version_info[0] >= 3):
                cn = (cert_components.get(b'CN')).decode('utf-8')
            else:
                cn = cert_components.get('CN')
            print('Centificate {0} - CN: {1}'.format(index, cn))

            try:
                temp_certname = '{0}_{1}.crt'.format(cert_file_pathname, index)
                with open(temp_certname, 'w+') as output_file:
                    if(sys.version_info[0] >= 3):
                        output_file.write((crypto.dump_certificate
                                         (crypto.FILETYPE_PEM, cert).decode('utf-8')))
                    else:
                        output_file.write((crypto.dump_certificate(crypto.FILETYPE_PEM, cert)))
            except IOError:
                print('Exception:  {0}'.format(IOError.strerror))

    except SSL.Error as e:
        print('Error: {0}'.format(str(e)))
        exit(1)

Point of Interest

I learned the difference between Python 2.7.15 and 3.7.2 while writing this code. I have learned different certificate formats and security protocol that can be used as well. I am hoping this code will help those that are new to Python and SSL like me.

Note that I use crypto to dump the certificate off in PEM format, but the cert can be dumped in FILETYPE_ASN1 as well. See additional reference: https://pyopenssl.org/en/stable/api/crypto.html.

History

The code above will generate the following output:

Connecting to <host> to get certificate...
Centificate 0 - CN: <certificate CN>
Centificate 1 - CN: <certificate CN>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNeeds imports Pin
Apurva Pathak29-Apr-21 6:58
Apurva Pathak29-Apr-21 6:58 
AnswerRe: Needs imports Pin
Josh Levine8-Aug-22 2:19
Josh Levine8-Aug-22 2:19 
QuestionNameError: name 'socket' is not defined Pin
fatman454-Apr-19 19:19
professionalfatman454-Apr-19 19:19 
AnswerRe: NameError: name 'socket' is not defined Pin
Josh Levine8-Aug-22 2:19
Josh Levine8-Aug-22 2:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.