How to decode a PEM certificate
By Flavio
Read a PEM certificate in the browser: subject, issuer, dates, SANs, public key, and the SHA-256 fingerprint of the DER. The signature is not checked.
Free toolThis tutorial uses the PEM Certificate Decoder. It runs in your browser, so open it in another tab and follow along.
A certificate is a small file that names a site, names who issued it, and carries a public key. TLS uses that file when a browser opens https://. You meet the file when you make a local HTTPS server, when a deploy fails because the name does not match, or when you want the fingerprint a monitoring check expects.
The on-disk form you usually paste is PEM. PEM is Base64 of a binary certificate, with a BEGIN CERTIFICATE line and an END CERTIFICATE line. The binary inside is DER, and the structure is X.509. The PEM certificate decoder reads that structure in the browser. It does not check the signature, and it does not ask whether the dates are in the past or the future.
What the fields are for
The subject is the name of the certificate. The issuer is the name of whoever signed it. For a public site those two strings differ, because a certificate authority sits in the middle. For a certificate you just created with openssl req -x509, they are often the same string.
The serial is an integer the issuer picked. Logs and revocation lists quote it in hex. The version says which fields are allowed. Version 3 is the one that can carry extensions, including the names in the subject alternative name extension.
Not before and not after are the window the issuer wrote down. A browser compares them with its clock. This page only prints them.
The public key is what the server will use to prove it holds the matching private key. The signature algorithm names the hash and the signature scheme. The signature value is the bytes of that signature. Printing the length is not the same as checking that the signature is valid.
SHA-256 and SHA-1 at the bottom are hashes of the DER bytes, not of the PEM text. Spaces and line breaks in the PEM do not change them. openssl x509 -fingerprint hashes the same bytes.
Decode the sample
Open the PEM certificate decoder and click Load sample. The sample is a certificate I generated for flaviocopes.com with OpenSSL 3. The command was openssl req -x509 -newkey rsa:2048 with serial 0x0A1B2C3D, a subject of country IT, state Bolzano, city Bolzano, organization Flavio Copes, and common name flaviocopes.com. The extra names are flaviocopes.com, www.flaviocopes.com, and the address 192.0.2.1. Basic constraints say this is not a CA, and key usage allows a digital signature and key encipherment.
The PEM is:
-----BEGIN CERTIFICATE-----
MIID2DCCAsCgAwIBAgIEChssPTANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJJ
VDEQMA4GA1UECAwHQm9semFubzEQMA4GA1UEBwwHQm9semFubzEVMBMGA1UECgwM
RmxhdmlvIENvcGVzMRgwFgYDVQQDDA9mbGF2aW9jb3Blcy5jb20wHhcNMjYxMTA1
MDQ0NzIwWhcNMjcxMTA1MDQ0NzIwWjBiMQswCQYDVQQGEwJJVDEQMA4GA1UECAwH
Qm9semFubzEQMA4GA1UEBwwHQm9semFubzEVMBMGA1UECgwMRmxhdmlvIENvcGVz
MRgwFgYDVQQDDA9mbGF2aW9jb3Blcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC8Ebt6RXCfzE2Kx2fFrohkR8UaC+3Bnz30AJnOzpWpH7RIW1Rf
EE2RwyuiZGotlcT6KiBC9xfeqeo8qWze8TXf5qLC31JzmE65uMsnJKHqmohPH4IJ
F5qqJMpa1OsZ7OCr5ZvO+a29/7Kh9rs/eT2nxJ2z7i9/tEE1U2c6eI3rcLV94lc5
2dTPuexgAm1JLmqd1gQ3S80SMV0ProzdJr1pJyltwqD/zteWizIdoJwlSQbn4H2Z
/AEl4dmqkmZp2zRP3XG/BjZgDyYvlqucGyXB4DV/5wR0tptXvWOMLu/X6FxJPJu3
HzRpmDXfEmlhmlu98vd/2fPpaFLFjIc5AxOpAgMBAAGjgZUwgZIwHQYDVR0OBBYE
FBOUuxJhjU3h1fauYIgbX/794aASMB8GA1UdIwQYMBaAFBOUuxJhjU3h1fauYIgb
X/794aASMDUGA1UdEQQuMCyCD2ZsYXZpb2NvcGVzLmNvbYITd3d3LmZsYXZpb2Nv
cGVzLmNvbYcEwAACATAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIFoDANBgkqhkiG
9w0BAQsFAAOCAQEAbK5pt5h5dq5MgcD5VD/1qQX5hYpr8UaIm/pFafMEr1fNSIR5
mpzv2wbU5h030B6UYdbH/Xx7vhnaaJ7wF3fcwmDWxqN2tlv5wsLZqFJlZFhe7iOO
n2OqAQ9XPgU9sZeiJo6Qxt3+vMwfQLtL7Rx3I1Diome/eHCUpSV59JE6jz/DnFay
cdFtG7aNCCGwPri1aOOTRa6xS1QksVag0UMmRwGnG5Cd9RmhHtzy2a2OxydsfyxM
mD11IWg5zwQGNPs69PmFEmG3MnGJm+2wK7kKkX5EgXAQU/rk4U13axDAxCAqecDL
62MKHT9Kc3Zbhx3ilRfiz+Q/KCEYB2a6tbLJiA==
-----END CERTIFICATE-----
The page prints:
Certificates: 1
Certificate 1
Subject: CN=flaviocopes.com,O=Flavio Copes,L=Bolzano,ST=Bolzano,C=IT
Issuer: CN=flaviocopes.com,O=Flavio Copes,L=Bolzano,ST=Bolzano,C=IT
Serial: 0A1B2C3D
Version: v3
Not before: 2026-11-05T04:47:20Z
Not after: 2027-11-05T04:47:20Z
Signature: sha256WithRSAEncryption (1.2.840.113549.1.1.11)
Public key: RSA 2048, exponent 65537
SANs: DNS:flaviocopes.com, DNS:www.flaviocopes.com, IP:192.0.2.1
Basic constraints: critical, CA:FALSE
Key usage: digitalSignature, keyEncipherment
Signature value: 256 bytes
SHA-256: 71:32:9B:05:27:0E:5B:B8:2D:A6:66:AB:7F:DE:0C:F0:A9:54:6B:32:C4:B4:B5:DD:69:78:77:F5:DA:2F:3F:39
SHA-1: 90:09:B9:CD:08:28:88:AE:BC:02:5E:A8:F9:FA:8D:7C:45:FB:E8:0F
Extensions: subjectKeyIdentifier, authorityKeyIdentifier, subjectAltName, basicConstraints (critical), keyUsage
The subject line is RFC 4514 order. The common name comes first, then the organization, the city, the state, and the country. OpenSSL’s -nameopt RFC2253 prints the same string. The issuer line matches it, so the page adds a note: Subject and issuer use the same distinguished name. That note means the two strings are equal. It does not mean the signature was checked.
Serial 0A1B2C3D is the hex I passed to -set_serial. Version v3 is the integer 2 inside the certificate, which is how X.509 numbers versions. The two times are UTC. They come from a one-year -days 365 window that started when the file was created, 2026-11-05T04:47:20Z.
RSA 2048 is the bit length of the modulus, after the leading sign byte is dropped. The exponent 65537 is the second integer in the RSA key. The signature OID 1.2.840.113549.1.1.11 is the dotted form of sha256WithRSAEncryption. The signature value is 256 bytes because a 2048-bit RSA signature is 256 bytes. That length is a property of this key. A different key size would print a different length.
The SAN list is the order the extension stores the names. DNS names are text. The IP is four bytes, 192.0.2.1, which is the documentation address from the command, not a host that answers. Basic constraints is marked critical, and CA:FALSE means this certificate is not allowed to sign other certificates. Key usage lists the bits that are set: digital signature, then key encipherment. The bits that are clear are omitted.
The two fingerprints match openssl x509 -fingerprint -sha256 and -sha1 on this file. If you re-wrap the Base64 and the DER stays the same, the fingerprints stay the same.
An EC certificate that says it is a CA
A second file, made with openssl req -x509 -newkey ec and the curve prime256v1, decodes like this:
Subject: CN=blog.flaviocopes.com
Public key: EC P-256 (prime256v1)
Signature: ecdsa-with-SHA256 (1.2.840.10045.4.3.2)
SANs: DNS:blog.flaviocopes.com
Basic constraints: critical, CA:TRUE, path length: none
Signature value: 70 bytes
SHA-256: 9A:B7:EB:FD:21:A1:16:CD:C0:8F:87:98:82:90:1D:D4:AB:9F:28:21:E5:53:AD:20:75:F1:B3:85:9D:CA:99:9B
The command did not pass -addext basicConstraints. OpenSSL 3 still wrote CA:TRUE into this file, and it marked the extension critical. path length: none means the path length integer is absent. It does not mean the path can be any length. The signature value here is 70 bytes. ECDSA signatures vary in length, so do not treat 70 as the size of every P-256 signature.
P-256 (prime256v1) comes from the curve OID 1.2.840.10045.3.1.7 in the key parameters. If the page does not know a curve, it prints the dotted OID and does not invent a name.
A root you can check against a published fingerprint
Let’s Encrypt publishes ISRG Root X1 as a PEM file. The page reads it as subject and issuer CN=ISRG Root X1,O=Internet Security Research Group,C=US, serial 8210CFB0D240E3594463E0BB63828B00, not before 2015-06-04T11:04:38Z, not after 2035-06-04T11:04:38Z. The public key is RSA 4096, exponent 65537. Basic constraints is critical, CA:TRUE, path length: none. Key usage is critical, keyCertSign, cRLSign.
The SHA-256 fingerprint is:
96:BC:EC:06:26:49:76:F3:74:60:77:9A:CF:28:C5:A7:CF:E8:A3:C0:AA:E1:1A:8F:FC:EE:05:C0:BD:DF:08:C6
That is the fingerprint OpenSSL prints for the same PEM. The serial’s first hex digit is 8, so the integer’s high bit is set. DER stores a leading 00 so the integer stays positive. The page strips that one sign byte before it prints the hex. If you leave the 00 on, the serial will not match the usual fingerprint pages.
There is no SAN line of names. The summary says SANs: none.
What goes wrong
Paste a private key and the page stops:
This PEM block is a private key. Paste the certificate, not the key.
A file from openssl req often contains both server.key and server.cert if you concatenated them. Put only the certificate block in the box. If a key block sits beside a certificate, the key is skipped and a note says the page does not read keys. The certificate still decodes.
Paste ordinary text and you get:
No PEM certificate block found. The text needs a BEGIN CERTIFICATE line.
The label has to be CERTIFICATE or X509 CERTIFICATE. A CSR labeled CERTIFICATE REQUEST is skipped.
An Ed25519 certificate made with openssl req and signed by its own key has no version field and no extensions. The page says v1, prints SANs: none and Extensions: none, and adds: The version field is absent, which means v1. The full summary for that file is:
Certificates: 1
Certificate 1
Subject: CN=ed.flaviocopes.com
Issuer: CN=ed.flaviocopes.com
Serial: 07
Version: v1
Not before: 2026-11-05T04:47:20Z
Not after: 2026-12-05T04:47:20Z
Signature: Ed25519 (1.3.101.112)
Public key: Ed25519, 32-byte public key
SANs: none
Signature value: 64 bytes
SHA-256: 38:83:00:C8:3B:46:52:4C:71:31:D7:87:0A:2D:BB:9C:DC:AA:4C:F2:6C:92:59:A7:40:06:A6:D5:30:7A:0C:99
SHA-1: FC:0F:4F:CB:E6:9B:22:AF:9F:5C:6B:7A:A7:18:97:29:37:7F:BC:1D
Extensions: none
Serial 07 keeps the leading zero so the hex has an even number of digits. The public key is 32 bytes, which is the Ed25519 key length. The signature value is 64 bytes.
A chain is several PEM blocks one after another. The page numbers them, Certificate 1 then Certificate 2, up to 8. Broken Base64 inside a block names that certificate and says the base64 is not valid. Indefinite-length DER is rejected, because this page reads DER only.
The page never compares not before and not after with the current time. A certificate that is not yet valid and one that expired years ago look the same here, apart from the dates themselves. That is deliberate. A page that said “expired” would change its answer as the clock moved, and a tutorial could not quote it.
When the page is the wrong tool
Use this page when you have a PEM and you want the names, the dates, the key type, and the fingerprint without installing anything. Paste it, read the summary, copy the SHA-256 line into a note.
Use openssl x509 -in server.cert -noout -text when you want the same fields plus the parts this page leaves as names only, such as the raw authority key identifier and the policy OIDs. Use openssl verify when you need to know whether a chain signs this certificate. This page will not tell you that. A matching subject and issuer is a string comparison.
A JWT is a different object. It is three Base64url parts, and the JWT decoder is the page for that. A certificate fingerprint is a hash of the whole DER certificate. A Subresource Integrity hash is a hash of a script or a stylesheet. The hash generator will hash whatever text you paste, which is the PEM text, not the DER, so it will not match the SHA-256 line on this page.
If you are creating the certificate rather than reading one, the walkthrough is an Express HTTPS server with a self-signed certificate. After openssl req writes server.cert, paste that file here and check the common name and the SAN before you point a browser at it. Installing the result on a Mac is covered in how to install a local SSL certificate in macOS.
Try the PEM Certificate Decoder
Paste a PEM certificate and read the subject, issuer, serial, dates, SANs, key, and fingerprints. The signature is not checked. Decoding stays in the browser.
Open the PEM Certificate Decoder →
Related tools
- JWT Decoder: Decode JSON Web Tokens client-side: header, payload, claims explained, and expiry status. Nothing leaves your browser.
- Subresource Integrity Generator: Hash a file or text in your browser and copy a Subresource Integrity value for a script or stylesheet. SHA-256, SHA-384, and SHA-512.
- Hash Generator: SHA-1/256/384/512 and MD5 digests plus HMAC signing, from text or files — hex or Base64, all in your browser.
Want me to talk about your product? You can sponsor this site.