X.509 Certificate Inspector: Code Examples

Paste a PEM certificate or chain, or drop a certificate file. Pasted certificate parsing runs in your browser; domain fetch sends only the hostname to the certificate service.

ZERO UPLOAD · ALL LOCAL
  1. Paste PEM text into the textarea or drop a certificate file (.crt, .pem, .cer, .der) onto the drop zone.
  2. Each certificate in the chain renders as a separate card, ordered leaf-first. The leaf is expanded; intermediates and root are collapsed.
  3. Coloured badges flag expired certificates, expiring-soon certificates, weak RSA keys, SHA-1 signatures, and wildcard SANs.
  4. Use the Copy button on any field row to copy its value to the clipboard.
  5. Enter a domain name (e.g. example.com) in the "Fetch from domain" field and click Fetch to inspect its live TLS certificate.

Drop .crt / .pem / .cer / .der here

or click to browse

── or paste PEM below ──

── or fetch from domain ──

Parsing certificate…

Inspecting Certificates with OpenSSL

OpenSSL is the most widely used command-line tool for certificate inspection, and its openssl x509 and openssl s_client subcommands handle every certificate format and source. Installed by default on most Linux distributions and macOS, OpenSSL provides text-format certificate dumps, chain verification, and live TLS connection inspection without requiring any additional software. Yet OpenSSL's output is dense and not immediately parseable for non-experts: field labels use X.509 ASN.1 terminology, and critical information like SANs and key usage appear buried in the Extensions section. The CapyToolkit Certificate Inspector provides a visual complement to OpenSSL, parsing the same certificates into structured cards that surface security flags without requiring command-line expertise. Knowing both tools lets you work efficiently in scripted automation and interactive inspection scenarios.

Inspecting a certificate file with openssl x509

The openssl x509 subcommand parses and displays a certificate from a PEM or DER file. The command openssl x509 -in certificate.pem -text -noout prints the full certificate in human-readable text format, including all extensions1. For a DER file, add -inform DER: openssl x509 -in certificate.der -inform DER -text -noout. To extract specific fields without the full dump, use the named output flags. For example, openssl x509 -in certificate.pem -noout -fingerprint -sha256 prints the SHA-256 fingerprint, and openssl x509 -in certificate.pem -noout -ext subjectAltName prints only the Subject Alternative Names. Parsing a DER file with -inform DER is required for certificates distributed in binary form, such as those stored in Java keystores or embedded in firmware images.

Choosing between -text and named output flags for scripts

Building on this, openssl x509 -in certificate.pem -noout -dates prints only the notBefore and notAfter fields, which is the fastest way to script expiry date checks across a large inventory of certificate files without parsing full text output. The named-output approach is especially useful in configuration management and monitoring pipelines where each script extracts exactly one field. Parsing -text output is more fragile because OpenSSL occasionally reorders fields or adds new extensions in patches, while the named output flags keep your scripts compatible across versions. When you need every field in machine-readable form, add -outform DER to write the binary encoding and parse it with a library that handles ASN.1 directly.

Retrieving and inspecting live certificates with openssl s_client

The openssl s_client subcommand establishes a TLS connection to a live host and prints the certificate chain it receives. The basic command is echo | openssl s_client -connect hostname:443 -servername hostname -showcerts2. The echo | provides standard input to the command so it exits after the handshake rather than waiting for more input. Without -servername, the SNI header is omitted, which causes servers hosting multiple domains on one IP to return the wrong certificate. The -showcerts flag prints every certificate in the chain rather than just the leaf. Consequently, you can extract the chain from s_client output and pipe it to openssl x509 for further inspection, or paste it into the Certificate Inspector.

Why -servername matters more than you think on shared hosting

Furthermore, chaining with openssl x509 directly, using echo | openssl s_client -connect hostname:443 -servername hostname 2>/dev/null | openssl x509 -noout -dates, prints only the validity dates from the live leaf certificate in a single pipeline. The -servername flag is easy to omit when testing from a developer workstation that maps the domain to the target server in its hosts file, but production hosts frequently serve dozens of domains behind a single IP and return a default certificate to visitors without SNI. When you capture a certificate chain without SNI and inspect it in the Certificate Inspector, the hostname validation will fail because the SANs match a different domain entirely, leading you to misdiagnose a perfectly valid certificate as misconfigured.

Verifying a certificate chain with openssl verify

The openssl verify subcommand validates a certificate chain against a CA bundle and reports whether validation succeeds or fails, printing the specific error if it fails3. The command openssl verify -CAfile ca-bundle.pem certificate.pem checks the leaf against the CA bundle. To verify an untrusted chain where you provide the root separately, use -CAfile trusted-root.pem -untrusted intermediate.pem leaf.pem, passing the root as the trusted CA and the intermediate as an untrusted supplementary certificate. Yet openssl verify does not check the live server's chain automatically; combine it with s_client output for end-to-end verification.

How the -untrusted flag changes chain assembly rules

Conversely, the Certificate Inspector provides the same chain structure visually, showing each certificate's role label and expiry badge without requiring the CA bundle to be available locally. A common mistake with the -untrusted flag is to pass the leaf certificate a second time as an untrusted entry, which creates a circular chain and confuses the verifier. The untrusted parameter is strictly for intermediates that are not self-signed; passing a self-signed root as untrusted often causes the verifier to report errors that would resolve simply by omitting the extra flag entirely.

Most verification failures in production come down to an incomplete chain file rather than to a misissued certificate, so inspect the chain each tool receives before assuming the CA is at fault. The most common symptoms are the unable to get local issuer certificate error, which indicates that OpenSSL cannot find the root CA that signed the leaf, and the certificate has expired error appearing on a certificate whose notAfter date is still in the future, which usually indicates an intermediate has expired rather than the leaf. Running the certificate chain through the Certificate Inspector reveals which certificate is missing from the file and what role it is expected to play, so you can track down the correct one from the issuing CA documentation.

When to use this

Use OpenSSL commands when you need scripted certificate inspection that integrates into CI/CD pipelines, when debugging TLS handshake failures on servers without a graphical interface, when verifying certificate chains against a local CA bundle before configuring a web server, or when computing certificate fingerprints for inclusion in application pinning configurations. OpenSSL is also the right tool when converting between DER and PEM formats or extracting specific certificate fields in shell scripts for automated expiry monitoring across large server fleets.

Notes

openssl x509 displays a certificate from a PEM or DER file. openssl s_client establishes a live TLS connection and retrieves the certificate chain. openssl verify checks a chain against a CA bundle. Key flags: -text (full field dump), -noout (suppress PEM output), -inform DER (binary input), -showcerts (full chain from s_client), -servername (set SNI hostname on s_client), -ext subjectAltName (print only the SAN extension), -fingerprint -sha256 (compute SHA-256 fingerprint). Chaining commands: echo | openssl s_client ... 2>/dev/null | openssl x509 -noout -text parses the live leaf certificate in one pipeline.

Examples

Print full certificate text from a PEM file

openssl x509 -in certificate.pem -text -noout

Outputs all certificate fields including extensions. Remove -noout to also print the PEM block at the end.

Retrieve the live certificate chain from a domain

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl x509 -noout -text

Prints the leaf certificate fields from the live chain. Remove the trailing openssl pipe to see all PEM blocks in the chain.

Compute the SHA-256 fingerprint of a certificate

openssl x509 -in certificate.pem -noout -fingerprint -sha256

Output: SHA256 Fingerprint=XX:XX:XX:.... Compare against the Certificate Inspector fingerprint field to verify file integrity.

Verify a certificate chain against a CA bundle

openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt -untrusted intermediate.pem leaf.pem

Returns leaf.pem: OK on success. On failure, reports the specific validation error and the certificate where it occurred.

Try in the tool

What this page covers

  • openssl x509 -text -noout prints the full parsed certificate, including all extensions
  • openssl s_client -showcerts -servername retrieves the live chain; always set -servername on SNI-enabled hosts
  • openssl verify -CAfile checks a chain against a CA bundle and reports the specific failure

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    OpenSSL Project, "openssl-x509 — Certificate display and signing utility," docs.openssl.org, accessed June 2026. https://docs.openssl.org/master/man1/openssl-x509/

  2. 2.

    OpenSSL Project, "openssl-s_client — SSL/TLS client," docs.openssl.org, accessed June 2026. https://docs.openssl.org/master/man1/openssl-s_client/

  3. 3.

    Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and Polk, W., "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile," RFC 5280, IETF, September 2008. https://www.rfc-editor.org/info/rfc5280

FAQ

TLS Certificate Inspection in Node.js

Node.js provides the built-in tls and https modules for TLS certificate handling, with getPeerCertificate() as the primary method for reading the certificate a server presents during a TLS connection. Certificate inspection in Node.js is useful for certificate pinning implementations, debugging TLS issues in automated tests, and extracting certificate metadata for security monitoring. The tls module uses OpenSSL under the hood on all platforms; the certificate parsing it surfaces follows the same X.509 structure, but Node.js exposes fields as a JavaScript object rather than raw DER bytes. Consequently, you can read subject, issuer, valid_from, valid_to, fingerprint, serialNumber, and subjectaltname fields directly without parsing ASN.1. Understanding the Node.js TLS API lets you write robust certificate validation logic without depending on external CLI tools.

Reading the peer certificate with tls.connect()

The tls.connect() function establishes a TLS client connection and fires the secureConnect event when the handshake completes. Inside the secureConnect handler, call tlsSocket.getPeerCertificate(true) to get the leaf certificate object with the full chain attached1. The boolean argument true enables the detailed certificate chain; without it, you get only the leaf. Each certificate object exposes subject (an object with CN, O, OU, C fields), issuer (same structure), valid_from and valid_to (date strings), fingerprint256 (SHA-256 hex), serialNumber (hex string), and subjectaltname (comma-separated string). Building on this, iterate through the chain by following cert.issuerCertificate until you encounter a certificate where the subject CN matches the issuer CN, indicating a self-signed root.

When subject and issuer fields stop being reliable chain signals

Most Node.js applications connect to publicly issued certificates where the subject and issuer are distinct organizations, but private CAs frequently reuse the same subject and issuer values at every layer to simplify bookkeeping. In that case, relying only on a matching subject and issuer field to detect the root will stop the iteration early if two intermediate certificates share the same CA name. A more robust way to terminate the walk is to check each certificate for the basicConstraints extension with cA:TRUE, which marks a certificate as a CA regardless of its subject, and stop only when the CA constraint is absent at the top of the chain.

A practical pattern is to collect every certificate your walk visits and log its subject, issuer, and fingerprint so an unexpected chain shows up in your monitoring. A chain that ends at an unfamiliar root is exactly the kind of change pinning and logging are meant to catch. The CapyToolkit Certificate Inspector renders the full chain with the root labeled, so you can compare the walk result against a known-good chain before trusting it in production code.

Implementing certificate pinning via checkServerIdentity

Certificate pinning in a Node.js HTTPS client requires comparing the server certificate's fingerprint against a stored expected value and aborting the connection if they do not match. The https.request() options object accepts a checkServerIdentity function that receives the hostname and the certificate object; throwing an error from this function aborts the connection immediately2. Inside checkServerIdentity, compare cert.fingerprint256 against your pinned fingerprint string and throw a descriptive Error if they differ. Furthermore, key pinning, in place of fingerprint pinning a static public key value, persists across certificate renewals when the same key pair is reused, which reduces the operational burden of updating pinned values on every renewal cycle. Yet key pinning requires extracting the SPKI bytes from the Node.js certificate object, which is more complex than fingerprint comparison and requires the crypto module for the hash computation.

How fingerprint pinning fails without a revocation path

The advantage of fingerprint pinning is that it does not rely on the CA ecosystem the way standard validation does. The disadvantage is that there is no revocation mechanism once a pinned fingerprint is compromised; if the pinned private key leaks you cannot ask the CA to revoke a fingerprint the way you can revoke a certificate. For services with a known, finite set of clients this trade-off is manageable because revoking access for a specific fingerprint just means rotating entries in the trusted configuration. For anything beyond a handful of clients the absent revocation path becomes the primary reason to prefer short-lived certificate lifecycles over pinning in the first place.

Handling certificate errors and custom CA bundles

By default, Node.js validates TLS certificates against the Mozilla CA bundle bundled with OpenSSL, the same bundle that Chrome and Firefox use. When you need to connect to a server using a private CA certificate, pass it as the ca option in the tls.connect() or https.request() options object. The ca option accepts a single PEM string, an array of PEM strings, or a Buffer. Passing the private CA certificate rather than disabling verification with rejectUnauthorized: false preserves TLS security while allowing connection to a private CA. Conversely, rejectUnauthorized: false disables all certificate validation: expired certificates, hostname mismatches, and untrusted CAs all succeed3.

Why rejectUnauthorized: false hides more than you intend

This option must never appear in production code. Use environment variables and configuration flags to control whether strict validation, a custom CA, or disabled validation (only in test environments) is active for each deployment. The tricky part is that rejectUnauthorized: false disables hostname verification AND signature verification at the same time, so a single misconfigured flag turns off every certificate check separately. A safer approach is to set the NODE_EXTRA_CA_CERTS environment variable to the path of a custom CA PEM file, which tells Node.js to append additional trusted roots to the existing bundle without replacing it. Combined with rejectUnauthorized: true, this preserves the full while only adding the private roots your application needs.

When to use this

Use Node.js certificate inspection when building an HTTP client that needs certificate pinning, when writing integration tests that verify TLS configuration against a staging or production endpoint, or when extracting certificate metadata in a backend service for security logging or compliance reporting. The tls module is also appropriate when connecting to internal services that use a private CA certificate and you need to configure a custom ca option programmatically rather than modifying the system trust store.

Notes

tls.connect() establishes a TLS connection programmatically. tlsSocket.getPeerCertificate(true) returns the full chain when the boolean is true; false returns only the leaf. The returned object has a nested chain: cert.issuerCertificate gives the next certificate up the chain. The subjectaltname field is a comma-separated string like "DNS:example.com, DNS:www.example.com, IP Address:192.0.2.1". The fingerprint256 property returns the SHA-256 fingerprint as colon-separated hex. The valid_to field is a date string, not a Date object; convert with new Date(cert.valid_to). https.request() also supports certificate inspection through socket.getPeerCertificate() in the socket event handler. The checkServerIdentity option in https.request() accepts a function that receives (host, cert) and can throw to abort the connection.

