Is this certificate DER or PEM encoded ? It turns out, both at the same time
On March 15, 2025 by Sosthène Guédon
X509 certificate can be encoded either as DER
or PEM
.
DER
encoding is an efficient binary format, while PEM
encoding is a wrapper around the Base 64 DER
encoding of the certificate.
Usually, when dealing with a specific certificate, you know beforehand whether it's encoded as DER
or PEM
.
For example, in the opennssl
CLI, you can give it the -inform
parameter, which accepts either DER
or PEM
.
However, what if don't know the encoding of the certificate, can you figure it out on the fly?
The PEM format is defined in RFC 7468. It has three parts: a preamble, in the form of -----BEGIN CERTIFICATE-----
followed by the Base 64 encoding of the DER version of the certificate, then the trailer -----END CERTIFICATE-----
.
This can make one think that it's possible to differentiate the two formats of the file by just checking whether the file starts with -----BEGIN CERTIFICATE-----
. However, the spec says that this is not enough:
Data before the encapsulation boundaries are permitted, and parsers MUST NOT malfunction when processing such data.
This means that a PEM-encoded certificate can include data before the certificate itself. This means that if we can embed a PEM
encoded certificate in one of the values inside a DER
encoded certificate, we will have one file that can be parsed as two distinct certificates depending on whether it's interpreted as PEM
or DER
encoding.
For example, here is a python script that generates a certificate, and then interprets it as two different values, parsing it once assuming it's DER
encoded:
=
=
=
=
=
=
=
=
=
=
The script is pretty simple, we generate a P256 keypair, then a self-signed certificate, except that in the Domain Component (DC
) of the certificate, we include a full PEM
encoded certificate. Then we add all the necessary values to build a certificate.
We need to include newlines before the preamble and after the trailer, otherwise the openssl
CLI tool will not accept the certificate.
The script then writes the certificate in the doublecert.der
file.
We can then parse it:
- as
DER
:openssl x509 -in doublecert.der -noout -text -inform DER
- as
PEM
:openssl x509 -in doublecert.der -noout -text -inform PEM
This will give us two distinct certificates!
The DER
one, which includes inside of it the PEM
certificate
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
4b:10:47:12:ac:84:ab:42:59:12:67:a4:24:8e:23:d0:25:5e:81:70
Signature Algorithm: ED25519
Issuer: DC=com + DC=example, CN=demo + CN=example + CN=certificate
Validity
Not Before: Jan 1 00:00:00 2000 GMT
Not After : Jan 1 00:00:00 2099 GMT
Subject: DC=com + DC=example + DC=
-----BEGIN CERTIFICATE-----
MIIBgDCCATKgAwIBAgIUUZedX2PRkE2W95Tcf0KvyT89kVEwBQYDK2VwMF8xKjAR
BgoJkiaJk/IsZAEZFgNjb20wFQYKCZImiZPyLGQBGRYHZXhhbXBsZTExMAsGA1UE
AwwEZGVtbzAOBgNVBAMMB2V4YW1wbGUwEgYDVQQDDAtjZXJ0aWZpY2F0ZTAgFw0w
MDAxMDEwMDAwMDBaGA8yMDk5MDEwMTAwMDAwMFowXzEqMBEGCgmSJomT8ixkARkW
A2NvbTAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMTEwCwYDVQQDDARkZW1vMA4GA1UE
AwwHZXhhbXBsZTASBgNVBAMMC2NlcnRpZmljYXRlMCowBQYDK2VwAyEATX1Ud7XN
vdIv02Mf3gZSR+oFvhYEX3xWKamUUXThtK4wBQYDK2VwA0EA9QaR6uz/9i6dHKmP
v20IH22aLrczHybXTXpQ59zNKHEN69G2kBnd0ckV/WJ+bGCTvinulpOHY5SeJ64O
I7csAw==
-----END CERTIFICATE-----
, CN=demo + CN=example + CN=certificate
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
3e:8e:f0:91:09:07:b4:7b:df:f8:de:8d:a8:75:29:
a2:11:06:e3:38:88:a1:e0:b6:90:06:e7:61:ff:d7:
bc:50
Signature Algorithm: ED25519
Signature Value:
16:fd:6c:6f:33:e3:4e:b4:94:1b:cf:d5:9a:0e:ce:4c:dd:47:
65:36:a8:03:5b:a0:c5:04:ac:46:34:0c:2b:55:7b:3c:f8:e3:
db:7a:f7:b4:77:f6:71:56:8a:fd:71:31:cb:25:62:a0:98:c5:
2e:e4:e4:49:7b:eb:d9:43:c0:06
And the PEM
certificate, which is much simpler:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
51:97:9d:5f:63:d1:90:4d:96:f7:94:dc:7f:42:af:c9:3f:3d:91:51
Signature Algorithm: ED25519
Issuer: DC=com + DC=example, CN=demo + CN=example + CN=certificate
Validity
Not Before: Jan 1 00:00:00 2000 GMT
Not After : Jan 1 00:00:00 2099 GMT
Subject: DC=com + DC=example, CN=demo + CN=example + CN=certificate
Subject Public Key Info:
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
4d:7d:54:77:b5:cd:bd:d2:2f:d3:63:1f:de:06:52:
47:ea:05:be:16:04:5f:7c:56:29:a9:94:51:74:e1:
b4:ae
Signature Algorithm: ED25519
Signature Value:
f5:06:91:ea:ec:ff:f6:2e:9d:1c:a9:8f:bf:6d:08:1f:6d:9a:
2e:b7:33:1f:26:d7:4d:7a:50:e7:dc:cd:28:71:0d:eb:d1:b6:
90:19:dd:d1:c9:15:fd:62:7e:6c:60:93:be:29:ee:96:93:87:
63:94:9e:27:ae:0e:23:b7:2c:03
This makes it very obvious of what is happening, but it would also be possible to store the PEM
certificate in the binary data of an X509 extension which would not be shown in such an explicit way by most tools.
Now the question is: Is there somewhere a system that includes two parsers that make different assumptions on the format of the certificate and could lead to making different interpretations, leading to a security vulnerability.
This post was updated to make it work with the openssl
CLI, thanks to the idea to add the missing newlines by Richard Levitte