Study about ASN.1 / OID encoding and size limitations

This study contains some research about OID encodings, ASN.1, X.509 and security vulnerabilities as well as conformance and size limitation tests of software products inclusive bug reports.

Last updated: 14 May 2019 by Daniel Marschall

Table of contents

1. BER encoding of OIDs
	1.1 Identifier octets
		1.1.1 Classes
		1.1.2 Universal Class tag numbers
	1.2 Length octets
	1.3 Content octets of OIDs
	1.4 Content octets of other types
2. OID Converter
3. Illegal OID encodings
	3.1 Length octet 0x00
	3.2 Length octet 0x80
	3.3 Length octet 0xFF
	3.4 Invalid type
	3.5 Illegal paddings
4. Software compatibility analysis for large OIDs
5. Bugs found by me
	5.1 [SOLVED] OpenSSL encodes a few OIDs wrong
	5.2.1 [Reported] OpenSSL displays root-arcs wrong
	5.2.2 [Reported] OpenSSL should accept standardized identifiers, like example
	5.3 [CRITICAL, Reported] Mac OS X cannot handle the OIDs 2.48+
	5.4 [SOLVED] OpenSSL crashes when limit of first subidentifier is reached
	5.5 [SOLVED] OpenSSL allows illegal paddings for first subidentifier
	5.6 [Reported] Apple allows illegal 0x80 padded OIDs
	5.7 [Not a bug] OpenSSL does not show errors on padded policy OIDs
	5.8 [Not yet reported] OpenSSL: Use Example OID
	5.9 [Not yet reported] OpenSSL cannot create 32768 bit RSA certificates
	5.10 [SOLVED] 0x80 padding bug in OID-Converter 1.6 - 1.9
	5.11 [SOLVED] BouncyCastle: Problems with ASN.1 Object Identifier parsing
	5.12 [Not yet reported] PolarSSL: DN does not show unknown OIDs correctly
6. Vulnerability: "NULL" inside CN security vulnerability
7. Vulnerability: Risks of overflowing OID identifiers in X.509 certificates
8. Limited RSA keysizes

1. BER encoding of OIDs

An OID is encoded in following BER format: "[identifier] [length] [content]" (TLV), described in Rec. ITU-T X.690.

There exists several formats like BER, CER or DER. DER is used for Rec. ITU-T X.509 certificates and requires always the shortest possible form of encodings, while BER also allows longer forms.

1.1 Identifier octets

       /+-------------- Class: 2 bit for Universal (00), Application (01), Context-specific (10) or Private (11)
       ||                      The class defines how the tag number is interpreted.
       ||                      Universal-0x06 is an absolute OID, Universal-0x0D is a relative OID.
       ||
       || /------------ P/C: 1 bit for primitive (0) or constructed (1). OIDs are always primitive (0)! (More information: See Rec. ITU-T X.690)
       || |
       vv v
0x06 = 00 0 00110 = OBJECT IDENTIFIER (Absolute)
0x0D = 00 0 01101 = RELATIVE-OID
            ^^^^^
            |||||
            \++++------- TAG NUMBER: 5 bits used for class (00110 = Absolute OID, 01101 = Relative OID)
                                     Possible values: 00000 (0) .. 11110 (30)
                                     Value 11111 means that a long-form of the class-tag is used (neccessary when encoding tag
                                     >= 31). The highest bit of the long-form octets is the "more" bit. (only last octet may have
                                     0 as highest bits). The remaining 7 bits are the bits which are concatenated
                                     (like the OID subidentifiers) The first octet may not be 0x80. (padding)
                                     BER: The long form may also be 0..30, so both, long-form and short-form are allowed.
                                     DER: Tags 0..30 MUST be encoded in short form and the long-form may only be used for tags >= 31.

                                     Example:

                                     xxx11111 1aaaaaaa 1bbbbbbb 0ccccccc
                                     "11111" implies the long-form, which has in our example 3 octets.
                                     The tag-number is then "000aaaaa aabbbbbb bccccccc"
                                     Encoding is big endian.
                                     Thanks to Jo Wilkes from metabit for providing useful information about this topic.


0x1F 0x23 = 00 0 11111  0 0100011 = OID-IRI (Absolute)
0x1F 0x24 = 00 0 11111  0 0100100 = RELATIVE-OID-IRI
            ^^ ^ ^^^^^  ^ ^^^^^^^
            || | |||||  | |||||||
            || | |||||  | \++++++------- TAG NUMBER: 35 for OID-IRI and 36 for RELATIVE OID-IRI
            || | |||||  |
            || | |||||  \--------------- "More" flag: Is an additional octet required to encode the tag number? (0 = No)
            || | |||||
            || | \++++------------------ "11111" means that the tag number will be encoded in the following octet(s) = "long form"
            || |
            || \------------------------ P/C: 1 bit for primitive (0) or constructed (1). OID-IRI are always primitive (see Rec. ITU-T X.690 clause 8.21)
            ||
            \+-------------------------- Class: 00 for Universal

1.1.1 Classes

More information about classes at Rec. ITU-T X.680 clause 8.

1.1.2 Universal Class tag numbers

