Example:
//generate specific serial number for testing purposes
byte[] result = "A0-41-A1-32-BD-9E-58-98-4B-CC-9E-E6-27-17-B4-10"
.Split('-')
.Select(item => Convert.ToByte(item, 16))
.ToArray();
using (X509Certificate2 cert = certRequest.Create(
caCert,
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddYears(50),
serialNumber))
{
// display serial number for testing purposes
Console.WriteLine(cert.SerialNumber);
}
Input serial number: "A0-41-A1-32-BD-9E-58-98-4B-CC-9E-E6-27-17-B4-10"
Certificates actual serial number: "00-A0-41-A1-32-BD-9E-58-98-4B-CC-9E-E6-27-17-B4-10"
Expected serial number to be the same as the input without a leading zero byte.
https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.2
The serial number MUST be a positive integer assigned by the CA to each certificate. It MUST be unique for each certificate issued by a given CA (i.e., the issuer name and serial number identify a unique certificate). CAs MUST force the serialNumber to be a non-negative integer. ...
(emphasis mine)
The serial number is stored as a DER integer, which is a minimum bytes signed big-endian value.
If you give it a value where serialNumber[0] > 0 && serialNumber[0] < 0x80
then the bytes are left as-is.
If you give it a value where serialNumber[0] == 0 && serialNumber[1] < 0x79
then the leading byte is removed (repeat until you hit the value { 0x00 } or the point where the zero is necessary).
These two statements are (slightly obliquely) embedded in The value is interpreted as an unsigned integer of arbitrary size in big-endian byte ordering.
To personify the method, it says "I was given this large value, 0xA041A132BD9E58984BCC9EE62717B410
, which I know to be large positive number. Now I need to write it down as a DER integer. The equivalent byte stream is { 0x00, 0xA0, 0x41, 0xA1, 0x32, 0xBD, 0x9E, 0x58, 0x98, 0x4B, 0xCC, 0x9E, 0xE6, 0x27, 0x17, 0xB4, 0x10 }
. Done."