I want the 'issued to' information from certificate in python. I try to use the SSL and SSLSocket library but did not happen.
Updated answer
If you can establish a connection to the remote server you can use the ssl
standard library module:
import ssl, socket
hostname = 'google.com'
ctx = ssl.create_default_context()
with ctx.wrap_socket(socket.socket(), server_hostname=hostname) as s:
s.connect((hostname, 443))
cert = s.getpeercert()
subject = dict(x[0] for x in cert['subject'])
issued_to = subject['commonName']
issuer = dict(x[0] for x in cert['issuer'])
issued_by = issuer['commonName']
>>> issued_to
u'*.google.com'
>>> issued_by
u'Google Internet Authority G2'
Original answer
Use pyOpenSSL.
from OpenSSL import crypto
cert_file = '/path/to/your/certificate'
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open(cert_file).read())
subject = cert.get_subject()
issued_to = subject.CN # the Common Name field
issuer = cert.get_issuer()
issued_by = issuer.CN
You can also access additional components, e.g. organisation (subject.O
/issuer.O
), organisational unit (subject.OU
/issuer.OU
).
Your certificate file might be in another format, so you could try crypto.FILETYPE_ASN1
instead of crypto.FILETYPE_PEM
.
cryptography
package which is the one causing your error. You can install with pip install pyopenssl
which should install the dependencies too, however, you might need a build environment. Your screenshot suggests a Windows environment - that might be more difficult to get going. –
Pierro ssl
and socket
modules. This method requires connecting to the remote server, which your acceptance of the keytool
answer indicates is possible. I think this a better solution because it does not rely on an external tool (which you have to obtain/install/maintain), and it is based entirely on the standard library so it will be portable between OSes without worrying about dependencies. –
Pierro The pyOpenSSL
library does not seem to be well suited for this task. Here is what they say in official docs
Note: The Python Cryptographic Authority strongly suggests the use of pyca/cryptography where possible.
There are some workarounds using Python's standard library, but most of those seem to be messy.
Here is the way to do it through pyca/cryptography. It looks pretty straightforward and clean
from cryptography.x509 import load_pem_x509_certificate
cert = load_pem_x509_certificate(certificate_content)
certificate_serial_number = cert.serial_number
certificate_issuer_info = cert.issuer.rfc4514_string()
certificate_subject_info = cert.subject.rfc4514_string()
the result will print the whole line with issuer details
CN=name,O=Org\, Inc.,L=Tucson,ST=Arizona,C=US
CN=Sectigo RSA Organization Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
To access particular details about the certificate, you can use the cert.issuer
object.
pyOpenSSL
module is NOT outdated and is being actively maintained by the same team responsible for the cryptography
module. In fact pyOpenSSL
uses cryptography
under the hood. What the note in the docs means is that the module should be used for the tasks it was made for, otherwise you risk shooting yourself in the foot. –
Lem The problem with the currently accepted answer, recommending the use of the ssl
module, is that it'll work only if the certificate of interest can be successfully verified. If, for any reason, verification fails, like, for example, with expired or a self-signed certificate, we'll get ssl.SSLCertVerificationError
instead of the requested info. This is because the SSLContext
's default verify_mode
is CERT_REQUIRED
.
We can change it:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
But then we'll find out that secure socket's getpeercert()
method returns an empty dictionary. That's a bummer!
We can workaround this by asking for the certificate in the binary form:
getpeercert(binary_form=True)
But now we have to convert it, and thus we need a third party cryptography
module:
from cryptography.x509 import load_der_x509_certificate
# create SSLContext and wrap the socket as ssock here
certificate_content = ssock.getpeercert(binary_form=True)
cert = load_der_x509_certificate(certificate_content)
certificate_subject_info = cert.subject.rfc4514_string()
This example is adapted from Henry's answer with the DER loader being used instead of the PEM loader.
Now, I do understand why anyone would prefer built-in modules instead of additional dependencies, but, since a third party module is needed anyway (by "anyway" I mean if we want to handle corner cases), I believe an easier choice for this task would be to use pyOpenSSL:
import socket
from OpenSSL import SSL
context = SSL.Context(SSL.TLS_CLIENT_METHOD)
conn = SSL.Connection(context, socket.socket())
conn.connect(address)
print(conn.get_peer_certificate().get_subject())
# or: print(conn.get_peer_certificate().get_subject().get_components())
# or: print(conn.get_peer_certificate().to_cryptography().subject.rfc4514_string())
pyOpenSSL uses cryptography
module under the hood and provides convenient methods to access certificate's properties.
If you use requests
, a simple code is here:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from requests.packages.urllib3.contrib import pyopenssl as reqs
def https_cert_subject_alt_names(host, port):
"""Read subject domains in https cert from remote server"""
x509 = reqs.OpenSSL.crypto.load_certificate(
reqs.OpenSSL.crypto.FILETYPE_PEM,
reqs.ssl.get_server_certificate((host, port))
)
return reqs.get_subj_alt_name(x509)
if __name__ == '__main__':
domains = https_cert_subject_alt_names("www.yahoo.com", 443)
print(domains)
The result is as follow:
[('DNS', '*.www.yahoo.com'),
('DNS', 'www.yahoo.com'),
('DNS', 'add.my.yahoo.com'),
('DNS', 'au.yahoo.com'),
('DNS', 'be.yahoo.com'),
('DNS', 'br.yahoo.com'),
('DNS', 'ca.my.yahoo.com'),
('DNS', 'ca.rogers.yahoo.com'),
('DNS', 'ca.yahoo.com'),
('DNS', 'ddl.fp.yahoo.com'),
('DNS', 'de.yahoo.com'),
...
('DNS', 'mbp.yimg.com')]
import os
import re
os.system('keytool -printcert -sslserver google.com:443 >cert.txt')
fh = open("cert.txt", "r")
content = fh.readlines()
fh.close()
content = content[2]
m = re.search('CN=(.+?),', content)
if m:
found = m.group(1)
print found
© 2022 - 2024 — McMap. All rights reserved.