For one of my project I was required to encode and decode CA certificate "CA Version" extension. The biggest problem is that there are no .NET or CryptoAPI interfaces that can do it.
CA Version extension is private Microsoft certificate extension and used in Windows PKI only. In addition, this extension exist only in CA certificate (where CA Type property of Basic Constraints extension is set to CA = True). End entity certificates never contains this extension. Main purpose of this certificate is to simplify CA server lifecycle. Sometimes CA certificate become expired and there are two choices to continue the work: build new CA from scratch or renew CA certificate (see related discussion about CA certificate renewal: Root CA certificate renewal). Though there are certain cases when CA requires to renew its own certificate prior to expiration. As the result CA server will maintain two signing certificates. Windows CA MUST publish CRLs for each valid signing key (not a certificate!). Since CA certificate can be renewed with existing key pair it is possible when CA server maintains multiple CA certificates and single CRL. Or CA server can maintain four CA certificates. Here is a great article that describes how CA Version extension works — Certification Authority Renewal.
Extension value has the following form: V<CA certificate index>.<CRL and key index>. For example: V0.0 (initial certificate), V1.0 (renewed with existing key pair), V1.1 (renewed with new key pair), V7.6 (the last string in the table of mentioned article) and so on. It is not possible to determine current Key Index from CA certificate index value. CA certificate index can be retrieved by using various methods, but key index only from CA Version extension. Therefore here is the following logic:
Also here might be another scenario — you may want to add CA Version support for your 3rd party CA server. In that case you will have to add to the certificate (or certificate request) encoded CA Version extension.
Here we will discuss about CA Version extension encoding rules by using Abstract Syntax Notation v1 Distinguished Encoding Rules (or simply ASN.1 DER). Here is a structure of raw data:
There might be several encoding forms:
Short form is applicable only when key index is zero. For example, V1.0, V3.0, V8.0, etc.:
V0.0 — 0x02, 0x01, 0x00
V2.0 — 0x02, 0x01, 0x02
V10.0 — 0x02, 0x01, 0x0a
V255.0 — 0x02, 0x01, 0xff
in a given examples we see that only last byte is changed that represents CA certificate index value. You may notice that the last byte can set value up to 255. What if we need higher value? In that case transitional byte is used (similarly as described in this article: How to encode Object Identifier to an ASN.1 DER encoded string). This byte acts as a multiplier to 256. For example:
V256.0 — 0x02, 0x02, 0x01, 0x00
V300.0 — 0x02, 0x02, 0x01, 0x2c
V3000.0 — 0x02, 0x02, 0x0b, 0xb8
you see that second byte is increased to 0x02 because encoded string is extended for one byte. 3rd byte is a multiplier to 256. Let's see how the last value is produced:
As 3000 is larger than 256 we add transitional byte. Divide 3000 by 256: 3000 / 256 = 11,71875 and round result to lesser integer = 11 (0x0b). Multiply 256 to 11 and get transitional byte base: 256 * 11 = 2816. Subtract this base value from original: 3000 – 2816 = 184 (0xb8).
Pretty simply! by using this encoding rules we can encode CA certificate index up to 65535 (imagine the CA certificate that was renewed 65535 times ). Short notation don't support encoding for a values that are larger than 65535. Instead, long form must be used.
Long form is preferred form for any scenario. Long form allows to encode both CA certificate index and key index values as follows:
V0.0 — 0x02, 0x03, 0x00, 0x00, 0x00
V1.0 — 0x02, 0x03, 0x00, 0x00, 0x01
V2.2 — 0x02, 0x03, 0x02, 0x00, 0x02
V255.127 — 0x02, 0x03, 0x7f, 0x00, 0xff
Note: remember that actual data is encoded in little-endian encoding.
though here is a little difference: key index is encoded by a single byte only up to 127 (but not to 255 as CA Certificate index). If key index value is larger than 127 additional empty byte is used:
V255.128 — 0x02, 0x04, 0x00, 0x80, 0x00, 0xff
V255.255 — 0x02, 0x04, 0x00, 0xff, 0x00, 0xff
if key index value is larger than 255, empty byte become as a transitional byte and the same logic as for CA certificate index value is used:
V256.256 — 0x02, 0x04, 0x01, 0x00, 0x01, 0x00
you see that transitional bytes are set to 1 and remaining value is set to 0. This means that we just multiply 256 to transitional byte value (256 * 1 = 256). Here is an example for the V300.300. Take key index value and divide it to 256. 300 / 256 = 1,171875. Round resulting value to lesser integer = 1. Set transitional byte to 1 and multiply 256 to this value: 256 * 1 = 256. This is our new base. Subtract this base value from initial value: 300 – 256 = 44 (0x2c). Set this value as a remaining value. Repeat this process for CA certificate index value and you should get the following string:
V300.300 — 0x02, 0x04, 0x01, 0x2c, 0x01, 0x2c
And finally take another custom example: V1000.750:
Take key index value (750) and divide it by 256: 750 / 256 = 2,9296875. Round resulting value to lesser integer = 2. Set this value as a transitional byte. Multiply this transitional byte base to 256: 256 * 2 = 512. Subtract this value from original key index value: 750 – 512 = 238. Set resulting value as a resulting byte. First part of encoded string is: 0x02, 0x04, 0x02, 0xee. Now we need to encode CA certificate index value. Divide 1000 by 256: 1000 / 256 = 3,90625. Round resulting value to lesser integer = 3. Set this value as a transitional byte. Multiply this value to 256 and get transitional byte base: 256 * 3 = 768. Subtract this value from original CA certificate index value: 1000 – 768 = 232 (0xe8). Set resulting value as a remaining byte. And here is a full encoded string:
V1000.750 — 0x02, 0x04, 0x02, 0xee, 0x03, 0xe8
As you see here (and in the previous such article: How to encode Object Identifier to an ASN.1 DER encoded string) we can get several general encoded string structure:
Hope this helps.
Post your comment: