openssl x509 into date -d. This method converts the human-readable expiration string directly into a numeric timestamp, which is essential for scripting, automated monitoring, and integration with systems that require epoch seconds rather than formatted dates.
Understanding the OpenSSL Output Format
Before converting the date, it is helpful to understand how OpenSSL presents certificate information. The commandopenssl x509 -enddate -noout retrieves the "notAfter" field from a certificate file or standard input. By default, this output appears as a readable string, such as notAfter=Dec 31 23:59:59 2025 GMT. While this format is easy for humans to read, it is not immediately usable in most programming languages or shell scripts that expect a numeric value representing the number of seconds since the Unix epoch (January 1, 1970).
To get this raw data, you typically point OpenSSL at your certificate file using the -in flag. For example, running openssl x509 -in cert.pem -enddate -noout will print the expiration line. The -noout flag ensures that only the specific requested field is displayed, preventing other certificate details from cluttering the output stream. This clean output is critical for the next step, where we parse the string.
Converting to Unix Time on Linux
On Linux systems, the GNU version of thedate command provides robust parsing capabilities. You can combine OpenSSL with date using process substitution or pipes to achieve the conversion in a single line. The most reliable approach involves extracting the date string and feeding it into date -d, which interprets natural language date formats.
Here is the standard one-liner for Bash:
openssl x509 -in cert.pem -enddate -noout | sed 's/notAfter=//' | xargs -I {} date -d "{}" +%s
Let us break down why each component is necessary. First, sed 's/notAfter=//' strips the prefix from the OpenSSL output, leaving only the date portion. Without this, date -d might fail because it does not recognize the notAfter= label. Next, xargs -I {} passes the cleaned string as an argument to the date command. Finally, +%s tells date to output the result in seconds since the epoch.
If you prefer a more concise approach without stripping the prefix manually, some versions of date can handle the full string if you use the --date option correctly, but explicit cleaning via sed or awk is safer across different environments. For instance, using awk allows you to isolate the second field if the separator changes:
openssl x509 -in cert.pem -enddate -noout | awk -F= '{print $2}' | xargs -I {} date -d "{}" +%s
This method ensures that regardless of whether the certificate uses UTC or local time notation, the conversion remains accurate. The resulting number can be stored in a variable for comparison against current time using $(date +%s).
Handling macOS and BSD Systems
A common pitfall occurs when running these commands on macOS or FreeBSD. These systems use BSD-derived utilities, where thedate command lacks the -d flag for parsing arbitrary strings. Instead, you must use the -j (jump) and -f (format) flags to specify both the input format and the desired output.
The OpenSSL output format on these systems is identical to Linux, so the initial extraction steps remain the same. However, the final conversion requires a different syntax. You need to tell date exactly what format the incoming string uses. The standard format for OpenSSL output is %b %d %T %Y %Z, which corresponds to Month Day Time Year Timezone.
Here is the compatible command for macOS/BSD:
openssl x509 -in cert.pem -enddate -noout | sed 's/notAfter=//' | xargs -I {} date -j -f "%b %d %T %Y %Z" "{}" +%s
The -j flag prevents date from setting the system clock, while -f defines the parser. If you omit the -f flag, date will attempt to guess the format, which often leads to errors or incorrect results. This distinction is vital for cross-platform scripts that manage certificates on both Linux servers and macOS development machines.
Using Python for Cross-Platform Reliability
For complex automation tasks, relying on shell utilities can introduce fragility due to differences in OS-specific tools. Python offers a robust alternative that works consistently across Windows, Linux, and macOS. Using the built-inssl and datetime modules, you can parse the certificate and calculate the Unix timestamp without invoking external processes.
First, load the certificate using ssl.PEM_cert_to_DER_cert or the cryptography library. If using the standard library, you can convert the PEM to DER bytes and then use ssl.DER_cert_to_PEM_cert logic, but it is often easier to use the cryptography package which is widely available. Once loaded, access the not_valid_after_utc property.
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import datetime
with open("cert.pem", "rb") as f:
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
expiry_date = cert.not_valid_after_utc
unix_time = int(expiry_date.timestamp())
print(unix_time)
This approach eliminates the need for sed or platform-specific date flags. It also handles timezone conversions automatically, ensuring that the Unix timestamp is always correct relative to UTC. This is particularly useful if you are building a web service or a CI/CD pipeline where consistent behavior across different deployment environments is required.
Integrating with Monitoring Scripts
Once you have the Unix timestamp, you can easily check if a certificate is expiring soon. Compare the extracted expiry time against the current time plus a buffer period. For example, to alert if a certificate expires within 30 days, calculate the threshold ascurrent_time + (30 * 86400).
This technique is frequently used in conjunction with other certificate management tasks. For instance, if you are managing serial numbers explicitly, you might want to ensure that the new certificate has a valid expiry before deploying it. Similarly, when dealing with P12 authentication, verifying the chain validity includes checking the expiry of intermediate certificates.
Related articles that may assist with broader certificate management include How to Set Length of Certificate Serial Number Explicitly? for handling metadata, and P12 Certificate Authentication for securing private key bundles. Additionally, if you are updating your environment, see How to Update OpenSSL to 3.1.1 from 1.0.2n in Node.js to ensure your tools support modern certificate formats.
Frequently Asked Questions
What happens if the certificate is self-signed?
The expiry date extraction process is identical for self-signed and CA-signed certificates. OpenSSL reads the "notAfter" field regardless of the trust chain. Ensure you are pointing the -in flag at the correct .pem or .crt file containing the leaf certificate.
Can I get the expiry date in milliseconds instead of seconds?
Standard Unix time is measured in seconds. To get milliseconds, simply multiply the output of +%s by 1000 in your shell script or multiply the Python integer by 1000. Most JavaScript environments and many APIs accept either format, but verify the API documentation for precision requirements.
Why does my date command return an error on macOS?
This usually happens because macOS uses BSD date, which does not support the -d flag. Use date -j -f "%b %d %T %Y %Z" instead to explicitly define the input format. Always test your scripts on both Linux and macOS if they are intended for cross-platform use.
How do I handle certificates with future expiry dates in scripts?
If a certificate is not yet valid, the notBefore field will be in the future. When comparing against $(date +%s), the expiry timestamp will be greater than the current time. Ensure your logic checks if expiry > current + buffer to avoid false positive alerts for certificates that are already expired or not yet active.
Is there a way to check multiple certificates at once?
You can loop through a directory of certificates using a for loop in Bash. Iterate over all *.pem files, apply the conversion command, and store the results in an associative array or temporary file. This allows you to batch-check expiration times for bulk certificate deployments.