When working with OpenSSL and cryptographic operations, developers frequently encounter two similar-looking PEM formats:
// Certificate format
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAL...
-----END CERTIFICATE-----
// Public key format
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG...
-----END PUBLIC KEY-----
A certificate contains the public key plus additional metadata:
- Subject information (CN, O, OU fields)
- Issuer details
- Validity period
- Signature algorithm
- X.509 extensions
Whereas a public key contains just the raw key material in SubjectPublicKeyInfo (SPKI) format.
Extracting the public key from a certificate:
openssl x509 -in cert.pem -pubkey -noout > pubkey.pem
Converting between formats (DER/PEM):
# Certificate to DER
openssl x509 -in cert.pem -outform DER -out cert.der
# Public key to DER
openssl rsa -pubin -in pubkey.pem -outform DER -out pubkey.der
Certificate (BEGIN CERTIFICATE) is required for:
- TLS handshakes
- Code signing verification
- PKI chain validation
Public Key (BEGIN PUBLIC KEY) is used for:
- Asymmetric encryption
- JWT signature verification
- Embedding in configurations
The certificate's public key (in SPKI format) is actually embedded within the certificate structure. You can inspect this using:
openssl asn1parse -in cert.pem -strparse 19
This reveals the identical public key bits that would appear in a standalone PUBLIC KEY file.
1. Trying to use a certificate where a public key is expected (e.g., in JWT libraries):
// Wrong
const publicKey = fs.readFileSync('cert.pem');
// Right
const publicKey = fs.readFileSync('pubkey.pem');
2. Assuming both formats work interchangeably in OpenSSL commands
When working with OpenSSL and cryptographic operations, you'll encounter two similar-looking PEM formats:
// Certificate format
-----BEGIN CERTIFICATE-----
BASE64_ENCODED_DATA
-----END CERTIFICATE-----
// Public key format
-----BEGIN PUBLIC KEY-----
BASE64_ENCODED_DATA
-----END PUBLIC KEY-----
A CERTIFICATE PEM contains:
- Public key
- Identity information (subject)
- Issuer details
- Validity period
- Signature algorithm
- Digital signature
A PUBLIC KEY PEM contains only the raw public key material in SubjectPublicKeyInfo format (ASN.1 DER encoded).
Here's how to extract the public key from a certificate:
openssl x509 -in certificate.pem -pubkey -noout > publickey.pem
To inspect the ASN.1 structure:
openssl asn1parse -in certificate.pem
openssl asn1parse -in publickey.pem
Certificate PEMs are used when:
// Node.js HTTPS server
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt') // CERTIFICATE PEM
};
Public Key PEMs are used when:
// Python RSA verification
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
with open("public_key.pem", "rb") as key_file:
public_key = serialization.load_pem_public_key(
key_file.read()
)
The Certificate structure includes:
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING }
While the Public Key structure is simpler:
SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
Certificate verification requires chain validation:
openssl verify -CAfile root-ca.crt intermediate.crt
Public key verification just checks key format:
openssl rsa -in publickey.pem -pubin -check -noout