10

I have noticed that openssl x509 -in $FILE -text sometimes displays the serial number of an X.509 certificate (a positive integer of up to 20 byte, thus most significant bit at 0) sometimes as an "octet string", and sometimes as integer with its hex representation following.

For example, here is the serial number of a certificate in the "Letsencrypt" chain (16 byte serial number, but the MSB is 1, something is wrong?):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            91:2b:08:4a:cf:0c:18:a7:53:f6:d6:2e:25:a7:5f:5a
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1

Here is a certificate from the system's "certificate bundle" (8 byte serial number, MSB is 0)

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 6828503384748696800 (0x5ec3b7a6437fa4e0)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES

Another one from the same bundle (16 byte serial number, MSB is 0):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0c:e7:e0:e5:17:d8:46:fe:8f:e5:60:fc:1b:f0:30:39
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Assured ID Root CA

The certificate QuoVadis Root CA 2 (2 byte serial number):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1289 (0x509)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=BM, O=QuoVadis Limited, CN=QuoVadis Root CA 2

Here is one with 9 byte serial number (MSB correctly at 0):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            11:00:34:b6:4e:c6:36:2d:36
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=RO, O=CERTSIGN SA, OU=certSIGN ROOT CA G2

And one from the same provider, a certificate with with 6 byte serial number:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 35210227249154 (0x200605167002)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=RO, O=certSIGN, OU=certSIGN ROOT CA

It seems rather random. Is there a particular significance to there being two ways of printing the serial number?

1
  • 1
    BTW although it is often builtin on Linux, OpenSSL is not Unix-specific; it also runs native on Windows, and in the past on several other OSes including VMS Commented Jun 18 at 0:10

1 Answer 1

12

It's just a stylistic choice in how openssl x509 -text prints serial numbers. The logic in the source code is:

  • If the serial number is small, then print it in as a number decimal and hexadecimal, with a leading - for negative numbers.
  • If the serial number is large, then print it as a colon-separated list of blocks of two hexadecimal digits, with a trailing (Negative) for negative numbers.

What determines small vs large is the absolute value of the serial number: a small serial number is strictly less than 2^63 on a 64-bit system or 2^31 on a 32-bit system (sizeof(long) being what matters).

The “serial number” should really have been an octet string, since it is just a unique identifier and is never used as a number. But it was specified as an integer in X.509 syntax a long time ago, so it's encoded as an integer. The syntax allows it to be a negative integer, but the semantics forbid that. Conversely, the syntax prevents serial numbers that only differ in the number of leading zero octets.

most significant bit at 0

Not exactly. RFC 5280 is ambiguous: it says “Certificate users MUST be able to handle serialNumber values up to 20 octets”, but a serial number is an INTEGER, not an OCTET STRING, and an INTEGER does not have an octet count. There are two plausible interpretations:

  • This might mean that the (positive) value must fit in 20 octets, thus the integer is between 1 and (2^8)^20-1 = 2^160-1.
  • This might mean that the payload of the encoding of the integer (including the sign bit) must fit in 20 octets, thus the integer is between 1 and (2^8)^20/2-1 = 2^159-1.

Under the second interpretation, the most significant bit of the 20-octet value must be 0. It's perfectly fine to have, for example, a serial number between (2^8)^16/2 and (2^8)/16-1, as in the Letsencrypt certificate signed by ISRG Root X1.

You're probably misinterpreting the constraints on encodings of an INTEGER as constraints on the value. In the encoding of an INTEGER, the top bit of the first octet is a sign bit. That means the serial number 0x912b084acf0c18a753f6d62e25a75f5a is encoded as

021100912b084acf0c18a753f6d62e25a75f5a
^^ type
  ^^ length
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value (with top bit = 0 for non-negative)

and not

0210912b084acf0c18a753f6d62e25a75f5a
^^ type
  ^^ length
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value (with top bit = 1 for negative)

which would be - 2^128 + 0x912b084acf0c18a753f6d62e25a75f5a = -0x6ed4f7b530f3e758ac0929d1da58a0a6. You can see the encoding with openssl asn1 -dump ….

5
  • 1
    And since 2016 CABforum has required certs issued by a publicly trusted CA (admittedly not the only kind) to have at least 64 bits entropy in serialnumber -- I think due to 'shattered' Commented Jun 18 at 0:03
  • 1
    @dave_thompson_085 Right. To be clear, this applies to the certificates issued by web CAs, not to the root CAs themselves unless they're relatively recent cross-signatures from another root. Commented Jun 18 at 6:19
  • Very detailed, thank you! Just one thing: In the bulleted alternatives "thus the integer is between 1 and ..." should be "between 0 and ..."? There are actual a handful of root certificates which have the serial number 0 (case of "Go Daddy" for example) Commented Jun 18 at 9:50
  • 1
    @DavidTonhofer RFC 5280 mandates that the serial number is positive (so nonzero). Non-compliant serial numbers can be below 1 or above (2^8)^20. Commented Jun 18 at 10:00
  • Off-topic, but this yielded a good occasion to update my old Perl script for splitting a bundle into individual certs. Ok, next problem... Commented Jun 18 at 10:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .