I'm parsing x509, versions 1 and 3. I found the version field in the v3 cert, but I have two v1 certs (one from my organization, one I generated via OpenSSL) and in both of these the first non-sequence field is the serial number integer:

    > openssl asn1parse -in ca.der -inform DER
    0:d=0  hl=4 l= 645 cons: SEQUENCE          
    4:d=1  hl=4 l= 494 cons: SEQUENCE          
    8:d=2  hl=2 l=   9 prim: INTEGER           :B7D50AE5F7DB09FD

The reason I'm confused is that the wording in RFC 1422 seems to quite clearly suggest that the version is an actual field and that it is the first field, before the serial number:

  3.3  Certificate Definition

   Certificates are central to the key management architecture for X.509
   and PEM.  This section provides an overview of the syntax and a
   description of the semantics of certificates.  Appendix A includes
   the ASN.1 syntax for certificates.   A certificate includes the
   following contents:

       1.  version

       2.  serial number

       3.  signature (algorithm ID and parameters)

       4.  issuer name

       5.  validity period

       6.  subject name

       7.  subject public key (and associated algorithm ID)

   3.3.1  Version Number

   The version number field is intended to facilitate orderly changes in
   certificate formats over time.  The initial version number for
   certificates used in PEM is the X.509 default which has a value of
   zero (0), indicating the 1988 version.  PEM implementations are
   encouraged to accept later versions as they are endorsed by

I'm looking at RFC 1422 because wikipedia says:

"The structure of version 1 is given in RFC 1422."

My expectation is to find an integer field of length 1 with value 0x00 before the serial number in a v1 cert.

Is version 0x00 (v1) just implicit? That is, will there only be a version field if version > 0x00? What does this mean for the version field description in RFC?


Short answer: The RFC (1422) mentioned by Wikipedia is not correct. Not only is the format mentioned in an earlier RFC (1114), but that RFC correctly references the ITU document which does have the original description: the 11/1988 version of the X.509, available here. In section 8.7(c) of that document, it is stated that "if the value of a type is its default value, it shall be absent." Therefore, a version 1 cert is the default version, and should not be included in the cert that is signed.

Longer answer, with a bit of explanation:

The newer X.509 specification (which is not in an RFC, but the ITU document is not freely available) that relates to public key certificates is most recently documented in RFC 5280. Here's a part of section 4.1, which covers the ASN.1 encoding:

Certificate  ::=  SEQUENCE  {
    tbsCertificate       TBSCertificate,
    signatureAlgorithm   AlgorithmIdentifier,
    signatureValue       BIT STRING  }

TBSCertificate  ::=  SEQUENCE  {
    version         [0]  EXPLICIT Version DEFAULT v1,
    serialNumber         CertificateSerialNumber,
    signature            AlgorithmIdentifier,
    issuer               Name,
    validity             Validity,
    subject              Name,
    subjectPublicKeyInfo SubjectPublicKeyInfo,
    issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
                         -- If present, version MUST be v2 or v3
    subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
                         -- If present, version MUST be v2 or v3
    extensions      [3]  EXPLICIT Extensions OPTIONAL
                         -- If present, version MUST be v3

Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }

Two parts of this notation that refer explicitly to versioning are the version tag itself, and the issuerUniqueID, subjectUniqueID, and extensions sections. The version tag says that the default is v1, and the original spec said that defaults should be absent. When the later revisions of X.509 added versions, those new versions were no longer default, and did appear in the signed cert.

Although you can use this newer specification to generate a version 1 pubkey cert, there's really no need. You can label a new cert with the version 3 tag (using the integer 2), and just choose not to use any of the new sections.

The new spec is backwards compatible with the original. ASN.1 is used for encoding in all of these cert revisions. ASN.1 is very compact, and mostly relies on the defined format of a structure to determine what any given field means. That is, what you see with all of the "::=" syntax is a definition that you follow when parsing the blob of data you get. The first field listed in the TBSCertificate sequence is the version, which is an integer. The next field is the serialNumber, which is also an integer. If the version field is missing, which it may be if the cert is a version 1, it could be really difficult to know that the serialNumber is not the version. (It is possible--and likely, with CA certs--that the serial number is a single digit number.) The way around this--which is used in the specification shown here--is to use DER tagging. If the version tag is present, it is tagged using DER tag "[0]." The serialNumber is not tagged, so if you encounter an integer without a DER tag, you know that it is probably a version 1 cert. (It's also possible that you just have an incorrectly formatted cert--look at enough certs, and you will find all sorts of ASN.1 formatting mistakes.)

Version 1 certs don't have any of the other sections marked with DER tags, but version 3 certs (the version you will encounter most often on the internet) don't have to include any or all of those sections either. To know which, if any, of the sections are present, you look at the DER tags. Most version 3 certs will have extensions, but not issuerUniqueID or subjectUniqueID. In those certs, you will see the [0] tag for version, 1 and [2] for subject and issuer uniqueIDs, and [3] tag for extensions. Many version 3 certs you encounter don't have uniqueID sections, so the [3] tag immediately following the subjectPublicKeyInfo is the indication that the extensions are next to be parsed.

You will not encounter many version 2 certificates. Because X.509 and ASN.1 isn't confusing enough, there is an X.509 specification for "attribute" certificates--a different kind of certificate which does not contain a public key and follows a different format (with similar fields in a different order)--which are always labeled as version 2 by definition. See RFC 5755 for more information, and the format, for attribute certs.

  • "if the value of a type is its default value, it shall be absent." is the golden nugget here. – Wilbur Whateley Jun 14 '17 at 20:37

If you look at ASN module:

Certificate ::= SIGNED SEQUENCE{
           version [0]     Version DEFAULT v1988,
           serialNumber    CertificateSerialNumber,
           signature       AlgorithmIdentifier,
           issuer          Name,
           validity        Validity,
           subject         Name,
           subjectPublicKeyInfo    SubjectPublicKeyInfo}
Version ::=     INTEGER {v1988(0)}

it is marked with DEFAULT keyword which denotes that the field may be present or absent (optional). A default value (zero in a given case) is assumed if the filed is absent.

if you write your own decoder, it should expect Version field in the structure and correctly handle field absence by asserting default value.

  • Thanks. Where is this from? A spec somewhere? – Wilbur Whateley Sep 7 '16 at 20:25
  • Oh, found it. It is in the appendix A.1 of RFC 1422. – Wilbur Whateley Sep 7 '16 at 20:27
  • Also, how would I tell it is missing or present? It is defined as an integer, just like the serial number. How are they distinguished? If the version is present, will it have an "A0" tag since it is optional? – Wilbur Whateley Sep 7 '16 at 20:33
  • Yes, there will be a CONTEXT_SPECIFIC type with 0xa0 tag identifier when presented. – Crypt32 Sep 7 '16 at 20:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.