Examples

Inspect the peer certificate from a TLS connection

const tls = require('tls');
const socket = tls.connect({ host: 'example.com', port: 443, servername: 'example.com' }, () => {
  const cert = socket.getPeerCertificate(true);
  console.log('Subject:', cert.subject);
  console.log('SANs:', cert.subjectaltname);
  console.log('Expires:', cert.valid_to);
  console.log('Fingerprint:', cert.fingerprint256);
  socket.destroy();
});

The servername option sets the SNI hostname. Pass true to getPeerCertificate() to include the full chain via issuerCertificate.

Certificate pinning via checkServerIdentity

const https = require('https');
const PINNED = 'AA:BB:CC:DD:...'; // SHA-256 fingerprint
const req = https.request({
  hostname: 'api.example.com',
  path: '/data',
  checkServerIdentity: (host, cert) => {
    if (cert.fingerprint256 !== PINNED) {
      throw new Error('Certificate fingerprint mismatch');
    }
  },
}, (res) => { /* handle response */ });
req.end();

Throws an error and aborts the connection if the fingerprint does not match. Update PINNED when the certificate renews.

Connect to a server with a private CA certificate

const fs = require('fs');
const https = require('https');
const ca = fs.readFileSync('private-ca.pem');
const req = https.request({
  hostname: 'internal.example.com',
  ca,
}, (res) => { /* handle response */ });
req.end();

Pass the private CA certificate via the ca option instead of setting rejectUnauthorized: false. This preserves TLS security.

Try in the tool

What this page covers

  • tlsSocket.getPeerCertificate(true) reads the leaf plus the full chain via cert.issuerCertificate
  • checkServerIdentity compares cert.fingerprint256 against a pinned value; throw to abort the connection
  • ca option vs rejectUnauthorized: false pass a private CA to trust it; never disable verification in production

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Node.js Project, "TLS (SSL) — tls.connect and getPeerCertificate," nodejs.org, accessed June 2026. https://nodejs.org/api/tls.html

  2. 2.

    Node.js Project, "HTTPS — https.request and checkServerIdentity," nodejs.org, accessed June 2026. https://nodejs.org/api/https.html

  3. 3.

    Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and Polk, W., "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile," RFC 5280, IETF, September 2008. https://www.rfc-editor.org/info/rfc5280

FAQ

TLS Certificate Inspection in Python

Python's ssl module and the cryptography library together cover every certificate inspection need, from quickly reading a live server's certificate to parsing complex certificate chains from PEM files. The built-in ssl module provides ssl.get_server_certificate() to retrieve a certificate from a live TLS connection and ssl.DER_cert_to_PEM_cert() to convert binary DER to PEM. For detailed field parsing including SANs, extensions, key algorithm, and fingerprint, the cryptography library's x509.load_pem_x509_certificate() function provides full ASN.1 access through a clean Python API. Consequently, Python is one of the most capable environments for certificate inspection scripting, certificate monitoring tools, and integration tests that verify TLS configuration before deployment.

Reading a live certificate with ssl.get_server_certificate()

The ssl.get_server_certificate() function establishes a TLS connection, retrieves the server's leaf certificate, and returns it as a PEM string. The simplest usage is pem = ssl.get_server_certificate(('example.com', 443)). This returns only the leaf certificate, not the full chain. To retrieve the full chain instead of only the leaf, use ssl.SSLContext.wrap_socket() with ssl.PROTOCOL_TLS_CLIENT, which provides access to SSLSocket methods for retrieving all chain certificates as DER bytes1.

Furthermore, passing timeout and CA bundle options to the SSLContext controls validation behavior: setting ssl_context.verify_mode = ssl.CERT_REQUIRED and ssl_context.check_hostname = True enforces strict validation, which is the default in Python 3.10 and later. Conversely, setting ssl_context.check_hostname = False and ssl_context.verify_mode = ssl.CERT_NONE disables all verification. That mode is appropriate only for diagnostic scripting and must never appear in production code. Setting verify_mode to CERT_REQUIRED without loading a CA bundle either through load_verify_locations or through the default system store causes every connection to fail, so a thorough inspection script must decide whether to trust the platform roots or load an explicit bundle before it can fetch a certificate for display.

When an integration test connects to a staging endpoint that uses a self-signed certificate, constructing an explicit SSLContext with the test CA bundled separately keeps the test code production-like instead of relaxing verification globally. Loading the test CA through ssl_context.load_verify_locations preserves the same chain-building logic your production code exercises, so the assertion the test suite makes is that the certificate validation path itself works rather than whether a particular hostname happens to pass.

When ssl.get_server_certificate() is not enough

A quick expiry check that only needs the leaf certificate can rely entirely on the one-liner form, but any code that needs to validate the full chain has to create an SSLContext and manage the socket lifecycle manually. The one-liner also accepts a cafile argument for custom CA bundles, but prior to Python 3.10 the default verification behavior differed across patch releases. Production pinning scripts that inspect certificates construct an explicit SSLContext with hardened settings to avoid depending on defaults that shift between Python versions.

A useful habit is to keep your inspection code and your production validation code as close as possible, so the certificate you check is the one clients actually see. Reusing the same SSLContext settings means a staging relaxation cannot leak into the path you trust. The CapyToolkit Certificate Inspector fetches the live certificate from a hostname and shows the full chain, so a quick manual check confirms whether your script and the real endpoint agree before you ship the code.

Parsing certificate fields with the cryptography library

The cryptography library's x509.load_pem_x509_certificate() function parses a PEM certificate into an object that exposes all X.509 fields through a typed Python API. Load the PEM bytes: cert = x509.load_pem_x509_certificate(pem_bytes). Access basic fields with cert.not_valid_before_utc, cert.not_valid_after_utc (datetime objects in Python 3.9+), cert.serial_number (integer), and cert.signature_algorithm_oid. Read the SANs: san_ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName), then san_ext.value.get_values_for_type(x509.DNSName) returns a list of DNS name strings. Compute the SHA-256 fingerprint with cert.fingerprint(hashes.SHA256()), which returns raw bytes; call .hex() to get the lowercase hex string2.

Building on this, iterate all extensions with for ext in cert.extensions to audit every extension including ones your code does not specifically handle. Switching from the standard library to the cryptography library for field access removes the boundary between inspection code and parsing internals, because the cryptography library returns native Python datetime and integer values instead of the byte strings the standard library leaves for the caller to decode.

How certificate extensions expose more than the obviously useful fields

Most scripts that parse certificates stop at the subject, issuer, SAN, and fingerprint, but certificate metadata that rarely appears in manual review often matters for security auditing. The Key Usage extension lists the cryptographic operations the certificate is authorized to perform, and the Extended Key Usage extension narrows those to specific TLS or code-signing roles. The CRL Distribution Points and Authority Information Access extensions reveal the URIs where revocation status can be retrieved, so checking those fields before relying on OCSP ensures your validation path is complete even when one endpoint is unreachable.

Checking certificate expiry programmatically

