ASN.1 key structures in DER and PEM

Introduction

PEM and the ASN.1 structures that are used in saving cryptographic keys and certificates in a portable format are very popular, yet they have not been documented extensively. It is important to understand the structure inside each DER or PEM formatted file, yet this can be challenging to find. Below, we provide this information.

ASN.1 and DER encoding

The RSA, PKCS#1, SSL and TLS communities use the Distinguished Encoding Rules (DER) encoding of ASN.1 to represent keys and certificates in a portable format. The certificate and key information is stored in the binary DER for ASN.1, and applications providing RSA, SSL and TLS should use DER encoding to parse the data. While ASN.1 is a complex representation format and can be difficult to understand, it also has its merits.

Some good resources on ASN.1 and DER

PEM files

Because DER encoding results in a truly binary representation of the encoded data, the PEM format was devised for sending these in an encoding of printable characters, so that they can be mailed. We focus on the PEM format below.

Most PEM formatted files encountered when exporting an RSA private or public key, or X509 certificates, are generated by OpenSSL. PEM files are essentially base64 encoded versions of the DER encoded data. A header and footer around the data designate what kind of data is inside the DER encoded string. An example of a PEM encoded file is:

    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMYfnvWtC8Id5bPKae5yXSxQTt
    +Zpul6AnnZWfI2TtIarvjHBFUtXRo96y7hoL4VWOPKGCsRqMFDkrbeUjRrx8iL91
    4/srnyf6sh9c8Zk04xEOpK1ypvBz+Ks4uZObtjnnitf0NBGdjMKxveTq+VE7BWUI
    yQjtQ8mbDOsiLLvh7wIDAQAB
    -----END PUBLIC KEY-----

The first and last line indicate the DER format that should be expected inside. The data inside is a base64 encoded version of the DER encoded information.

Formats

Below, we discuss the different formats and the structure that you should expect to see in each file.

RSA public Key file (PKCS#1)

The RSA Public key PEM file is specific for RSA keys.

It starts and ends with the tags:

    -----BEGIN RSA PUBLIC KEY-----
    BASE64 ENCODED DATA
    -----END RSA PUBLIC KEY-----

Within the base64 encoded data, the following DER structure is present:

    RSAPublicKey ::= SEQUENCE {
    modulus   INTEGER,  -- n
    publicExponentINTEGER   -- e
    }

Public Key file (PKCS#8)

Because RSA is not used exclusively inside X509, SSL and TLS, a more generic key format, PKCS#8, is available. It identifies the type of public key and contains the relevant data.

It starts and ends with the tags:

    -----BEGIN PUBLIC KEY-----
    BASE64 ENCODED DATA
    -----END PUBLIC KEY-----

Within the base64 encoded data, the following DER structure is present:

    PublicKeyInfo ::= SEQUENCE {
      algorithm   AlgorithmIdentifier,
      PublicKey   BIT STRING
    }
    
    AlgorithmIdentifier ::= SEQUENCE {
      algorithm   OBJECT IDENTIFIER,
      parameters  ANY DEFINED BY algorithm OPTIONAL
    }

So, for an RSA public key, the OID is 1.2.840.113549.1.1.1, and the RSAPublicKey is the PublicKey key data bitstring.

RSA private Key file (PKCS#1)

The RSA private key PEM file is specific for RSA keys.

It starts and ends with the tags:

    -----BEGIN RSA PRIVATE KEY-----
    BASE64 ENCODED DATA
    -----END RSA PRIVATE KEY-----

Within the base64 encoded data, the following DER structure is present:

    RSAPrivateKey ::= SEQUENCE {
      version   Version,
      modulus   INTEGER,  -- n
      publicExponentINTEGER,  -- e
      privateExponent   INTEGER,  -- d
      prime1INTEGER,  -- p
      prime2INTEGER,  -- q
      exponent1 INTEGER,  -- d mod (p-1)
      exponent2 INTEGER,  -- d mod (q-1)
      coefficient   INTEGER,  -- (inverse of q) mod p
      otherPrimeInfos   OtherPrimeInfos OPTIONAL
    }

Private Key file (PKCS#8)

Because RSA is not used exclusively inside X509, SSL and TLS, a more generic key format, PKCS#8, is available. It identifies the type of private key and contains the relevant data.

The unencrypted PKCS#8 encoded data starts and ends with the tags:

    -----BEGIN PRIVATE KEY-----
    BASE64 ENCODED DATA
    -----END PRIVATE KEY-----

Within the base64 encoded data, the following DER structure is present:

    PrivateKeyInfo ::= SEQUENCE {
      version Version,
      algorithm   AlgorithmIdentifier,
      PrivateKey  BIT STRING
    }
    
    AlgorithmIdentifier ::= SEQUENCE {
      algorithm   OBJECT IDENTIFIER,
      parameters  ANY DEFINED BY algorithm OPTIONAL
    }

So for an RSA private key, the OID is 1.2.840.113549.1.1.1, and the RSAPrivateKey is the PrivateKey key data bitstring.

The encrypted PKCS#8 encoded data starts and ends with the tags:

    -----BEGIN ENCRYPTED PRIVATE KEY-----
    BASE64 ENCODED DATA
    -----END ENCRYPTED PRIVATE KEY-----

Within the base64 encoded data, the following DER structure is present:

    EncryptedPrivateKeyInfo ::= SEQUENCE {
      encryptionAlgorithm  EncryptionAlgorithmIdentifier,
      encryptedDataEncryptedData
    }
    
    EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
    
    EncryptedData ::= OCTET STRING

The EncryptedData OCTET STRING is a PKCS#8 PrivateKeyInfo, as described earlier.

Useful tools

OpenSSL

openssl asn1parse -inform DER

Limitation: gives up on invalid ASN.1.

To customize which OIDs are recognized, you can use an OID file — see NOTES in man asn1parse.

Also, many subcommands that work with ASN.1 formatted data (openssl x509, openssl pkey, etc.) have a -text option to dump a text representation of most of the data.

dumpasn1

dumpasn1 is a utility by Peter Gutmann. It is available as a Debian/Ubuntu package. It’s a standalone single-file C program plus a configuration file with a list of OIDs.

LAPO ASN.1 JavaScript decoder

Highlights the correspondence between the hex data and pieces of the decoded tree.

  • Online version.

  • Offline version: Unpack the zip in an empty directory and point a browser at the index.html file.

asn1_indent

asn1_indent is a home-made script that just splits the nested SEQUENCES from ASN.1 data. Useful when inspecting or tweaking data to form bad-case test cases.