After my last blog entry about the status page that runs locally on the browser you are using which you can review here
I was thinking, I wonder if I can do the same for a SSL check for expiry and the issue, yes this has been done before but it usually behind a paywall after a couple of certificates checked, so the requirements are like this:
- List of websites to check
- Expiration Date listed
- "Issued By" populated
- Days to Expiry listed
- Status of the certificate (7 days is less is a red flashing dot, else a green pulsing glowing dot)
I have chosen to use Python for this script, but your choice of weapon is down to you for how to run the code and generate the website, this cannot be done from the client as this code cannot run from a browser, this is what it will look like:
Now, lets see how it works as the technical details are below, followed by the code underneath the walkthrough.
If you wish to see this live then you can do here
Now, lets see how it works as the technical details are below, followed by the code underneath the walkthrough.
Importing Libraries
- ssl: Provides functions for secure sockets layer (SSL) encryption.
- socket: Offers functions for network communication using sockets.
- datetime: Provides date and time manipulation tools.
- x509: Contains classes and functions for handling X.509 certificates.
- cryptography.hazmat.backends: Provides cryptographic backend implementations.
This is shown here:
Defining Website List:
websites: A list of website URLs to check for certificate expiration.
This is shown here:
The list I used was as follows:
websites = ["a6n.co.uk", "blog.a6n.co.uk", "outlook.office.com", "stwater.co.uk", "restricted.a6n.co.uk", "adfs.severntrent.co.uk"]
This is shown here:
This is shown here:
The list I used was as follows:
websites = ["a6n.co.uk", "blog.a6n.co.uk", "outlook.office.com", "stwater.co.uk", "restricted.a6n.co.uk", "adfs.severntrent.co.uk"]
get_certificate_info Function:
This function takes a hostname as input and returns the expiration date and issuer information of its SSL certificate.
- It creates an SSL context using ssl.create_default_context().
- It wraps a socket using context.wrap_socket(), creating a secure connection to the specified hostname.
- It retrieves the peer certificate using conn.getpeercert().
- It loads the certificate using x509.load_der_x509_certificate().
- It extracts the expiration date and issuer information from the certificate.
- It returns a tuple containing the expiration date and issuer common name.
This is shown here:
Generating HTML Output
- The code defines an HTML template string html_output.
- It iterates through the websites list.
- For each website, it calls the get_certificate_info function to retrieve certificate information.
- It calculates the days until the certificate expires.
- It determines the appropriate status dot class (valid or warning) based on the expiration date.
- It constructs an HTML table row with website details, expiration date, issuer, days until expiry, and status dot.
- If an error occurs while retrieving information for a website, it adds an error message to the HTML table.
HTML Report Export
JavaScript for Animation (those little dots)
The HTML report is written to a file named ssl_certificate_report.html using the with open() context manager.
A success message is printed to the console indicating that the HTML report was generated successfully.
JavaScript for Animation (those little dots)
A JavaScript snippet is appended to the HTML template to add animation effects to the status dots, all magic.
Code for Python
All you need to do is customise the items in bold, which is limited to the website you wish to check and the time before they get the red blinking dot of doom!
Code for Python
All you need to do is customise the items in bold, which is limited to the website you wish to check and the time before they get the red blinking dot of doom!
import ssl
import socket
import datetime
from cryptography import x509
from cryptography.hazmat.backends import default_backend
websites = ["<website1>", "<website2>", "<website3>"]
def get_certificate_info(hostname):
context = ssl.create_default_context()
conn = context.wrap_socket(socket.create_connection((hostname, 443)), server_hostname=hostname)
certificate = conn.getpeercert(binary_form=True)
x509_cert = x509.load_der_x509_certificate(certificate, default_backend())
expiration_date = x509_cert.not_valid_after
issuer = x509_cert.issuer
# Extract Common Name (CN) from the issuer's distinguished name
issuer_common_name = None
for attribute in issuer:
if attribute.oid == x509.NameOID.COMMON_NAME:
issuer_common_name = attribute.value
break
return expiration_date, issuer_common_name
html_output = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A6N Certificate Expiry Checker</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 20px;
background-color: #f4f4f4;
}
h2 {
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.valid-dot, .warning-dot {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 5px;
animation: pulse 1s infinite;
}
.valid-dot {
background-color: #4CAF50; /* Green */
}
.warning-dot {
background-color: #FF0000; /* Red */
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
</style>
</head>
<body>
<h2>A6N Certificate Expiry Checker</h2>
<table>
<tr>
<th>Website</th>
<th>Expiration Date</th>
<th>Issued By</th>
<th>Days until Expire</th>
<th>Status</th>
</tr>
"""
for website in websites:
try:
expiration_date, issuer_common_name = get_certificate_info(website)
days_until_expire = (expiration_date - datetime.datetime.utcnow()).days
status_dot_class = "valid-dot" if days_until_expire > 7 else "warning-dot"
html_output += f"<tr><td>{website}</td><td>{expiration_date}</td><td>{issuer_common_name}</td><td>{days_until_expire}</td><td><span class='{status_dot_class}'></span></td></tr>"
except Exception as e:
html_output += f"<tr><td colspan='5'>Error retrieving information for {website}: {e}</td></tr>"
html_output += """
</table>
<script>
// JavaScript for flashing and pulsing animations
const dots = document.querySelectorAll('.valid-dot, .warning-dot');
dots.forEach(dot => dot.addEventListener('animationiteration', () => dot.classList.toggle('pulse')));
</script>
</body>
</html>
"""
with open("ssl_certificate_report.html", "w") as file:
file.write(html_output)
print("HTML report generated successfully.")