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: 21 March 2021 by Daniel Marschall
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
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.
/+-------------- 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
UNIVERSAL
Universal class (tag numbers are defined in ITU-T X.680)APPLICATION
Application-specificCONTEXT
Context-specificPRIVATE
PrivateThe 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 --------------------------------------------------------
- [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.
- The top-level arcs were limited to 3 standard bodies "0" (itu-t), "1" (iso) 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".
Please see Rec. ITU-T X.690
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
Note: The following tests were done with Windows 7 and IE11 as well as Mozilla Firefox 31 Nightly.
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
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
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
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
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!
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...
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 |
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 |
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 |
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! |
Microsoft .Net "System.Security.Cryptography" | Unlimited! (Actually, accepting the OID as string, therefore 3.0 and 2.-1 are also valid) | Unlimited! | Unlimited! | Yes! |
FreeDSx ASN.1 | Max 2.39 due to a bug. | (Not tested) | (Not tested) | (Not tested) |
* 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: .
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.
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
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
(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:
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).
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:
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
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:
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.
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!
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
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?
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.
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.
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...