Computing days remaining until certificate expiry requires comparing cert.not_valid_after_utc against the current UTC time. In Python 3.9+: from datetime import timezone; remaining = cert.not_valid_after_utc - datetime.now(timezone.utc). The remaining.days value is negative for expired certificates. Consequently, a monitoring script that checks a list of hostnames and alerts when remaining.days is less than 30 needs fewer than twenty lines of Python using ssl.get_server_certificate() and x509.load_pem_x509_certificate()3. Yet automating this as a scheduled job requires handling connection errors, certificates where the intermediate is not returned by ssl.get_server_certificate() (which returns only the leaf), and rate limits when checking many hosts. The CapyToolkit Certificate Inspector provides the same expiry countdown visually for manual checks without writing any code, which complements the scripted monitoring approach for ad-hoc investigation.

A monitoring checklist that covers retries on connection timeouts, logging the hostname alongside the parsed expiry, and treating a missing intermediate as a warning rather than a silent skip takes roughly thirty lines and prevents most false negatives. Without that checklist, silent failures caused by network blips or rate limits often go unnoticed until a certificate actually expires and the monitoring alert never fires.

When timedelta arithmetic crosses a date boundary silently

A subtle pitfall with expiry arithmetic is that Python datetime subtraction produces a timedelta with only days and seconds, so the remaining hours within a partial day are easy to misreport if you look only at remaining.days. For accurate alerting, compare against the total_seconds() value of the timedelta rather than days, since a certificate expiring in thirty hours will print remaining.days == 1 even though it is already past the thirty-day alerting threshold. Adding a small buffer to the comparison also handles clock skew between the host performing the check and the certificate authority that issued it.

When to use this

Use Python certificate inspection when building scripts that check expiry across a fleet of servers, when writing automated tests that verify TLS configuration before a deployment goes live, or when extracting certificate fields for security logging and compliance reporting in a backend service. The ssl module is the right starting point for quick one-off inspections and scripts that need no additional dependencies; add the cryptography library when you need full ASN.1 field access including extensions, key algorithm details, and fingerprint computation. Furthermore, Python's certificate inspection tools apply when you need to validate that a certificate received from a third party matches your expectations before configuring your application to trust it. Building on this, use Python-based certificate inspection in any data pipeline that processes certificates at scale, such as CT log monitoring tools that parse new certificate entries, compute fingerprints, and alert on unauthorized issuances for domains that your organization currently manages.

Notes

ssl.get_server_certificate((hostname, port)) fetches a PEM certificate from a live TLS connection. ssl.DER_cert_to_PEM_cert(der_bytes) converts binary DER to a PEM string. The cryptography library (pip install cryptography) provides x509.load_pem_x509_certificate(pem_bytes) and x509.load_der_x509_certificate(der_bytes). cert.not_valid_after_utc returns a timezone-aware datetime (Python 3.9+); older Python uses cert.not_valid_after (naive UTC datetime). cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value reads the subject CN. cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value.get_values_for_type(x509.DNSName) returns a list of DNS SAN strings. cert.fingerprint(hashes.SHA256()) returns raw bytes; call .hex() for the hex string. The requests library validates certificates by default; requests.get(url, verify=False) disables verification and must not be used in production.

Examples

Fetch and inspect a live certificate

import ssl
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from datetime import datetime, timezone

pem = ssl.get_server_certificate(('example.com', 443)).encode()
cert = x509.load_pem_x509_certificate(pem)
print('Subject CN:', cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value)
print('Expires:', cert.not_valid_after_utc)
print('Days left:', (cert.not_valid_after_utc - datetime.now(timezone.utc)).days)
print('Fingerprint:', cert.fingerprint(hashes.SHA256()).hex())

Requires Python 3.9+ for not_valid_after_utc. For older Python, use not_valid_after and datetime.utcnow().

Read Subject Alternative Names from a certificate file

from cryptography import x509

with open('certificate.pem', 'rb') as f:
    cert = x509.load_pem_x509_certificate(f.read())

san_ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
for name in san_ext.value.get_values_for_type(x509.DNSName):
    print('DNS SAN:', name)

Raises ExtensionNotFound if the certificate has no SAN extension. Wrap in try/except for certificates that predate SAN usage.

Connect to a server using a private CA bundle

import ssl
import socket

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_verify_locations('private-ca.pem')

with socket.create_connection(('internal.example.com', 443)) as sock:
    with ctx.wrap_socket(sock, server_hostname='internal.example.com') as ssock:
        pem = ssl.DER_cert_to_PEM_cert(ssock.getpeercert(binary_form=True))
        print(pem)

Use load_verify_locations to add a private CA certificate rather than disabling verification with CERT_NONE.

Try in the tool

What this page covers

  • ssl.get_server_certificate() fetches the live leaf as PEM text, without verifying it
  • cryptography x509.load_pem_x509_certificate() parses SANs, serial number, and fingerprint through a typed API
  • not_valid_after_utc a timezone-aware datetime on Python 3.9+; compare against datetime.now(timezone.utc)

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Python Software Foundation, "ssl — TLS/SSL wrapper for socket objects," docs.python.org, accessed June 2026. https://docs.python.org/3/library/ssl.html

  2. 2.

    cryptography Project, "X.509 Certificate Object," cryptography.io, accessed June 2026. https://cryptography.io/en/latest/x509/reference/#x509-certificate-object

  3. 3.

    Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and Polk, W., "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile," RFC 5280, IETF, September 2008. https://www.rfc-editor.org/info/rfc5280

FAQ

nginx SSL Certificate Configuration

nginx serves TLS certificates through a set of ssl_* directives in the server block, and getting each directive right is essential for a secure, compatible, and high-performance TLS configuration. The two required directives are ssl_certificate, which points to the PEM file containing your leaf certificate followed by the intermediate chain, and ssl_certificate_key, which points to the private key file. Beyond the required directives, ssl_protocols and ssl_ciphers control which TLS versions and cipher suites clients can negotiate, and ssl_stapling enables OCSP stapling for improved revocation checking. Inspecting the certificate file you configure with the CapyToolkit Certificate Inspector before loading it into nginx confirms the chain is complete and in the correct order, catching issues nginx would not report until the next configuration reload.

Configuring the certificate chain in nginx

The ssl_certificate directive accepts a PEM file path. For a correctly configured TLS deployment, this file must contain the full chain: the leaf certificate first, followed by any intermediate certificates in order, but not including the root. Concatenate the files in the shell: cat leaf.pem intermediate.pem > fullchain.pem. Let's Encrypt Certbot creates this file automatically as fullchain.pem in the /etc/letsencrypt/live/domain/ directory1.

Building the full chain

A common mistake is pointing ssl_certificate at only the leaf certificate file; nginx does not emit a warning for this, but clients that have not cached the intermediate will fail to validate the chain. The leaf-only configuration works during local testing against a certificate that the client already trusts, which is why the mistake survives until deployment and breaks only for fresh visitors that have no intermediate cached in their browser or device store.

A reliable deployment step is to diff the chain you are about to ship against the one already live, so a missing intermediate cannot slip in unnoticed. Catching the gap before traffic arrives is cheaper than debugging handshake failures after they reach real users. The CapyToolkit Certificate Inspector renders the chain as ordered cards, so pasting fullchain.pem shows at a glance whether the leaf, the intermediate, and the root are all present and in the right sequence.