The list of Universal Class tag assignments can be found at Rec. ITU-T X.680, clause 8, table 1.

----------------------------------------------------------------------------------------------------------------------------
Number  Name                            Prim./  Identifier - octet(s)               Tag defined in       Encoding defined in
Dec.                                    Cnstr.  Hex(P)  Hex(C)  Binary              Rec. ITU-T X.680     Rec. ITU-T X.690
----------------------------------------------------------------------------------------------------------------------------
0       EOC (End-of-Content)		P	00      -       00000000            ---                  clause 8.1.5
1       BOOLEAN				P	01      -       00000001            clause 18            clause 8.2
2       INTEGER				P	02      -       00000010            clause 19            clause 8.3
3       BIT STRING			P/C	03      23      00x00011            clause 22            clause 8.6
4       OCTET STRING			P/C	04      24      00x00100            clause 23            clause 8.7
5       NULL				P	05      -       00000101            clause 24            clause 8.8
6       OBJECT IDENTIFIER		P	06      -       00000110            clause 32            clause 8.19
7       Object Descriptor		P	07      -       00000111            clause 48            clause 8.25
8       EXTERNAL			C	-       28      00101000            clause 37            clause 8.18
9       REAL (float)			P	09      -       00001001            clause 21            clause 8.5
10      ENUMERATED			P	0A      -       00001010            clause 20            clause 8.4
11      EMBEDDED PDV			C	-       2B      00101011            clause 36            clause 8.17
12      UTF8String			P/C	0C      2C      00x01100            clause 41            clause 8.23.10
13      RELATIVE-OID			P	0D      -       00001101            clause 33            clause 8.20
14      TIME                  	    	P	0E      -       00001110            clause 38            clause 8.26.1
15      (reserved)			?	0F?     2F?     00?01111            ---                  ---
16      SEQUENCE / SEQUENCE OF		C	-       30      00110000            clause 25 & 26       clause 8.9  & 8.10
17      SET / SET OF			C	-       31      00110001            clause 27 & 28       clause 8.11 & 8.12
-       (CHOICE)                                                                    clause 29            clause 8.13
-       (Selection)                                                                 clause 30            ---
-       (Prefixed type)                                                             clause 31            clause 8.14
18      NumericString			P/C	12      32      00x10010            clause 41            clause 8.23
19      PrintableString			P/C	13      33      00x10011            clause 41            clause 8.23
20      T61String (TeletexString)	P/C	14      34      00x10100            clause 41            clause 8.23
21      VideotexString			P/C	15      35      00x10101            clause 41            clause 8.23
22      IA5String			P/C	16      36      00x10110            clause 41            clause 8.23
23      UTCTime				P/C	17      37      00x10111            clause 47            clause 8.25
24      GeneralizedTime			P/C	18      38      00x11000            clause 46            clause 8.25
25      GraphicString			P/C	19      39      00x11001            clause 41            clause 8.23
26      VisibleString (ISO646String)	P/C	1A      3A      00x11010            clause 41            clause 8.23
27      GeneralString			P/C	1B      3B      00x11011            clause 41            clause 8.23
28      UniversalString			P/C	1C      3C      00x11100            clause 41            clause 8.23.7
29      CHARACTER STRING		P/C	1D      3D      00x11101            clause 44            clause 8.24
30      BMPString			P/C	1E      3E      00x11110            clause 41            clause 8.23.8
31      DATE                            P	1F 1F   -       00011111 00011111   clause 38.4.1        clause 8.26.2
32      TIME-OF-DAY			P	1F 20   -       00011111 00100000   clause 38.4.2        clause 8.26.3
33      DATE-TIME			P	1F 21   -       00011111 00100001   clause 38.4.3        clause 8.26.4
34      DURATION			P	1F 22   -       00011111 00100010   clause 38.4.4        clause 8.26.5
35      OID-IRI                         P       1F 23   -       00011111 00100011   clause 34            clause 8.21
36      RELATIVE-OID-IRI                P       1F 24   -       00011111 00100100   clause 35            clause 8.22
----------------------------------------------------------------------------------------------------------------------------

Derivied from the table above, we can create following lookup-table:

