<!-- https://pkizerotohero.com/handbook/cheat-sheet/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Tested commands to inspect certificates and server chains, check expiry, create keys and CSRs, convert formats, test chains and match keys to certificates.

Source: https://pkizerotohero.com/handbook/cheat-sheet/ · Last reviewed: 2026-10-20

Appendix

# Cheat sheet: the certificate commands you actually need

**In one sentence:**

Tested OpenSSL, curl, keytool, Windows and Kubernetes commands for everyday certificate work, grouped by the task you are trying to do.

## Who is this for

- [Developer](https://pkizerotohero.com/start/#developer)
- [IT ops](https://pkizerotohero.com/start/#itops)
- [Expert](https://pkizerotohero.com/start/#expert)

Level: **Practitioner**

Every command on this page was run for real, and every output block is real output, sometimes shortened where marked. The tools used were OpenSSL 3.2 and curl 8.10 in Git Bash on Windows, keytool from a Java 21 runtime, and Windows’ own `certutil` and PowerShell 5.1. Newer versions print the same information, sometimes with small differences in layout.

Note

Before you start

- **Git Bash on Windows** rewrites arguments that start with `/`, which breaks OpenSSL options such as `-subj "/CN=…"`. Run `export MSYS_NO_PATHCONV=1` first.
- **Never paste a private key** into a website, a chat or a ticket. The tools on this site take certificates and CSRs only, and they run in your browser.
- **Passwords on the command line** end up in your shell history. The examples use `pass:changeit` to stay short. In real work, let the tool prompt you, or use `-passin file:path` or `-passin env:VAR` with OpenSSL.

## How do I inspect a certificate file?

Print everything, or only the fields you need:

```bash
openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -subject -issuer -dates -ext subjectAltName
```

```text
subject=CN=www.example.com
issuer=CN=www.example.com
notBefore=Oct 19 22:46:10 2026 GMT
notAfter=Jan 17 22:46:10 2027 GMT
X509v3 Subject Alternative Name: 
    DNS:www.example.com, DNS:example.com
```

Get the SHA-256 [fingerprint](https://pkizerotohero.com/handbook/glossary/#fingerprint), which identifies this exact certificate:

```bash
openssl x509 -in cert.pem -noout -fingerprint -sha256
```

If the file is binary (DER), add `-inform DER`. If OpenSSL says `Could not find certificate`, the file is probably DER, PKCS #12 or PKCS #7; see the [file formats appendix](https://pkizerotohero.com/handbook/file-formats/).

**On Windows**, `certutil` reads PEM and DER files alike (output shortened):

```powershell
certutil -dump le.cer
```

```text
Issuer:
    CN=YE2
 NotBefore: 2026-09-04 4:34 PM
 NotAfter: 2026-12-03 4:34 PM
Subject:
    CN=letsencrypt.org
        DNS Name=cp.letsencrypt.org
        DNS Name=cp.root-x1.letsencrypt.org
```

`certutil` prints times in your local time zone. PowerShell can load the certificate as a .NET object:

```powershell
$c = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("C:\certs\le.cer")
$c | Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint
```

```text
Subject    : CN=letsencrypt.org
Issuer     : CN=YE2, O=Let's Encrypt, C=US
NotBefore  : 2026-09-04 4:34:32 PM
NotAfter   : 2026-12-03 3:34:31 PM
Thumbprint : 4AD7818D0F13687181FDED3B88D1F06561D580C7
```

The Windows “thumbprint” is the SHA-1 fingerprint. It identifies a certificate in the Windows stores; it says nothing about how secure the certificate is.

Or paste the certificate into the decoder:

*Interactive tool: Certificate decoder (runs in the browser at [/tools/decoder/](https://pkizerotohero.com/tools/decoder/))*

## How do I see the chain a server sends?

`openssl s_client -showcerts` prints every certificate the server sends, in order (PEM blocks removed here):

```bash
openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org -showcerts </dev/null
```

```text
Certificate chain
 0 s:CN=letsencrypt.org
   i:C=US, O=Let's Encrypt, CN=YE2
   a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Sep  4 14:34:32 2026 GMT; NotAfter: Dec  3 14:34:31 2026 GMT
 1 s:C=US, O=Let's Encrypt, CN=YE2
   i:C=US, O=ISRG, CN=Root YE
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
 2 s:C=US, O=ISRG, CN=Root YE
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   ...
 3 s:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
   ...
```

Read it as pairs: each certificate’s issuer (`i:`) should be the next certificate’s subject (`s:`). Always pass `-servername`: it sends [SNI](https://pkizerotohero.com/handbook/glossary/#sni), and without it many servers return a default certificate for a different name. Certificates 2 and 3 here are [cross-signed](https://pkizerotohero.com/handbook/cross-signing/) roots, sent so that clients with older trust stores can still build a path.

For a one-line verdict, use `-brief`:

```bash
openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org -brief </dev/null
```

```text
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_128_GCM_SHA256
Peer certificate: CN=letsencrypt.org
Hash used: SHA256
Signature type: ECDSA
Verification: OK
Server Temp Key: X25519, 253 bits
```

**With curl**, `--write-out '%{certs}'` prints the chain the server sent (curl 7.88 and later; shortened):

```bash
curl -s https://letsencrypt.org/ -o /dev/null -w "%{certs}" | grep -E "^(Subject|Issuer|Expire Date):"
```

```text
Subject:CN=letsencrypt.org
Issuer:C=US, O=Let's Encrypt, CN=YE2
Expire Date:2026-12-03 14:34:31 GMT
Subject:C=US, O=Let's Encrypt, CN=YE2
Issuer:C=US, O=ISRG, CN=Root YE
...
```

**With Java’s keytool** (shortened):

```bash
keytool -printcert -sslserver letsencrypt.org:443
```

```text
Certificate #0
Owner: CN=letsencrypt.org
Issuer: CN=YE2, O=Let's Encrypt, C=US
Valid from: Fri Sep 04 16:34:32 CEST 2026 until: Thu Dec 03 15:34:31 CET 2026
Certificate #1
Owner: CN=YE2, O=Let's Encrypt, C=US
Issuer: CN=Root YE, O=ISRG, C=US
```

Paste a chain into the chain checker to see which link is missing or out of order:

*Interactive tool: Chain checker (runs in the browser at [/tools/chain/](https://pkizerotohero.com/tools/chain/))*

## When does it expire?

For a file, print the end date, or let OpenSSL answer “will it still be valid in N seconds?” (2,592,000 seconds is 30 days). The exit code makes it easy to script:

```bash
openssl x509 -in cert.pem -noout -enddate
openssl x509 -in cert.pem -noout -checkend 2592000; echo "exit=$?"
```

```text
notAfter=Jan 17 22:46:10 2027 GMT
Certificate will not expire
exit=0
```

When the certificate expires within that window, it prints `Certificate will expire` and exits with 1.

For a live server, pipe `s_client` into `x509`:

```bash
openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -enddate
```

On Windows, list the certificates in your personal store that expire within 30 days:

```powershell
Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(30) } |
  Format-Table Subject, NotAfter, Thumbprint
```

Use `Cert:\LocalMachine\My` for the machine store that services such as IIS use. Plan renewals with the lifetime calendar at [/tools/lifetime/](https://pkizerotohero.com/tools/lifetime/), and see [chapter 19](https://pkizerotohero.com/handbook/certificate-lifecycle/) for inventory and monitoring.

### In Kubernetes

A TLS secret stores the certificate base64-encoded under `tls.crt`. Decode it and hand it to OpenSSL. The command below builds the secret locally with a client-side dry run, so it needs no cluster; against a real cluster, replace the first part with `kubectl get secret www-example-tls -n <namespace> -o jsonpath='{.data.tls\.crt}'`:

```bash
kubectl create secret tls www-example-tls --cert=leaf.pem --key=key.pem \
  --dry-run=client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -enddate
```

```text
subject=CN=www.example.com
notAfter=Jan 17 22:46:46 2027 GMT
```

With cert-manager installed, these show the state of every managed certificate and force a renewal. They need a cluster, so they were not run for this page; the syntax is from the [cert-manager documentation](https://cert-manager.io/docs/reference/cmctl/):

```bash
kubectl get certificate -A
kubectl get certificaterequest -n my-namespace
cmctl status certificate my-certificate -n my-namespace
cmctl renew my-certificate -n my-namespace
```

## How do I create a key and a CSR?

Generate the key where it will be used, then a CSR that carries the names in the subject alternative name. Browsers and Go ignore the common name for hostname checks. Some libraries, such as OpenSSL-based clients, Python and Node.js, fall back to it only when the certificate has no DNS name in its SAN (no SAN at all, or only IP addresses). So put every name in the SAN:

```bash
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
openssl req -new -key key.pem -out req.csr -subj "/CN=www.example.com" \
  -addext "subjectAltName=DNS:www.example.com,DNS:example.com"
```

For RSA instead of ECDSA, use `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out key.pem`. Check the CSR before you send it:

```bash
openssl req -in req.csr -noout -verify -subject
openssl req -in req.csr -noout -text | grep -A1 "Subject Alternative Name"
```

```text
Certificate request self-signature verify OK
subject=CN=www.example.com
                X509v3 Subject Alternative Name: 
                    DNS:www.example.com, DNS:example.com
```

**With keytool**, the key pair is created inside a keystore, and the CSR comes from there:

```bash
keytool -genkeypair -alias app -keyalg EC -groupname secp256r1 -dname "CN=app.example.com" \
  -ext "SAN=dns:app.example.com" -validity 30 -keystore app.p12 -storetype PKCS12
keytool -certreq -alias app -keystore app.p12 -ext "SAN=dns:app.example.com" -file app.csr
```

Repeat the `-ext` option in `-certreq`: the SAN is not copied into the CSR from the temporary self-signed certificate. When the CA returns the certificate, import it under the same alias with `keytool -importcert -alias app -file app.crt -keystore app.p12`, after importing the CA’s intermediate certificates. Read [chapter 10](https://pkizerotohero.com/handbook/csr/) for what belongs in a CSR, and decode one here:

*Interactive tool: CSR decoder (runs in the browser at [/tools/csr/](https://pkizerotohero.com/tools/csr/))*

## How do I convert between formats?

The [file formats appendix](https://pkizerotohero.com/handbook/file-formats/) explains each format. The everyday conversions:

```bash
# PEM to DER, and back
openssl x509 -in cert.pem -outform DER -out cert.der
openssl x509 -in cert.der -inform DER -out cert.pem

# certificate + key (+ chain) into one PKCS #12 / PFX file
openssl pkcs12 -export -in cert.pem -inkey key.pem -certfile chain.pem -name www.example.com -out bundle.p12

# PKCS #12 back to PEM: certificates only, then the key into its own file
openssl pkcs12 -in bundle.p12 -nokeys -out certs.pem
openssl pkcs12 -in bundle.p12 -nocerts -noenc -out key.pem

# PKCS #7 / P7B to PEM certificates
openssl pkcs7 -in bundle.p7b -print_certs -out certs.pem
```

`-noenc` (called `-nodes` before OpenSSL 3.0) writes the key without a password. Do that only when the software that reads it cannot handle an encrypted key, and protect the file.

OpenSSL 3 encrypts PKCS #12 files with AES-256 by default. Some older systems cannot open those. `openssl pkcs12 -export -legacy` writes the old format instead, which uses weak 40-bit RC2 encryption for the certificates, so use it only when you must, and never send such a file over an untrusted channel.

On Windows, `certutil` converts between binary DER and Base64 (PEM-style) files:

```powershell
certutil -encode le.cer le-b64.cer
certutil -decode le-b64.cer le-der.cer
```

```text
CertUtil: -encode command completed successfully.
CertUtil: -decode command completed successfully.
```

For Java keystores, see the keytool section below.

## How do I test a chain?

`openssl verify` takes the trusted root with `-CAfile` and any intermediates with `-untrusted`:

```bash
openssl verify -CAfile root.pem -untrusted intermediate.pem leaf.pem
openssl verify -CAfile root.pem leaf.pem
```

```text
leaf.pem: OK
CN=www.example.com
error 20 at 0 depth lookup: unable to get local issuer certificate
error leaf.pem: verification failed
```

The second command leaves out the intermediate, which is exactly what a client sees when a server forgets to send it. That is [error 20](https://pkizerotohero.com/errors/openssl-verify-error-20/), and curl reports the same problem as [unable to get local issuer certificate](https://pkizerotohero.com/errors/curl-unable-to-get-local-issuer-certificate/). Add a hostname check to catch name mismatches:

```bash
openssl verify -CAfile root.pem -untrusted intermediate.pem -verify_hostname shop.example.com leaf.pem
```

```text
CN=www.example.com
error 62 at 0 depth lookup: hostname mismatch
error leaf.pem: verification failed
```

On Windows, `certutil -verify -urlfetch` builds the chain with the Windows trust store, downloads missing intermediates and checks revocation (output shortened):

```powershell
certutil -verify -urlfetch le.cer
```

```text
Verified Application Policies:
    1.3.6.1.5.5.7.3.1 Server Authentication
Cert is an End Entity certificate
Leaf certificate revocation check passed
CertUtil: -verify command completed successfully.
```

For a certificate from a private CA that Windows does not trust, the same command reports `CERT_E_CHAINING` (“A certificate chain could not be built to a trusted root authority”).

## Does this key match this certificate?

A certificate, its CSR and its private key match when they contain the same public key. Hash the public key from each and compare:

```bash
openssl x509 -in cert.pem -noout -pubkey | openssl sha256
openssl pkey -in key.pem -pubout | openssl sha256
openssl req -in req.csr -noout -pubkey | openssl sha256
```

```text
SHA2-256(stdin)= 0459fcddca0c5251a423f21a81a1292bfedbdbd413bc4cbf58ad8981961436a8
SHA2-256(stdin)= 0459fcddca0c5251a423f21a81a1292bfedbdbd413bc4cbf58ad8981961436a8
SHA2-256(stdin)= 0459fcddca0c5251a423f21a81a1292bfedbdbd413bc4cbf58ad8981961436a8
```

Identical hashes mean a match. This works for RSA and EC keys alike and never prints the private key itself. A web server that refuses to start with “key values mismatch” or “private key does not match” fails this test.

## How do I look inside a Java keystore?

List the entries, or show every certificate in detail (shortened):

```bash
keytool -list -keystore bundle.p12 -storepass changeit
keytool -list -v -keystore bundle.p12 -storepass changeit
```

```text
Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 1 entry

www.example.com, Oct 20, 2026, PrivateKeyEntry, 
Certificate fingerprint (SHA-256): 78:AA:D2:0B:61:70:AA:36:10:CF:FC:4D:14:8A:93:91:FF:28:9E:27:0B:E7:A2:5E:5A:0F:28:DC:57:CD:6D:70
```

A `PrivateKeyEntry` holds a key and its certificate chain (a server’s or client’s identity). A `trustedCertEntry` holds a CA certificate that Java should trust. The JDK’s own trust store is reached with `-cacerts`; its default password is `changeit`:

```bash
keytool -list -cacerts -storepass changeit
```

```text
Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 109 entries
```

The number of entries depends on your Java version. To trust a private CA in your own trust store (rather than editing the JDK’s), import its root:

```bash
keytool -importcert -noprompt -alias example-root -file root.pem -keystore truststore.p12 -storetype PKCS12 -storepass changeit
```

```text
Certificate was added to keystore
```

Then point the application at it, for example with `-Djavax.net.ssl.trustStore=truststore.p12`. This is the real fix for [PKIX path building failed](https://pkizerotohero.com/errors/java-pkix-path-building-failed/).

Convert an old JKS keystore to PKCS #12, the standard format that current JDKs use by default:

```bash
keytool -importkeystore -srckeystore legacy.jks -srcstorepass changeit \
  -destkeystore converted.p12 -deststoretype PKCS12 -deststorepass changeit
```

keytool itself suggests this when you create a JKS file:

```text
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore legacy2.jks -destkeystore legacy2.jks -deststoretype pkcs12".
```

## What should I never do?

Warning

Do not switch verification off

`curl -k` (`--insecure`), `verify=False` in Python, `NODE_TLS_REJECT_UNAUTHORIZED=0` in Node.js and “trust all certificates” code in Java make every error disappear, including the one that would have told you someone is intercepting the connection. Fix the cause instead: send the full chain from the server, add your private CA to the right trust store, or correct the name. The [error pages](https://pkizerotohero.com/errors/) show the right fix for each message.

- **Do not email or upload private keys.** Move a key only inside an encrypted PKCS #12 file with a strong password, and only when you must.
- **Do not reuse one key everywhere.** Generate a new key for each server or service and for each renewal, unless you have a reason not to.
- **Do not leave unencrypted keys world-readable.** On Linux, `chmod 600 key.pem`.

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/handbook/cheat-sheet/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

### Tools

- [Certificate decoder](https://pkizerotohero.com/tools/decoder/): Paste a PEM certificate and read every field in plain words.
- [Chain checker](https://pkizerotohero.com/tools/chain/): Paste a chain and see which link is missing, expired or out of order.
- [CSR decoder](https://pkizerotohero.com/tools/csr/): Check a certificate signing request before you send it to a CA.

### Topics

- [Certificates](https://pkizerotohero.com/topics/certificates/)