Verifying the chain before deploying

Consequently, always verify the fullchain.pem file in the Certificate Inspector before deploying: paste its content and confirm that the chain view shows both the leaf and intermediate certificates, with the last card labeled ROOT CERTIFICATE if the root is included. The Inspector flags chain order problems and missing intermediates immediately, which catches the most common nginx deployment mistake before it reaches production traffic.

TLS protocol and cipher suite configuration

The ssl_protocols directive restricts which TLS versions nginx accepts. The current recommended setting is ssl_protocols TLSv1.2 TLSv1.3;, which disables TLS 1.0 and TLS 1.1, both deprecated by RFC 89962. Leaving older protocols enabled for the sake of legacy clients exposes the server to known vulnerabilities such as POODLE and BEAST, so disabling them is the default hardening step that every new nginx deployment should apply. Cipher suite selection with ssl_ciphers controls which algorithms are available for TLS 1.2 handshakes; TLS 1.3 manages its own cipher suites independently and ignores ssl_ciphers.

Choosing the cipher suite

The Mozilla SSL Configuration Generator provides nginx ssl_ciphers strings for Modern, Intermediate, and Old compatibility profiles, updated as vulnerabilities are discovered. ssl_prefer_server_ciphers on instructs nginx to choose the cipher from its list rather than the client's preference, which is useful when you need to ensure weak client-preferred ciphers are not selected even when the client offers them alongside stronger options.

Testing the live configuration

Testing the configured TLS with Qualys SSL Labs provides a detailed grade and highlights any residual weaknesses, including whether the server still accepts cipher suites that the Mozilla Modern profile would reject. The grade also reveals whether the chain order and stapling configuration meet the expectations of modern clients, so treat the report as a final verification step before declaring a nginx TLS deployment ready.

OCSP stapling configuration

OCSP stapling improves TLS performance and certificate revocation checking by having nginx pre-fetch the OCSP response and include it in the TLS handshake. Without OCSP stapling, browsers that check OCSP must make a separate HTTP request to the CA's OCSP responder during each TLS handshake, adding latency to every new connection. Enable it with the pair of directives ssl_stapling on and ssl_stapling_verify on3.

Configuring the resolver and trusted chain

The ssl_stapling_verify directive requires the trusted CA certificate chain to be available; either include it in the ssl_trusted_certificate directive pointing to the CA bundle, or use Let's Encrypt's chain.pem file. nginx needs a working resolver to fetch OCSP responses, so add resolver 1.1.1.1 8.8.8.8 valid=300s to the server block alongside the stapling directives. Omitting either the trusted certificate or the resolver causes stapling to stay silent, which is why both the certificate chain file and the resolver directive must appear together before nginx will fetch or cache a staple.

Stapling verification

Verify OCSP stapling is working by running echo | openssl s_client -connect domain:443 -status 2>/dev/null | grep -A 17 OCSP after configuring. The response should include a successful OCSP status line and a nextUpdate timestamp that confirms nginx is refreshing the staple before expiration. The CapyToolkit Certificate Inspector displays both the OCSP URI and the Authority Information Access extension when parsing a certificate, so cross-reference the URI your nginx resolver must reach before enabling stapling in production.

When to use this

Use this reference when configuring TLS for a new nginx server block, when diagnosing a certificate chain error reported by clients connecting to an nginx-served endpoint, when enabling OCSP stapling for performance improvement, or when adding a second ECDSA certificate alongside an existing RSA certificate for dual-stack client compatibility. It is also relevant when updating ssl_protocols to disable TLS 1.0 and TLS 1.1 as part of a TLS hardening initiative or security audit remediation.

Notes

ssl_certificate must point to a PEM file containing the full chain (leaf first, intermediates following, but never the root). ssl_certificate_key points to the private key in PEM format. ssl_protocols TLSv1.2 TLSv1.3 is the current recommended setting; TLS 1.0 and 1.1 are deprecated by RFC 8996. ssl_session_cache shared:SSL:10m; and ssl_session_timeout 10m; configure session resumption. ssl_stapling on; enables OCSP stapling. ssl_stapling_verify on; enables verification of the OCSP staple. resolver and resolver_timeout configure the DNS resolver nginx uses for OCSP stapling lookups. nginx -t tests the configuration without reloading. nginx 1.11.0+ supports dual RSA+ECDSA certificates via multiple ssl_certificate/ssl_certificate_key directive pairs in the same server block.

Examples

Basic TLS server block for Let's Encrypt

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
}

Use fullchain.pem (not cert.pem) for ssl_certificate to send the complete chain including the intermediate.

Enable OCSP stapling

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Add these directives inside the server block alongside the ssl_certificate directives.

Test nginx configuration without reloading

sudo nginx -t

Checks the configuration for syntax errors and certificate file path validity. Always run this before nginx -s reload to catch configuration errors without dropping connections.

Try in the tool

What this page covers

  • ssl_certificate must point to fullchain.pem (leaf + intermediates), never the leaf alone
  • ssl_protocols TLSv1.2 TLSv1.3 the current recommendation; TLS 1.0/1.1 are deprecated by RFC 8996
  • ssl_stapling on + ssl_stapling_verify on requires ssl_trusted_certificate and a working resolver to actually staple

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Let's Encrypt, "Certbot User Guide — nginx," letsencrypt.org, accessed June 2026. https://certbot.eff.org/docs/using.html#nginx

  2. 2.

    Sheikh, H. and Rescorla, E., "Deprecating TLS 1.0 and TLS 1.1," RFC 8996, IETF, March 2021. https://www.rfc-editor.org/info/rfc8996

  3. 3.

    NGINX Project, "Module ngx_http_ssl_module," nginx.org, accessed June 2026. https://nginx.org/en/docs/http/ngx_http_ssl_module.html

FAQ

Java Certificate Inspection with keytool

Java manages certificates through keystores, and keytool is the command-line utility for creating, inspecting, and managing these keystores and their certificate entries. Java TLS connections use either the JKS (Java KeyStore) or PKCS12 format, both managed via keytool. Every Java TLS connection reads the server's certificate from the peer and validates it against a truststore, which by default is the cacerts file bundled with the JDK. Understanding keytool commands and the Java TLS certificate model is essential for debugging SSLHandshakeException errors, configuring mTLS client certificates, and managing enterprise CA certificates in a Java application environment. The CapyToolkit Certificate Inspector complements keytool by providing a visual representation of any certificate you export from a keystore using the keytool -exportcert command.

Inspecting certificates with keytool

The keytool -printcert command prints a human-readable representation of a certificate from a file or URL. For a DER or PEM file: keytool -printcert -file certificate.pem. On recent JDK versions, keytool accepts PEM directly; on older versions, convert to DER first. To inspect all certificates in a keystore: keytool -list -keystore keystore.jks -storepass changeit -v prints all entries with full certificate details including serial number, validity dates, fingerprints, and extensions1. Consequently, keytool -list is the first tool to reach for when debugging PKIX path building failed errors: checking the truststore confirms whether the CA certificate for a service is present and not expired. Furthermore, keytool -list -dacerts on JDK 9+ lists the default JDK truststore without needing to specify its path manually.