--------------------------------------------------------
Ident.  Name                            Primitive/
Hex.                                    Constructed
--------------------------------------------------------
00	EOC (End-of-Content)		Primitive
01	BOOLEAN				Primitive
02	INTEGER				Primitive
03	BIT STRING			Primitive
04	OCTET STRING			Primitive
05	NULL				Primitive
06	OBJECT IDENTIFIER		Primitive
07	Object Descriptor		Primitive
09	REAL				Primitive
0a	ENUMERATED			Primitive
0c	UTF8String			Primitive
0d	RELATIVE-OID			Primitive
0e	TIME				Primitive
12	NumericString			Primitive
13	PrintableString			Primitive
14	T61String (TeletexString)	Primitive
15	VideotexString			Primitive
16	IA5String			Primitive
17	UTCTime				Primitive
18	GeneralizedTime			Primitive
19	GraphicString			Primitive
1a	VisibleString (ISO646String)	Primitive
1b	GeneralString			Primitive
1c	UniversalString			Primitive
1d	CHARACTER STRING		Primitive
1e	BMPString			Primitive
1f 1f	DATE				Primitive
1f 20	TIME-OF-DAY			Primitive
1f 21	DATE-TIME			Primitive
1f 22	DURATION			Primitive
1f 23	OID-IRI				Primitive
1f 24	RELATIVE-OID-IRI		Primitive
23	BIT STRING			Constructed
24	OCTET STRING			Constructed
28	EXTERNAL			Constructed
2b	EMBEDDED PDV			Constructed
2c	UTF8String			Constructed
30	SEQUENCE / SEQUENCE OF		Constructed
31	SET / SET OF			Constructed
32	NumericString			Constructed
33	PrintableString			Constructed
34	T61String (TeletexString)	Constructed
35	VideotexString			Constructed
36	IA5String			Constructed
37	UTCTime				Constructed
38	GeneralizedTime			Constructed
39	GraphicString			Constructed
3a	VisibleString (ISO646String)	Constructed
3b	GeneralString			Constructed
3c	UniversalString			Constructed
3d	CHARACTER STRING		Constructed
3e	BMPString			Constructed
--------------------------------------------------------

1.2 Length octets

- [length] is encoded as follows:
  0x00 .. 0x7F = The actual length is in this byte, followed by [data].
  0x80 + n     = The length of [data] is spread over the following 'n' bytes. (0 < n < 0x7F)
  0x80         = "indefinite length" (only constructed form) -- Invalid
  0xFF         = Reserved for further implementations -- Invalid
  See page 396 of ASN.1 - Communication between Heterogeneous Systems by Olivier Dubuisson.

1.3 Content octets of OIDs

- The top-level arcs were limited to 3 standard bodies "0" (iso), "1" (itu-t) and "2" (joint-iso-itu-t) in 1986 .
- It is NOT possible to encode any OID with root 3 or higher
- It is NOT possible to encode any root OID without any second element (e.g. "1")
- The OIDs { 0 0 } until { 0 39 } are encoding into 1 octet. ( "06 01 00" till "06 01 27" )
- It is NOT possible to encode { 0 x } with x >= 40
- The OIDs { 1 0 } until { 1 39 } are encoding into 1 octet. ( "06 01 28" till "06 01 4F" )
- It is NOT possible to encode { 1 x } with x >= 40
- The OIDs { 2 0 } until { 2 47 } are encoding into 1 octet. ( "06 01 50" till "06 01 7F" )
- It *IS* possible to encode { 2 x } with x >= 40
- It *IS* possible to encode { 2 x } with x >= 47
- The OIDs { 2 48 } till { 2 unlimited } encodes into 2 or more octets, while the highest is
  defined as "more" bit, like it is defined at the higher levels.
- Encoding is always big endian.
- Please note that you have to subtract 0x50 (80) from the second value, but not on any other value.
  Example: { 2 0 999 } is "06 03 50 87 67". { 2 0 } does encode into "50" and "999" got encoded to
  "87 67" at the third resp. any higher level. But: { 2 999 } is encoded into "06 02 88 37".
  "88 37" does encode into "999" at the second level and not to "1079". (Difference: 80)
- The encoding of the example OID { 2 999 } is "06 02 88 37".

1.4 Content octets of other types

Please see Rec. ITU-T X.690

2. OID Converter

The OID converter by Matthias Gärtner is not capable in UUIDs and has encoding problems with OIDs like 2.999 and cannot handle length greater than 0x7F (OpenSSL, Windows and Mozilla can handle them correctly)

I made great changes and have updated the program to version 1.10 which fixes:

Test it online: OID Converter (*)

(*) Please note bug #10 in version 1.6 till 1.9

3. Illegal OID encodings

Note: The following tests were done with Windows 7 and IE11 as well as Mozilla Firefox 31 Nightly.

3.1 Length octet 0x00

Length octet 0x00 used (OID cannot have zero length)

06 00 00
	Windows:       Whitespace shown
	Mozilla:       Certificate does not open
	OID Converter: Error message
	OpenSSL:       Error message
	Mac OS X:      Error message

3.2 Length octet 0x80

Length octet 0x80 used (indefinite length, only valid for constructed types)

06 80 00
	Windows:       Certificate incomplete
	Mozilla:       Certificate does not open
	OID Converter: Error message
	OpenSSL:       Error message
	Mac OS X:      Error message

3.3 Length octet 0xFF

Length octet 0xFF used (reserved for extensions)

06 FF 00
	Windows:       Certificate incomplete
	Mozilla:       Certificate does not open
	OID Converter: Error message
	OpenSSL:       Error message
	Mac OS X:      Error message

3.4 Invalid type

No valid OBJECT IDENTIFIER or RELATIVE-OID class tag used (e.g. type 0x08)

08 01 00
	Windows:       Certificate incomplete
	Mozilla:       Certificate does not open
	OID Converter: Error message
	OpenSSL:       Error message
	Mac OS X:      Error message

3.5 Illegal paddings

