ENC424J600, Gunnebo Business Solutions, Microchip, PIC24, PIC24FJ256GB206, Security, TLS/SSL

x.509 Certificates Explained

x.509 is an international digital certificate used for verification in different internet protocols. It can be applied to verify either a person or a website name, an email address, etc. Its main advantage is that it allows minimising problems which might occur while encrypting and exchanging confidential information, like data interception or cracking. While using the certificate system, you may be quite sure you have encrypted your data in such a way that it can be read only by the intended recipient.

The system implies the use of public key encryption, which actually means you deal with two keys, from which one is made public and the other is kept private by the owner of the key. Typically, a message is encrypted using the public key.But it is only the owner of the private key who can decrypt it. Alternatively, a message is encrypted with the private key and decrypted with the public one, for example, when it goes about the signature which is created by the owner of the private key and then can be verified by any other person.

The complexity of the mathematical relationship between the public key and the private key secures private key from cracking.Moreover,  there exist time limitations within which the certificate is active.  And there can be other constraints on the use of the certificate.

The certificate contains a digital signature, the purpose of which is to certify the validity of the information in the certificate. The certificate cannot be trusted without verification of the signature. The signature is generated in such a way that it will not verify in case any important information in the certificate has been tampered with.

x.509 Structure

Let’s decode a binary hex display for an exemplary X.509 certificate. The first 4 bytes constitute the ASN.1 sequence DER encoding with remaining bytes (0x04A2). Then we deal with the exact binary data covered by the signature. Here belong the required certificate fields which include ordered sequence of certificate version,  signature algorithm ID, validity period, serial number, issuer, subject and public key. After that, optional extensions, encoded in ASN.1 format, are placed (detailed specification can be found in IEFT RFC 3280). The encoded signature-algorithm specifier goes next. And finally, the actual PKCS #1 v1.5 signature, which is 128 bytes, or the same size as the public key corresponding to the private key for the certificate.

Field Identifiers

BER encoding

Basic Encoding Rules (BER) define a self-describing and self-delimiting format for encoding ASN.1 data structures. Data elements are encoded by means of TLV (type-length-value) encoding in the following order: identifier octets, length octets, actual data elements (content octets), and, sometimes, end-of-content markers. It allows a receiver to decode the ASN.1 data from an incomplete stream, regardless of content, size or semantic meaning of the data.

DER encoding

Distinguished Encoding Rules (DER) were created to meet the requirements of the x.509 specification for the secure data transfer and are considered to be the most popular encoding format to store X.509 certificates in files. They constitute a restricted variant of BER aimed at producing unambiguous transfer syntax for data structures described by ASN.1. In DER encodings, all but one sender’s options are removed.

You would use DER  when you need a unique encoding, such as in cryptography. It ensures that a digitally signed data structure obtains a unique serialized representation. The identifier octets for the BER encoding are 30 if the chosen alternative is certificate, and a0 if the chosen alternative is extended certificate.

Field Types

Decompiled Certificate

Here you may see a description of the certificate format from RFC 5280, slightly cleaned up to improve readability. It will be helpful to refer to it while writing the decoding code.

The v2 issuerUniqueID and subjectUniqueID are almost never used in practice, instead equivalent v3 extensions (subjectAlternativeName and issuerAlternativeName) are used. You can safely assume they will not appear. On the other hand, v3 extensions are almost always used.

Here is an annotation/description of the contents of a particular certificate:


As it has already been mentioned, ASN.1 encoding uses a TLV (tag, length, value) system, always in that order. The tag and the length may be encoded over multiple bytes.

The tag type is a bit field, the most important values for certificate parsing are:

Decoding the tag

The tag can be a multi-byte value, and is potentially encoded using a variable length scheme.

For typical tags (with value <= 31), the encoding is a single byte, where the bottom 5 bits specify the ‘type’, bit 6 specifies if this is a primitive (such as an integer or a string) or constructed object (such as a sequence or a set), and the top two bits specify the ‘class’: universal (b00), context specific (b10), application (b01), and private (b11). Ordinarily only ‘universal’ class is used, but some complex applications use the other class types. It is important to know that in the case of a non-universal class type, the tag type *does not* correspond to the ordinary tag type, for example if the type value was 2 (integer), but the class type was context_specific, this would probably not represent an integer. The application would have to rely on some specific knowledge of the format in order to parse it completely.

As an example, the first byte of the certificate is 0x30, or 0x10 | 0x20, meaning this is a constructed sequence with universal class type.

A constructed sequence means several values follow, each of which is made up of their respective encodings.

Decoding the length

Length fields are also variables, and come in two forms.

In the short form, which is one octet long and can represent values up to 127 bytes, the top bit has value 0, and bits 1-7 give the length.

The long form is between 2 and 127 octets (though ordinarily only 2-4 octets would be seen). The top bit of the first octet has value 1, and bits 1-7 give the number of length octets which follow. The following length is encoded base-256 in that number of bytes.

The next bytes of the certificate are 82 04 80, so, there are 2 bytes in the length field, and they have value 0x480, so following 1152 bytes make up the next object. Thus we know the specified length of a sequence that follows. This is the outer sequence of the Certificate type:

We know then that this will be followed by a second sequence header, the one prefixing the TBSCertificate (TBS == “to be signed”, as it is followed by the signature algorithm ID and signature value which authenticate the TBSCertificate values).

And, in fact, we see the next 4 bytes are 30 82 03 68, indicating second (nested) sequence of length 0x368 == 872 bytes.

Explicit Tags

Certain optional values are tagged using ‘explicit tags’, which means the value is tagged twice. This allows distinguishing optional values. In an X.509 certificate, it first occurs at the beginning. Originally (in v1 format), the first field of a certificate was an INTEGER value with the serial number. However in the later v2 and v3 formats an INTEGER value for the version number was added at the beginning. There should be some way for applications to distinguish between a v3 certificate (with a version field of 2) and a v1 certificate with a serial number of 2.

Explicit tags are encoded using a class type other than universal. Normally, the class type used for explicit tags is the context specific class; the choice depends on the ASN.1 definition of the objects. In the case of X.509 certificates, context specific tags are always used.

We may observe this when decoding the first value of the TBSCertificate sequence, with
the bytes:

A0 03 02 01 02

Here A0 is the type byte. It is a combination of the CONSTRUCTED (x20) and CONTEXT_SPECIFIC (x80) bit fields. The low 5 bits of the type are 0, matching what we would expect for the TBSCertificate.Version field:

version [0] EXPLICIT Version DEFAULT v1,

It is also possible that this version field might not appear, in the case of a v1 certificate. This is quite common for older trusted CA certificates.

It is followed by x03, indicating 3 bytes follow in the value. Because the tag is explicit, it is followed by a normal INTEGER TLV 02 01 02, that is, an INTEGER tag, followed by a length byte of 1, followed by the single byte of the value (2). Thus, we know the version field is 2, or that this is a v3 certificate.

Further Decoding

Decoding the Serial Number

Progressing onward through the bytes of the certificate, we next encounter this block (the serial number).

02 08 01 18 F0 44 A8 F3 18 92

Here we have a type of 2 (integer), followed by a length field of 8, followed by the 8 byte value of the serial number, 79077171161929874 (0x0118F044A8F31892). Here, the serial number is 64 bits, but do not assume the serial number is of any specific size; many certificates use randomly generated 128 or 160 bit serial numbers.

Decoding the Algorithm ID

The next element of the certificate is the signature algorithm identifier.

30 0D 06 09 2A 86 48 86 F7 0D 01 01 0B 05 00

Here the type is 30 (sequence+constructed), and the length is 13. The two elements of an algorithm identifier are:

And, in fact, we see that the first byte of the value of the sequence is x06, or the tag type for object identifiers. This is followed by the length of the OID, 9 bytes. The encoded value of the OID is then 2A 86 48 86 F7 0D 01 01 0B

In ASN.1, OIDs are a sequence of integer values representing a hierarchical allocation scheme. The first two values in the OID are encoded in the leading initial byte (OIDs always consist of at least 2 components). This is done by 40*b[0] + b[1]; this is reversible because both of the initial values are restricted to be strictly less than 40. So, 0x2A is the encoding of 1.2, which is the first part of the sequence, since 0x2A = 42, and dividing by 40 gives the first component, while taking 42 modulo 40 gives the second component.

The remaining components are encoded by a variable length encoding scheme. Each byte encodes 7 bits of the component value, with the high bit being used to indicate if any further bytes follow. The third byte of the value is 86, so we know that another byte follows for the third component. The fourth byte is 48, which does not have the high bit set. So, the component value is 6 * 128 + 0x48, or 840. Following that is 86,F7,0D, which decodes to 6*128*128 + 119*128 + 0xD, or 113549. Then, the single-byte values are 01, 01, 0B. So, the final decoded OID is 1.2.840.113549.1.1.11 – which we can find in sha256WithRSAEncryption (despite the name, this is actually used for signature operations).

In an AlgorithmIdentifier, the parameters field can take on any type, depending on what algorithm is identified by the OID. With RSA certificates, the parameters field may be completely empty (omitted), or NULL (0500). Here we see it is an explicit NULL value.

Decoding the Issuer Name

The next constituent in the certificate is the issuer name. The name type in X.509, if taken in its full generality, is exceedingly complicated (for example, it allows encoding teletex addresses, obsolete since the 80s). Only a narrow subset of the Name type is used in the Internet PKI. A simplified description of the Name structure is:

Ordinally the value types are some form of ASN.1 string. ASN.1 supports many different string types; RFC 5280 allows

The most commonly used are UTF8String (UTF-8), BMPString (16-bit Unicode), and PrintableString (ASCII subset).

Now let us decode the issuer Name field from the certificate:


The first byte is the expected type, 30 (sequence), followed by the length x49 (73 bytes).
The value is a SET, which has a type code of x11, combined with the constructed bit to produce x31:


The first SET is of length 0xB, or 11 bytes:


Here we see the SEQUENCE corresponding to the AttributeTypeAndValue structure.
It has the expected length of 9, and consists of an OID (0603550406, which decodes to or “Country”), followed by the value of 13025553. The type field is x13, for “printable string”, followed by the 2 byte value 5553 (or ASCII “US”).

The second SET is of length x13 (19 bytes):


The nested AttributeTypeAndValue SEQUENCE has the expected length of 17:


This breaks down to

3011 – SEQUENCE, length 17
060355040A – OID, (“Organization”)
130A476F6F676C6520496E63 – Printable String, length 10, “Google Inc”

The third SET is:


The AttributeTypeAndValue is

3023 – SEQUENCE, length 35
0603550403 – OID, (“Common Name”)

The string decodes to “Google Internet Authority G2”

Decoding the Validity Information

The Validity information specifies when the certificate becomes valid, and when it expires:

Here CHOICE means that one or the other time type must be used. A UTCTime is encoded as YYMMDDHHMMSSZ, and GeneralizedTime as YYMMDDHHMMSSZ. In both cases, Z is a literal representation of ‘Zulu’ time (or GMT).

[ASN.1 allows specifying other time zones, however, Zulu time is strictly required by the Internet PKIX profile and in practice, most certificate implementations will reject any non-GMT time as invalid. Simple checking that the final byte is ‘Z’ is sufficient.]

In our example certificate, the Validity sequence is:

170d3137303632383130303734365a – notBefore
170d3137303932303039323730305a – notAfter

So, a SEQUENCE is of length 0x1e (30). The first value is 170d3137303632383130303734365a, where x17 is the type for UTCTime (GeneralizedTime used x18), and the length is 13 bytes. The value is 3137303632383130303734365a, or ASCII “170628100746Z”. For UTCTime, the leading ’20’ of the year is implicit, with the rule that years >= 49 should instead be treated as 19YY. (So, for instance, a UTCTime starting with “97” should be treated as 1997, rather than 2097). The notAfter “137303932303039323730305a” decodes to 170920092700Z.

So, this certificate is valid starting on Jun 28 2017 at 10:07:46, and expiring on Sep 20 2017 at 09:27:00.

Decoding the Subject Name