When to use keytool -printcert versus -list

Use keytool -printcert when you have a standalone certificate file on disk, such as a certificate exported from a keystore with keytool -exportcert or a certificate received from a third party. Use keytool -list when you want to audit every entry inside an existing keystore, including the chain certificates and private key aliases that each truststore entry carries. The -v flag on either command expands the output to show extensions and fingerprints that the default compact view omits.

A good habit is to run keytool -list against every truststore your application uses, not just the system cacerts, so you know which CAs each connection actually trusts. A private CA added to one truststore but forgotten in another is a frequent cause of PKIX errors that only appear in specific services. The CapyToolkit Certificate Inspector shows the issuer chain for any certificate you paste, so you can confirm the CA you imported matches the one the server presents before chasing the error in application code.

Managing CA certificates for custom PKI

Adding a private CA certificate to a Java application's truststore enables connections to servers using that CA's certificates without disabling certificate validation. The recommended approach is to maintain a separate truststore for application-specific CAs rather than modifying the system cacerts file. Create or update the truststore with: keytool -importcert -alias my-private-ca -file private-ca.pem -keystore app-truststore.jks -storepass changeit -noprompt2. Configure the Java application to use this truststore via system properties: -Djavax.net.ssl.trustStore=app-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit. Building on this, Java applications can also configure the truststore programmatically using SSLContext.init() with a custom TrustManagerFactory that loads your keystore, which is useful in applications that need multiple connection contexts with different trust requirements.

Configuring the application truststore

System properties are the simplest way to direct a Java application to a custom truststore, but they apply globally to every SSL connection the JVM opens. For applications that need two distinct trust anchors on different connections, loading truststores through SSLContext.init() lets you choose the trust material per scope without affecting the global JVM default. The per-scope approach is how frameworks such as Spring Boot wire multiple REST clients that each trust a different internal CA from the same JVM process.

Programmatic certificate inspection with X509Certificate

Java's java.security.cert.X509Certificate class provides programmatic access to all certificate fields. Retrieve the certificate from an active TLS connection with ((SSLSocket) socket).getSession().getPeerCertificates(), which returns an X509Certificate array with the leaf certificate at index 0. Key methods include: getSubjectX500Principal().getName() (subject DN as a String), getNotAfter() and getNotBefore() (Date objects), getSerialNumber() (BigInteger), getSubjectAlternativeNames() (Collection of type-value lists), and getSigAlgName() (signature algorithm name as a String)3. Furthermore, computing the SHA-256 fingerprint requires MessageDigest.getInstance("SHA-256").digest(cert.getEncoded()), where cert.getEncoded() returns the DER bytes. Formatting the resulting byte array as hex with colon separators matches the fingerprint displayed in the CapyToolkit Certificate Inspector and in keytool -printcert output.

Reading fields from a live connection

The getPeerCertificates() call on an active SSLSocket returns the full certificate chain the server presented during the handshake, including the leaf certificate at index 0 and every intermediate certificate the server sent in ascending trust order. Inspecting the chain this way confirms that the server is sending the same intermediates that clients will see, which is the most reliable way to detect a misconfigured chain before it causes production errors.

When to use this

Use keytool and the Java TLS certificate API when debugging PKIX path building failed errors in Java applications, when adding private CA certificates to an application truststore, or when inspecting certificates exported from Java keystores in formats your other tools can parse. It is also relevant when migrating Java keystores from the legacy JKS format to the recommended PKCS12 format, since JKS files cannot be read by non-Java tools and PKCS12 provides interoperability with the broader certificate toolchain. ### Client certificate and integration test setup Use this reference when configuring mTLS for a Java application that needs to present a client certificate, since the KeyManager and TrustManager components require separate keystores for the client certificate and the CA trust anchors. Building on this, apply the X509Certificate API guidance when writing integration tests that verify the certificate presented by a remote server matches your expected fingerprint, issuer, or SAN list before the application proceeds with a sensitive operation.

Notes

keytool -printcert -file certificate.pem prints certificate fields from a PEM or DER file. keytool -list -keystore keystore.jks -v lists all entries with full certificate details. keytool -importcert -alias myca -file ca.crt -keystore keystore.jks adds a certificate to a keystore. keytool -exportcert -alias mycert -keystore keystore.jks -file exported.der exports a certificate in DER format. The default Java truststore is at $JAVA_HOME/lib/security/cacerts with the password changeit. PKCS12 is the recommended format for new keystores (JDK 9+); JKS is the legacy proprietary format. X509Certificate.getSubjectX500Principal().getName(), getNotAfter(), getNotBefore(), getSerialNumber(), getSubjectAlternativeNames(), and getSigAlgName() provide programmatic field access. javax.net.ssl.SSLSocket.getSession().getPeerCertificates() returns the peer's certificate chain as an array.

Examples

Print certificate details from a file

keytool -printcert -file certificate.pem

Works with PEM and DER files on JDK 8+. Output includes validity dates, fingerprints, subject, issuer, and extensions.

List all certificates in a keystore

keytool -list -keystore keystore.jks -storepass changeit -v

Use -cacerts instead of -keystore to list the default JDK truststore without specifying its path (JDK 9+).

Import a CA certificate into a custom truststore

keytool -importcert \
  -alias private-ca \
  -file private-ca.pem \
  -keystore app-truststore.jks \
  -storepass changeit \
  -noprompt

Creates the keystore if it does not exist. Use -noprompt to skip the trust confirmation prompt in automation scripts.

Export a certificate from a keystore for inspection

keytool -exportcert -alias mycert -keystore keystore.jks -storepass changeit -file exported.der

Exports in DER binary format. Drop the .der file onto the CapyToolkit Certificate Inspector dropzone to view its fields visually.

Try in the tool

What this page covers

  • keytool -printcert -file prints certificate fields from a standalone PEM or DER file
  • keytool -list -keystore -v audits every entry in a keystore, including chain certs and fingerprints
  • keytool -importcert adds a private CA to a truststore; the recommended fix for PKIX path building failed

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Oracle, "keytool — Key and Certificate Management Tool," docs.oracle.com, accessed June 2026. https://docs.oracle.com/en/java/javase/17/tools/keytool.html

  2. 2.

    Oracle, "Java Platform SE 8 — javax.net.ssl Package," docs.oracle.com, accessed June 2026. https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/package-summary.html

  3. 3.

    Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and Polk, W., "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile," RFC 5280, IETF, September 2008. https://www.rfc-editor.org/info/rfc5280

FAQ

TLS Certificate Inspection with curl

curl is the universal command-line HTTP client, and its TLS flags cover every certificate inspection and verification scenario you encounter in a terminal. The -v flag exposes TLS handshake details including the certificate subject and issuer. The --cert-status flag requests an OCSP status check during the connection. The --cacert flag specifies a custom CA bundle for private CA verification. Combining curl with openssl s_client and openssl x509 gives you a powerful certificate inspection pipeline without any additional tools. The CapyToolkit Certificate Inspector complements curl by providing a visual, structured view of certificate fields when you need to share findings with colleagues who do not work on the command line, or when you want to inspect the full chain rather than just the leaf certificate fields that curl -v shows.

Certificate inspection with curl -v

Running curl -v https://example.com prints the TLS handshake details to stderr, including the certificate subject, issuer, validity dates, and key algorithm1. The output appears between the * lines in the verbose output: lines starting with * rather than > (request) or < (response) are curl's internal status messages. The certificate section shows the subject CN and other DN fields, the start and expiry dates, and the SSL connection summary. Yet curl -v output is abbreviated and does not show SANs, extensions, or the full chain; for full certificate inspection, pipe the openssl s_client output to openssl x509 instead.

Extracting the full certificate chain

Building on this, echo | openssl s_client -connect hostname:443 -servername hostname -showcerts 2>/dev/null provides all PEM blocks in the chain, which you can then paste into the Certificate Inspector for a structured view of every certificate. openssl s_client reveals the chain order and intermediate content that curl -v conceals, so running both commands together gives you the high-level summary during development and the full field breakdown when you share results with a colleague or ticket.

A practical routine is to capture the full PEM chain with openssl and keep it alongside the curl -v summary, so you have both the quick read and the complete record when a teammate needs it. The chain order and intermediate content are exactly what curl hides, so the openssl output is the source of truth for debugging chain gaps. The CapyToolkit Certificate Inspector takes that same PEM and renders each certificate as a card, so you can confirm the leaf, intermediates, and root line up before you close the ticket.

Testing certificates before DNS propagation with --resolve

The --resolve flag overrides DNS resolution for a specific host, allowing you to connect to an IP address while sending the correct SNI hostname in the TLS handshake. The syntax is --resolve hostname:port:ip_address. For example, to test a new certificate deployed at IP 203.0.113.1 before DNS propagation: curl -v --resolve example.com:443:203.0.113.1 https://example.com. This sends the TLS SNI header for example.com but connects to 203.0.113.1 directly, so you see exactly which certificate that IP serves for that hostname2. Consequently, --resolve is invaluable for verifying certificate deployment before cutting over DNS, catching the case where a new IP is serving the wrong certificate or an incomplete chain before real traffic reaches the endpoint.

Testing a new certificate on a specific IP

When a certificate rotation is scheduled for a new IP that has not yet been added to DNS, --resolve lets you confirm the new deployment is correct without waiting for propagation. The CapyToolkit Certificate Inspector displays the same certificate fields that curl --resolve confirms, so you can cross-check the live endpoint against the certificate file on disk before flipping the DNS record.

Public key pinning with curl --pinnedpubkey

curl's --pinnedpubkey flag implements public key pinning for a specific request or test. The syntax is --pinnedpubkey sha256//base64encodedSHA256hash, where the hash is the Base64-encoded SHA-256 digest of the DER-encoded SubjectPublicKeyInfo from the certificate. Compute the hash with the pipeline: echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | base64. The resulting hash, when passed to --pinnedpubkey, causes curl to reject any connection where the server's public key does not match, even if the certificate chain validates correctly3. Furthermore, this technique works for both RSA and ECDSA keys equally, since both use the same SubjectPublicKeyInfo DER encoding that the pipeline extracts.

Computing the pinned hash reliably

Running the full openssl pipeline on a freshly fetched certificate rather than on a cached file ensures the hash reflects the server's current public key. Re-use the same Certificate Inspector workflow to confirm the pinned hash matches the certificate you actually tested, since pinning against an incorrect hash causes the same hard failure mode as an expired or untrusted certificate. Always recompute the hash after every certificate rotation to avoid pinning failures that block production traffic silently.

When to use this

Use curl for certificate inspection when testing TLS from a command line without writing code, when verifying that a new certificate deployment is serving correctly on a specific IP before DNS propagation, or when testing mTLS connections that require presenting a client certificate. It is the right tool when validating that OCSP stapling is working on a live server, when testing with a private CA bundle for internal services, or when computing a public key hash for use with the --pinnedpubkey flag in automated API test scripts. curl also serves as a rapid sanity check when debugging certificate chain failures reported by other HTTP clients, since its -v output shows the certificate subject, issuer, and validity dates without requiring any additional tools.

Notes

curl -v shows TLS handshake details including certificate chain information. curl --cert-status enables OCSP certificate status checking. curl --cacert ca.pem specifies a custom CA bundle. curl --cert client.pem --key client.key provides a client certificate for mTLS. curl -k (--insecure) disables certificate verification and must never be used in production scripts. curl --resolve hostname:port:ip overrides DNS for a specific host, useful for testing certificates before DNS propagation. curl --pinnedpubkey sha256//base64hash pins a certificate's public key. The -w %{ssl_verify_result} flag prints the OpenSSL verification return code (0 = success). curl versions 7.54.0+ support TLS 1.3 with --tlsv1.3.

Examples

Inspect the live certificate chain and paste into the inspector

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null

Copy the PEM blocks from the output and paste into the Certificate Inspector for a structured chain view. Each -----BEGIN CERTIFICATE----- block is a separate certificate.

Check certificate expiry dates for a live domain

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

Prints notBefore and notAfter dates from the leaf certificate. Automate with a shell script iterating a list of hostnames.

Test with a custom CA for internal services

curl -v --cacert private-ca.pem https://internal.example.com

Validates the TLS connection using private-ca.pem instead of the system CA bundle. Do not use -k in production scripts.

Test a certificate before DNS propagation

curl -v --resolve example.com:443:203.0.113.1 https://example.com 2>&1 | grep -E "subject|issuer|expire|SSL"

Connects to the specific IP while sending the correct SNI hostname, showing the certificate fields that IP serves for that domain.

Try in the tool

What this page covers

  • curl -v shows subject, issuer, and validity dates only; no SANs or full chain
  • curl --resolve host:port:ip tests a certificate on a specific IP before DNS cutover, with the correct SNI
  • curl --pinnedpubkey sha256//... pins the Base64 SHA-256 hash of the DER SubjectPublicKeyInfo

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Stenberg, D. and contributors, "curl - command line tool and library for transferring data with URLs," curl.se, accessed June 2026. https://curl.se/docs/manpage.html

  2. 2.

    Stenberg, D. and contributors, "curl --resolve," curl.se, accessed June 2026. https://curl.se/docs/manpage.html#--resolve

  3. 3.

    Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., and Polk, W., "Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile," RFC 5280, IETF, September 2008. https://www.rfc-editor.org/info/rfc5280

FAQ

Kubernetes TLS Certificate Management

Kubernetes manages TLS certificates through Secrets of type kubernetes.io/tls, where cert-manager automates certificate issuance and renewal via ACME or internal CA integrations. Every HTTPS ingress in Kubernetes relies on a TLS Secret containing the certificate chain and private key; misconfigured or expired secrets cause 502 errors or TLS handshake failures that surface in ingress controller logs before they affect users. The kubectl CLI provides commands to inspect Secret content and extract certificate PEM for inspection. Checking certificates in Kubernetes-managed infrastructure requires understanding both the Kubernetes resource model and the underlying X.509 certificate structure. The CapyToolkit Certificate Inspector integrates into this workflow: extract the certificate PEM from a Kubernetes Secret and paste it into the inspector to verify the chain, SANs, and expiry without installing openssl in every environment where you debug.

Inspecting TLS certificates in Kubernetes Secrets