The subidentifier shall be encoded in the fewest possible octets, that is, the leading octet of the subidentifier shall not have the value 0x80. (See Rec. ITU-T X.690, clause 8.19.2)

The word "shall" shall be used to express mandatory requirements. The word "may" shall be used to express optional requirements. Although the negative form of "shall" is "shall not", the negative form of "may" is not "may not", but is "need not". The use of "may not" shall be avoided. (See Rec. ITU-T A.23, Annex A, clause 6.10.8 - Use of words)

06 07 01 80 80 80 80 80 7F (illegal_padding1.crt) - this *could* be decoded as 0.1.127
	Windows:       Whitespace
	Mozilla:       Outputs "(0 1 Unknown)"; also, the certificate chain will show the same cert (issued to itself) over and over, max. 20 times
	OID Converter: Error
	OpenSSL:       Error for EKU/DN-OIDs resp. hex-dump-fallback for OIDs in extensions (e.g. policy OIDs)
	Mac OS X:      0.1.127 (see bug #6)

06 02 80 01 (illegal_padding2.crt) - this *could* be decoded as 2.-79
	Windows:       Whitespace
	Mozilla:       Outputs "(Unknown)"; also, the certificate chain will show the same cert (issued to itself) over and over, max. 20 times
	OID Converter: Error
	OpenSSL:       Since July, 14th 2011: Error message
	               Older versions: 0.1 (see bug #5)
	Mac OS X:      2.48.1 (see bug #6)

06 02 80 7F (illegal_padding3.crt) - this *could* be decoded as 2.47
	Windows:       Whitespace
	Mozilla:       Outputs "(Unknown)"; also, the certificate chain will show the same cert (issued to itself) over and over, max. 20 times
	OID Converter: Error
	OpenSSL:       Since July, 14th 2011: Error message
	               Older versions: 2.47 (see bug #5)
	Mac OS X:      2.48.127 (see bug #6)

Since Apple does not show unknown OIDs in the subject/issuer's DN, the following test certificate contains policy OIDs which are visible (scroll down at the certificate viewer!)

Warning: As these Rec. ITU-T X.509 certificates contain very unusual values, your software might crash. I am not responsible for any damages. Use at your own risk!

4. Software compatibility analysis for large OIDs

The OID standards have no limit whatsoever about the depth of an OID in the OID tree (i.e., number of arcs) and the size of the integer associated to each OID arc. However, some tools may have size limitations.

Possible limits could be...

  1. Limitation of an arc size, e.g. 2.999.18446744073709551615
  2. Limitation of the total size of the dot-notation of an OID (string length)
  3. Limitation of the depth of a OID
  4. Limitation of the value of an attribute inside a Rec. ITU-T X.509 certificate
  5. Limitation of the length of the DER encoded data

I have created a Rec. ITU-T X.509 test certificate which contains large OID values (#1) as well as long OID strings (#2) to check which clients have limits. Here is the result of my study:

Product Biggest possible 1st subidentifier (2.[value]) Biggest possible higher level arc-value (e.g. 2.999.[value]) Display/Length/Depth limit UUID-Capable (128 bit)
Mozilla Firefox 4, 5 (x86)
Mozilla Thunderbird 3.1.10, 5.0 (x86)
Max 2.4294967215 (decoded 232-1), otherwise it is displayed as "(Unknown)" Max 4294967295 (232-1), otherwise the arc is shown as "Unknown", e.g. 2.999.4294967296.0 becomes "(2 999 Unknown 0)" Display of dot-notation is limited to 299 chars, the rest is cut off. No!
Windows XP, 7 (x86)
Windows Server 2008-R2 (x64)
Max 2.18446744073709551535 (decoded 264-1), otherwise, the whole OID is shown as whitespace Max 18446744073709551615 (264-1), otherwise, the whole OID is shown as whitespace Maximum 255 chars of dot-notation, otherwise whole OID is shown as whitespace. No!
OpenSSL 0.9.8o (1 Jun 2010) on Debian (x86) Since July, 14th 2011: Unlimited! (Creation/Reading)

Before: Bug #4 - Creation/Reading max: 2.2147483567 (decoded 232-1), otherwise AppCrash (Windows) or ErrorMessage (Linux).
Unlimited! The program will switch to BigNumber-library as soon as an arc is greater than 232-1 . Please note bug #1. Various: Display of dot-notation limited to 79 chars in EKUs or DNs, the rest is cut off. For OIDs inside X509v3 extensions (e.g. policy OIDs): Unlimited Yes!
OpenSSL 0.9.8k (25 Mar 2009) on Win2008 (x64) Since July, 14th 2011: Unlimited! (Creation/Reading)

Before: Bug #4 - Creation/Reading max: 2.18446744073709551535 (decoded 264-1), otherwise AppCrash (Windows) or ErrorMessage (Linux).
Unlimited! The program will switch to BigNumber-library as soon as an arc is greater than 264-1 . Please note bug #1. Various: *Display* limited to 79 chars in EKUs or DNs. For policy OIDs: Unlimited Yes!
OID-Converter 1.6 (x86) Unlimited! (GMP-Libary used) Unlimited! (GMP-Libary used) Unlimited! (GMP-Libary used) Yes!
Mac OS X 10.5.8 (32-Bit) Max 2.47 (decoded 27-1 = 127), then bug #3 will occur and the OIDs 2.48+ are wrongly decoded.

There is probably also an overflow possible. (ATTENTION, this is a security related issue)!*

Here is a screenshot of MAC OS X Lion 10.7.4, 64 bit

Max 2147483647 (232/2-1, signed int32 limit), then it begins at -2147483648 again (ATTENTION, this is a security related issue)! The DER encoding of an OID is limited to max 32 bytes (34 bytes inclusive content-octet and length-octet), otherwise the OID is marked as invalid.

As a result of this limitation: The max depth is 33 arcs if every arc's value is max 127 (0x7F) and the first 2 arcs are max 2.47 (decoded 0x7F). Or the biggest possible number is 0.0.210624583337114373395836055367340864637790190801098222508621955071 resp. 2.26959946667150639794667015087019630673637144422540572481103610249135 (but there will be integer-overflows as well as the bug #3).
No!
Mac OS X 10.6.6 (64-Bit)
MAC OS X Lion 10.7.4 (64-Bit)
Max 9223372036854775807 (264/2-1, signed int64 limit), then it begins at -9223372036854775808 again (ATTENTION, this is a security related issue)! No!
Microsoft MakeCert 5.131.1863.1 (x86) Max 2.18446744073709551535 (decoded 264-1), then it begins at 0.0 again (ATTENTION, this is a security related issue)! Max 18446744073709551615 (264-1), then it begins at 0 again (ATTENTION, this is a security related issue)! (No OID display) No!
BouncyCastle JAVA Unlimited! Unlimited! Unlimited! Yes!
Java's built-in OID class org.ietf.jgss.Oid Unlimited! Unlimited! Unlimited! (Display through .toString()) Yes!
ASN.1 JavaScript decoder by lapo.it Unlimited! Unlimited! Unlimited! (Output of very large/long OIDs may be trimmed to avoid horizontal scrolling) Yes!
OSS ASN-1Step Version 8.3 Max 2.18446744073709551615 (decoded 264-1), otherwise the encoding fails. Max 18446744073709551615 (264-1), otherwise error messsages. Display of the DER/PER/... encoding is limited to 32 characters and then "..." is added.

Display of the E-XER encoding is limited to 1024 characters and then "..." is added.
No!
OSS ASN.1 Tools for C Unlimited!* Unlimited!* Unlimited when the ENCODED representation is used for the C struct* Yes!
OSS ASN.1 Tools for C++ Unlimited!* Unlimited!* Unlimited when the ENCODED representation is used for C struct in the C++ class* Yes!
OSS ASN.1 Tools for C# Unlimited!* Unlimited!* .NET limit on size of byte[] array for OID value ((Int32.MaxValue - 8)*7/8 = about 1879048184 bytes). The ENCODED representation is always used.* Yes!
OSS ASN.1 Tools for Java Unlimited!* Unlimited!* JVM limit on size of byte[] array for OID value ((Integer.MAX_VALUE - 8)*7/8 = about 1879048184 bytes). The ENCODED representation is always used.* Yes!
Ararat Synapse Max 2.39 due to a bug. Max 2147483647 (232/2-1, signed int32 limit), then it begins at -2147483648 again (ATTENTION, this is a security related issue)! Probably unlimited No!
SOP ASN.1 for PHP Unlimited! (bug fixed) Unlimited! (GMP-Libary used) Unlimited! Yes!
Go programming language Max 28 bits (see bug report)* Unknown. Unknown. No!
PHPseclib Unlimited!* (bug fixed) Unlimited!* (BigInteger library used after bugfix) Probably unlimited Yes!
fgrosse's PHPASN1 Max 2.39 due to a bug. Unlimited!* (BigInteger library used.) Probably unlimited Yes!
lionet asn1c Limited to 32 bit (according to source)* Limited to 32 bit (according to source)* Unknown No!

* The points marked with asterisk were not yet verified by me. (This will be done in the future). These points are replies from the authors, bug ticket resolutions or assumptions by reading the source code (i.e. by looking if a BigInteger class is referenced in the code, but without actually testing it)

If you have more results, please send them to me: .

5. Bugs found by me

5.1 [SOLVED] OpenSSL encodes a few OIDs wrong

Please note that OpenSSL contained a bug (see report RT#2542 of June 19th 2011, login using guest/guest) in the encoding of OIDs.

The bug was fixed at June, 22th 2011 in the branches:

In x86-builds of OpenSSL, the faulty values were 4294967296..4294967299 (232 .. 232+3).

In x64-builds of OpenSSL, the faulty values were 18446744073709551616..18446744073709551619 (264 .. 264+3).

They were encoded into 0..3 instead of their real values. On Windows Server 2008 R2, the x64 bit version of OpenSSL also crashes as soon as the integer overflow happens.

5.2.1 [Reported] OpenSSL displays root-arcs wrong

OpenSSL display the following OIDs as:

0.0 = ITU-T
1.0 = ISO
2.0 = JOIUNT-ISO-ITU-T

This is wrong, for 2 reasons: (1) these identifiers need to be lowercase and (2) the description is only counting for the ROOT-arc (0, 1, 2) and not the root-arc, followed by the 2nd arc "0".

How to reproduce:

openssl req -nodes -newkey rsa:1024 -nodes -keyout mytest.key -out test.csr -subj "/CN=testcert/0.0=test0/1.0=Test1/2.0=test2"
openssl req -noout -text -in test.csr | grep "Subject:"

Actual behavior:

Subject: CN=testcert/0.0=test0/1.0=Test1/2.0=test2

Expected behavior:

Subject: CN=testcert/ITU-T=test0/ISO=Test1/JOINT-ISO-ITU-T=test2

See report: RT#2556 (part 1) of July 6th 2011, login using guest/guest

5.2.2 [Reported] OpenSSL should accept standardized identifiers, like example

Additional feature: Would be good to have 2.999 shown as "example".

See report: RT#2556 (part 1) of July 6th 2011, login using guest/guest

5.3 [CRITICAL, Reported] Mac OS X cannot handle the OIDs 2.48+

(This bug was reported via web contact form to Apple in June 2011. The mail was not answered and there was no fix.)

Mac OS X decodes OIDs which need more than one octet in the first two arcs wrong.

DER encoding "06 02 81 00" is shown as 2.49.0, but should be 2.48
DER encoding "06 02 81 01" is shown as 2.49.1, but should be 2.49
DER encoding "06 02 88 37" is shown as 2.56.55, but should be 2.999
and so on.

This violates Rec. ITU-T X.690, clause 8.19.4

This is also weird - I have no clue how they calculate THAT (Tested with a MAC OS X Lion 10.7.4, 64 bit):

2.4294967301 (DER = 06 05 90 80 80 80 55) is shown as "2.64.85"
2.18446744073709551621 (DER = 06 0A 82 80 80 80 80 80 80 80 80 55) is shown as "2.50.85"

The following screenshot shows the wrong decoding of OID arc 2.999:

Apple OIDs

The bug is still existing in OS X Yosemite 10.10.2 (64 bit).

Additional things:

- The usage of *signed* integer data types for an OID arc (which is always posive or zero) is very ... odd ... or let's say wrong.

- The size limitation of 34 bytes DER encoding per OID should be removed. There is no need to limit the DER encoding size.

- Minor priority: Apple should support UUID OIDs by using a BigInteger library (e.g. BigNum).

5.4 [SOLVED] OpenSSL crashes when limit of first subidentifier is reached

While OpenSSL can handle unlimited arc sizes for higher arcs (e.g. 2.999.[value]), the size of the first two arcs is limited to the respective ULONG_MAX (232-1 resp. 264-1). E.g. for x86 builds the highest possible OID to encode is 2.2147483567 . If this value is increased, Linux shows an non-informative error message and Windows reports an AppCrash. I would recommend also to use the combination between ULONG and BigNum like it is done for the higher arcs.

See report: RT#2556 (part 2) of July 6th 2011, login using guest/guest

The bug was fixed at July, 14th 2011 in the branches:

5.5 [SOLVED] OpenSSL allows illegal paddings for first subidentifier

The DER encoding "06 02 80 xx" which includes an illegal 0x80 padding at the first subidentifier can be decoded by OpenSSL, but is illegal as defined by Rec. ITU-T X.690, clause 8.19.2.

(by Steve) A bug in the check has another consequence: some correct OIDs like 2.65500 (06 03 84 80 2C) are rejected as having an invalid encoding.

The bug was fixed at July, 14th 2011 in the branches:

Note: For other subidentifiers, OpenSSL successfully marks the OIDs as invalid (e.g. "06 03 01 80 xx")

See report: RT#2556 (part 3) of July 6th 2011, login using guest/guest

5.6 [Reported] Apple allows illegal 0x80 padded OIDs

Their software decodes illegal padded 0x80 OIDs.

06 07 01 80 80 80 80 80 7F = Mac OS X says this is 0.1.127
06 02 80 01 = Mac OS X says this is 2.48.1
06 02 80 7F = Mac OS X says this is 2.48.127

This violates Rec. ITU-T X.690, clause 8.19.2.

Screenshot:

Apple OIDs 2

5.7 [Not a bug] OpenSSL does not show errors on padded policy OIDs

As reported in bug #5 ("OpenSSL allows illegal paddings for first subidentifier"), OpenSSL does allow the decoding of illegal 0x80 paddings in the first subidentifier.

For other subidentifiers, OpenSSL usually outputs an error message, as there is a check for sanity. But this check is not done on OIDs like policy OIDs in the text dump!

If a 0x80 padded OID occurs in the 2+ subidentifier, the whole policy section will be damaged and a hex-dump of the ASN.1 DER encoding will be shown in the textdump.

The MAC test certificate contains the following X509v3 Certificate Policies:
	1.2.3.4.5.6.7.8, DER[06 07 01 80 80 80 80 80 7F], DER[06 02 80 01], DER[06 02 80 7F], 1.2.3, 1.2.3

In the textdump this entry becomes:
	0.0...*......0..........0.....0.....0...*.0...*.

I have found out that here, a HEX-DUMP of a part of the certificate is outputted!

Content of DER cert (1) against output (2):
(1)	0.0...*......0....€€€€€.0...€.0...€.0...*.0...*.0...*†H†÷
(2)	0.0...*......0..........0.....0.....0...*.0...*.

To be sure that only the first illegal padded OID procudes this damage, I have also checked following:
	1.2.3.4.5.6.7.8, DER[06 07 01 00 00 00 00 00 7F], DER[06 02 80 01], DER[06 02 80 7F], 1.2.3, 1.2.3

And this gave the textdump
            X509v3 Certificate Policies:
                Policy: 1.2.3.4.5.6.7.8
                Policy: 0.1.0.0.0.0.0.127
                Policy: 0.1
                Policy: 2.47
                Policy: 1.2.3
                Policy: 1.2.3

Which is OK, but still has bug #5 for policies 0.1 and 2.47.

See report: RT#2556 (post 2) of July 13th 2011, login using guest/guest

Update: Since the fix of bug #5 in 2011-07-14, all 3 paddings lead to an hex-dump.

Update: The hex-dump is an expected behavior in X509v3 extensions.

5.8 [Not yet reported] OpenSSL: Use Example OID

In /etc/ssl/openssl.cnf (Debian Squeeze) there is following example code:

[ new_oids ]

# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6

It should be changed to testoid=2.999 . The OID 2.999 was allocated by ITU-T and ISO for this purpose. The "usage" (even for just documentation and/or examples) of the OID 1.2.3.4 is highly discouraged!

5.9 [Not yet reported] OpenSSL cannot create 32768 bit RSA certificates

please see here

5.10 [SOLVED] 0x80 padding bug in OID-Converter 1.6 - 1.9

My C and JAVA implementation of "OID converter" (see above) has a bug in the decoding routine of the versions 1.6 - 1.9.

The OID 2.99999999999999999 is correctly encoded to "06 09 81 B1 D1 AF 85 EC A8 80 4F".

But the decoding of "06 09 81 B1 D1 AF 85 EC A8 80 4F" back to 2.99999999999999999 is currently not possible. The error message "Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)" will appear (which is false).

Reason: The decoding routine checks if there are 0x80 paddings in front of each arc, which would be against the ITU-T recommendations. But the program did actually not recognize that the 0x80 is INSIDE the arc and not at its beginning: "06 09 81 B1 D1 AF 85 EC A8 80 4F".

The bug was fixed in the version 1.10

5.11 [SOLVED] BouncyCastle: Problems with ASN.1 Object Identifier parsing

See BouncyCastle bug tracker

5.12 [Not yet reported] PolarSSL: DN does not show unknown OIDs correctly

In the OpenVPN log file of a customer's iPhone I found following output:
2014-01-30 08:25:06 VERIFY OK: depth=0
cert. version : 3
serial number : 01
issuer name : C=DE, ST=BW, L=Heidelberg, O=FooBar, OU=myCA, CN=example.com, 0x29=myopenvpn, emailAddress=info@example.com
subject name : C=DE, ST=BW, L=Heidelberg, O=FooBar, OU=myCA, CN=server, 0x29=myopenvpn, emailAddress=info@example.com
issued on : 2014-01-29 09:25:59
expires on : 2024-01-27 09:25:59
signed using : RSA+SHA1
RSA key size : 1024 bits

Please note the 0x29=myopenvpn . Actually it should mean 2.5.4.41=myopenvpn or name=myopenvpn.

It turned out that it is a cosmetic bug in PolarSSL. It affects forks like TropicSSL, too. Also it affects OpenVPN for iPhone and Android because they wrap PolarSSL.

Actual behavior:

If the attribute OID is not inside 2.5.4 (ds) or 1.2.840.113549.1.9 (pkcs-9), it will be displayed as 0x(id)=(value) where (id) is the hexadecimal notation of the 5th resp. 10th octet of the DER encoding (which is NOT equal to the last arc if its value is greater than 0x7F). No system administratior or developer knows what 0x29 means!

If the OID is not inside ds or pkcs-9, then it will be displayed as ??=(value).

Expected behavior:

The attribute OID should ALWAYS be displayed completely, regardless of its parent arcs. The notation should be either dot-notation as defined per IETF RFC 2252 or ASN.1 notation as defined per ITU Rec. X.680 .

Affected products:

Why the hell can't they simply implement a printable_oid() function?

6. Vulnerability: "NULL" inside CN security vulnerability

Another important security vulnerability shown by Dan Kaminsky at 26C3 - Black OPs of PKI is the "NULL inside CN vulnerability".

The ITU-T did not define the NULL character as a special character for terminating the String sequence. Therefore it is a valid part of a String.

An attacker who wants to fake the connection to e.g. "google.com" could simply create a CSR with following CN: "google.com[NULL].attacker.com".

In case that the CA is running a software which is ITU-T conform (i.e. not recognizing NULL as a termination symbol), the CA will see "google.com[NULL].attacker.com". It will then run a Domain Validation for "attacker.com" which will be successful if the attacker owns attacker.com. The certificate will then be issued.

If the browser is now checking the certificate, it will probably interprete "google.com[NULL].attacker.com" as "google.com", e.g. if it is developed in C++. Now, the certificate is useable for intercepting a "google.com" connection. The scary part of this is that the user has NO CHANCE in finding out if the certificate is valid or not by just viewing the certificate. If the user opens the certificate in his browser, the program WILL show "google.com" instead of "google.com[NULL].attacker.com".

PLEASE NOTE: Even if the program SHOWS "google.com" at the GUI it is no evidence that it is actually vulnerable against such an attack, since the GUI and the "backend" (which is doing the validation of the certificate) are two separate parts. It is possible that the GUI does not 'escape' the NULL character to something (e.g. \0) while the core of the browser/OS/whatever might do a full Memory-Comparsion (e.g. memcmp()) instead of a C-String-Comparsion (e.g. strcmp(), NULL-terminated)! However, a good browser/OS/whatever should also ignore the [NULL] character in the GUI by simply replacing it with nothing or escape it with \0 or [NULL] or the NULL-UNICODE-Symbol (␀), so that experienced users can see what is actually happening with their connection.

In future I want to create a small testing-page which shows if your browser is vulnerable or not. I will also publish some testing results.

7. Vulnerability: Risks of overflowing OID identifiers in X.509 certificates

Software which has integer overflows (e.g. Microsoft MakeCert or MAC OS X) is probably vulnerable to allow false SSL certificates.

As shown by Dan Kaminsky at 26C3 - Black OPs of PKI, this can be used to incercept a secure communication.

A hacker can generate a CSR which contains following values:

2.5.4.3 (CN)               = badguy.example.com   (this is the "real" attribute which will be Domain-Verified (DV) by the CA)
2.5.4.4294967299           = google.com           (32 bit overflow attack, e.g. Apple x86)
2.5.4.18446744073709551619 = google.com           (64 bit overflow attack, e.g. MakeCert and Apple x64)

The attacker just need to find a CA which accepts CSR files without stroking unknown attributes like e.g. 2.5.4.4294967299 and which is using a non-overflowing OID implemention (e.g. OpenSSL). The CA will check the value 2.5.4.3 (CN) and will verify that badguy.example.com is owned by the person who is requesting the certificate. This will succeed and the hacker will get a valid X.509 certificate.

But the value 2.5.4.4294967299 will overflow to 2.5.4.3 for all 32 bit browsers with the overflow vulnerability and so the browser will interpret 2.5.4.4294967299 as 2.5.4.3 which is CN with the value google.com .

So we urge the vendors Microsoft and Apple to fix those bugs. Either they should implement a BigInteger solution or they should mark the OID as invalid as soon as the browser detects an integer overflow .

Of course you can also let the arc OID "2.5.4" (attributeType) or the root-arc 2.5 ("directory"), combinations of them or all together, overflow, e.g.:

2.5.4.3 (CN)                                  = badguy.example.com    (this is the "real" attribute which will be verified (DV) by the CA)

2.5.4.4294967299                              = google.com    (32 bit overflow of arc #2:   2.5.4.4294967299 = 2.5.4.3)
2.5.4294967300.3                              = google.com    (32 bit overflow of arc #1:     2.5.4294967300 = 2.5.4)
2.4294967301.4.3                              = google.com    (32 bit overflow of the root arc: 2.4294967301 = 2.5)
2.4294967301.4294967300.4294967299            = google.com    (32 bit overflow, everthing together)

2.5.4.18446744073709551619                    = google.com    (64 bit overflow of arc #2:   2.5.4.18446744073709551619 = 2.5.4.3)
2.5.18446744073709551620.3                    = google.com    (64 bit overflow of arc #1:     2.5.18446744073709551620 = 2.5.4)
2.18446744073709551621.4.3                    = google.com    (64 bit overflow of the root arc: 2.18446744073709551621 = 2.5)
2.18446744073709551621.18446744073709551620.18446744073709551619 = google.com (64 bit overflow, everthing together)

I have not yet checked if the vulnerability can actually be used, but you can download test certificates here.

Update for MAC OS X

I have checked it with MAC OS X Lion 10.7.4 (64 bit) and indeed the OIDs 2.5.4.3 did overflow. BUT the software seems to be NOT vulnerable. The OID 2.5.4.18446744073709551619 is shown as 2.5.4.3 in the (e.g. purposes), but the OID is shown as "Other name" not as "Common Name" in the attributes. So Apple probably checks the DER encoding matching to CN rather than checking the decoded OID value matching against 2.5.4.3.

But note that I have not yet checked if the "root arc" can be manipulated so that it shows 2.5.4 due to an overflow. And I do not know if THIS overflow is vulnerable or not.

8. Limited RSA keysizes

Note, that most programs cannot open 16.384 bit RSA keys.

The same goes to Convergence probably if you run a notary with a 16.384 bit key.

People, why don't you like 16.384 bit keys? You need to be prepared for DNA computers cracking your certs ;-)

Here is a bunch of test certificates you can use for testing!

The current analysis:

Further research needs to be done...