It is decoded in the same manner as the issuer Name:


Decoding the Public Key

The next sequence is the public key associated with the subject.

Because the length of the public key does not fit into a single byte, the long-form length encoding is used:

30820122 — sequence with a length field 2 bytes long, length 0x122

The sequence has the value, which represents the structure:

First, we decode the AlgorithmIdentifier in a pretty much the same way the signature algorithm was decoded:


Here the nested (AlgorithmIdentifier) sequence has length xD, and value

06092a864886f70d010101 — OID encoding “1.2.840.113549.1.1.1” for RSA
0500 — NULL parameter encoding

This is followed by a BIT STRING, which contains the public key. The  BIT STRING encoding here is somewhat arbitrary, since nested inside the BIT STRING, is another field, which encodes the actual (algorithm-specific) key parameters:


The first bytes here are:


x03 is the type of BIT STRING, and the length field is 2 bytes long with value x10F. The following byte specifies the number of unused bits in the first octet of the bit string encoding. It can safely be ignored in this context.

This is followed by the encoding of the RSA key, which has a structure of

[This type comes from PKCS #1, see also RFC 3447]

The value in the BIT STRING is:


This starts as a SEQUENCE with 2-byte length field (value x10a), which matches up with the length of the surrounding bit string. Then we have an integer (02) with length x101 or 257 bytes. The value is:


The leading 00 byte is included because the high bit of the actual modulus is set, and the initial leading bit in BER encoding signifies a *negative* number. So, this is actually a 256 byte (or 2048 bit) integer. This is the public RSA modulus.

This is followed by the second value, the RSA exponent:


By now, we can easily read this as an integer with length 3 and value x10001 or decimal 65537.

Decoding the Extensions

The remainder of the certificate is the extensions.

The extnID specifies what the extension type is, and the extnValue provides any associated value (this will be specific to the extension type). The “critical” value is used as follows: if an implementation processes the extensions, and does not understand an extension marked critical, it should reject the certificate rather than continue. This is used to mark extensions which are essential for the correct processing of the certificate. Normal (non-critical) extensions can instead be ignored if not understood.

In the example certificate, the extensions have value:


The first byte is 0xA3, or CONTEXT SPECIFIC with tag value 3. The length is x14b, and it begins a SEQUENCE (x30) with length 0x147. The first Extension value in the sequence is


The OID (0603551d25) decodes as (extended key usage). This extension has a definition of SEQUENCE OF OID, which we can see in the value


The 04 is the OCTET STRING tag, in which the extension value is nested. It has length x16 (22 bytes):


This nested sequence decodes to the OIDs and, which are the serverAuth and clientAuth extended usage OIDs.

Further extensions appear. Decoding them is left as an exercise for the reader:





This one is notable for being marked as critical. It starts with the OID as normal 0603551d13 ( “Basic Constraints”), then is followed by the optional critical BOOLEAN 0101 (type=1, boolean, followed by either 00 or 01 for false/true resp). The BasicConstraints allows specifying if the certificate is or is not a certificate authority, and if it is a CA, how deep a certificate path issued by it may go. (For instance, if pathLenConstraint == 1, then the CA can issue certificates, but certificates issued by the CA cannot, even if they have the CA bit set).

We may observe that the value of the extension is “04023000”, or a OCTET STRING of length 2, containing a SEQUENCE that is … completely empty! That’s because cA defaults to FALSE, and the pathLenConstraint is optional (and not needed for non-CA certificates). So, the mere presence of the extension is sufficient to mark the certificate as not being a CA:




Decoding the Signature

At this point (when the decoding of the extensions has been completed), the entire sequence of the TBSCertificate has been consumed. What remains is the signature information in the outer Certificate sequence:

First, there is the signatureAlgorithm OID


This matches exactly with the signatureAlgorithm value, which is included in
the TBSCertificate.

Finally, there is the signature generated by the issuer, which authenticates
the information:

0382010100 — BIT STRING header, length 0x101, 0 bits unused


Thanx to Jack Lloyd and Zoran Ristic for their invaluable input into this post 🙂

1 thought on “x.509 Certificates Explained”

Leave a Reply