TLS Secrets in Kubernetes store the certificate chain and private key as Base64-encoded PEM values in the data.tls.crt and data.tls.key fields. Extracting the certificate for inspection requires decoding the Base64: kubectl get secret tls-secret -n namespace -o jsonpath='{.data.tls\.crt}' | base64 -d1. The output is the PEM certificate chain, which you can paste directly into the CapyToolkit Certificate Inspector. Yet the chain in the Secret may differ from the certificate the ingress controller actually serves: cert-manager synchronizes Secrets automatically, but a failed synchronization can leave a stale certificate in the Secret even after renewal.

Cross-checking the Secret against the live endpoint

Consequently, check both the Secret content and the live certificate from the ingress endpoint to confirm they match; comparing the SHA-256 fingerprints of both sides detects any deployment mismatch between what cert-manager wrote and what the ingress controller is serving. Capturing the SHA-256 fingerprint of each certificate with openssl x509 -fingerprint gives you a direct comparison point that eliminates guesswork when a renewal appears to have failed or when the ingress controller serves an unexpected chain order.

A dependable check is to run the fingerprint comparison on a schedule rather than only after an incident, because a silently stale Secret is exactly the kind of drift that surfaces during an unrelated outage. Catching the mismatch early turns a potential cluster-wide surprise into a routine reconciliation. The CapyToolkit Certificate Inspector shows the SHA-256 fingerprint for any certificate you paste, so you can compare the Secret value against the live ingress certificate without recomputing hashes by hand.

Using cert-manager for automated certificate management

cert-manager extends Kubernetes with Certificate and Issuer custom resources, automating certificate issuance and renewal via Let's Encrypt ACME, Vault, or custom CAs. A ClusterIssuer resource configures the ACME endpoint once for the entire cluster, and a Certificate resource in a namespace requests a certificate for a specific set of domains. cert-manager watches the certificate's notAfter date and begins renewal when the renewBefore period (default 30 days) is reached2. The command kubectl describe certificate my-cert -n production shows the current certificate status, including the ACME order URL and any renewal errors.

Configuring automatic Ingress integration

Building on this, cert-manager annotates Ingress resources: adding the annotation cert-manager.io/cluster-issuer: letsencrypt-prod causes cert-manager to create a Certificate resource and TLS Secret automatically from the Ingress spec.tls configuration, eliminating the need to create Certificate resources manually. In clusters with dozens of Ingress resources, this annotation-driven approach removes the operational burden of creating and renewing Certificate resources one by one. The same annotation works with ClusterIssuer and Issuer references, switching the certificate backend requires only updating the annotation value and letting the existing Certificate resource reconcile to the new issuer.

Choosing between ClusterIssuer and Issuer

A ClusterIssuer configures the ACME endpoint once for the entire cluster and issues certificates to any namespace that references it, while an Issuer is scoped to a single namespace and is appropriate when each team should manage its own private CA independently. For clusters with strict tenant isolation,Issuers prevent one namespace from accidentally trusting CAs that another namespace manages, since each CA Secret stays visible only in the namespace that owns it.

Control plane certificate expiry in kubeadm clusters

kubeadm clusters generate a set of control plane certificates for the API server, etcd, controller-manager, and scheduler at cluster initialization. These certificates have a one-year validity period by default and are not renewed automatically by the cluster itself; kubeadm must be used to renew them explicitly. The command kubeadm certs check-expiration lists all control plane certificates with their expiry dates3.

Renewing and inspecting control plane certificates

Unlike workload certificates managed by cert-manager, control plane certificate expiry causes the entire Kubernetes control plane to stop accepting authenticated requests, making it a cluster-wide incident rather than a service-level one. Furthermore, worker node kubelet certificates managed by the Kubernetes Certificate Signing API are renewed automatically by the kubelet when configured with rotateCertificates: true in the kubelet configuration. Inspecting control plane certificate PEM files directly with the Certificate Inspector provides an alternative view to kubeadm output, since the Inspector reveals SANs, chain completeness, and fingerprint details that kubeadm certs check-expiration does not display.

When to use this

Use this reference when deploying HTTPS ingress controllers backed by cert-manager, when inspecting TLS Secrets to verify the certificate a Kubernetes workload is serving, when auditing control plane certificate expiry in a kubeadm-managed cluster, or when diagnosing cert-manager renewal failures before they cause production outages. It is also relevant when configuring a private CA Issuer for internal service certificates, when comparing the SHA-256 fingerprint of a TLS Secret against the live certificate from the ingress endpoint to catch deployment mismatches, or when evaluating ingress controller options based on their certificate handling and rotation behavior.

Notes

kubectl get secret tls-secret -o jsonpath='{.data.tls\.crt}' | base64 -d retrieves the certificate PEM from a Kubernetes TLS Secret. cert-manager creates Certificate resources (kind: Certificate) and manages the associated TLS Secrets automatically. kubectl describe certificate cert-name shows cert-manager's status including renewal time and ACME errors. kubeadm certs check-expiration checks expiry for control plane certificates (etcd, API server, controller-manager, scheduler). The kubernetes.io/tls Secret type requires tls.crt (certificate chain PEM) and tls.key (private key PEM) data fields. cert-manager's ClusterIssuer and Issuer resources configure the ACME or CA backend. Ingress resources reference TLS Secrets in the spec.tls[].secretName field. The cert-manager.io/cluster-issuer annotation on an Ingress resource causes cert-manager to create the Certificate and TLS Secret automatically.

Examples

Extract and inspect a TLS Secret certificate

kubectl get secret tls-secret -n production \
  -o jsonpath='{.data.tls\.crt}' | base64 -d

Pipe the output to openssl x509 -noout -text to read in the terminal, or copy the PEM text and paste it into the Certificate Inspector for a visual chain view.

Check cert-manager certificate status

kubectl describe certificate my-certificate -n production

Shows renewal status, ACME order state, and the target TLS Secret name. Look for Certificate is up to date and has not expired in the Conditions field.

Check control plane certificate expiry in a kubeadm cluster

kubeadm certs check-expiration

Lists all control plane certificates with expiry dates. Run this at least once per quarter on kubeadm-managed clusters to avoid unexpected control plane outages from expired certificates.

Manually renew a specific control plane certificate

kubeadm certs renew apiserver

Renews the API server certificate only. Restart the kube-apiserver pod after renewal: kubectl delete pod -n kube-system -l component=kube-apiserver.

Try in the tool

What this page covers

  • kubectl get secret ... tls.crt | base64 -d extracts the PEM chain from a kubernetes.io/tls Secret
  • cert-manager renewBefore defaults to 30 days before notAfter
  • kubeadm certs check-expiration control plane certificates default to one-year validity, not auto-renewed

Verify with the X.509 Certificate Inspector tool.

Try it in the tool ↑
Sources
  1. 1.

    Kubernetes Project, "Secrets — TLS Secrets," kubernetes.io, accessed June 2026. https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets

  2. 2.

    cert-manager Project, "Certificate Resource," cert-manager.io, accessed June 2026. https://cert-manager.io/docs/concepts/certificate/

  3. 3.

    Kubernetes Project, "kubeadm certs," kubernetes.io, accessed June 2026. https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/

FAQ