From 705d94e5d577e6dea239817bfdf533e49cc7996b Mon Sep 17 00:00:00 2001 From: dhjoshi Date: Thu, 9 Jan 2025 15:22:18 -0800 Subject: [PATCH] Update CMS Version to 1 for CodeSigningCIPolicySigning --- .../CodeSigning/Helpers/CmsSigner.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/CodeSigning/CodeSigning/Helpers/CmsSigner.cs b/src/CodeSigning/CodeSigning/Helpers/CmsSigner.cs index 04aaaa00853b..e7bd6ebfcbb2 100644 --- a/src/CodeSigning/CodeSigning/Helpers/CmsSigner.cs +++ b/src/CodeSigning/CodeSigning/Helpers/CmsSigner.cs @@ -50,6 +50,8 @@ public void SignCIPolicy(TokenCredential tokenCred, string accountName, string c var signedData = cms.Encode(); + UpdateCMSVersion(signedData); + if (!string.IsNullOrWhiteSpace(timeStamperUrl)) { var timestampingUri = new Uri("http://www.microsoft.com"); @@ -89,5 +91,72 @@ public void SignCIPolicy(TokenCredential tokenCred, string accountName, string c } } } + + private byte[] UpdateCMSVersion(byte[] signedData) + { + + /* Find and update CMSVersion to 1 if current version is 3. + * + * Ref: https://datatracker.ietf.org/doc/html/rfc2315 + * + * + * ContentInfo ::= SEQUENCE { + * ContentType ::= OBJECT IDENTIFIER, + * Content ([0] EXPLICIT ANY DEFINED BY contentType OPTIONAL) + * SignedData ::= SEQUENCE { + version Version ::= INTEGER, // https://datatracker.ietf.org/doc/html/rfc2315#section-6.9 + digestAlgorithms DigestAlgorithmIdentifiers, + contentInfo ContentInfo, + certificates + [0] IMPLICIT ExtendedCertificatesAndCertificates + OPTIONAL, + crls + [1] IMPLICIT CertificateRevocationLists OPTIONAL, + signerInfos SignerInfos } } + * + * The OID takes 11 bytes. Each other constructed element has a minimum of 2 bytes + * for tag and length, so start looking at offset 15 for a SEQUENCE followed + * immediately by INTEGER 3 (02 01 03). + * + */ + + const int startOffset = 15; // Start checking from this index + const int endOffset = 100; // Stop checking at this index + const byte asn1SequenceTag = 0x30; // ASN.1 SEQUENCE tag + const byte asn1LengthBit = 0x80; // Bit indicating a constructed ASN.1 length + const byte cmsVersionTag = 0x02; // ASN.1 INTEGER tag for cmsVersion + const byte cmsVersionLength = 0x01; // CMS version value length + const byte cmsVersionV1 = 0x01; // cmsVersion 1 + const byte cmsVersionV3 = 0x03; // cmsVersion 3 + + // Iterate over the specified range in the data + for (int i = startOffset; i < endOffset; i++) + { + // Check if the current byte marks the start of an ASN.1 SEQUENCE + if (signedData[i] == asn1SequenceTag && (signedData[i + 1] & asn1LengthBit) == asn1LengthBit) + { + // Extract the length of the SEQUENCE + int sequenceLength = signedData[i + 1] & ~asn1LengthBit; // 0x7F + + // Calculate the index of the cmsVersion value + int cmsVersionIndex = i + 4 + sequenceLength; + + if (cmsVersionIndex >= signedData.Length) continue; + + // Verify that the structure matches the expected pattern + if (signedData[i + 2 + sequenceLength] == cmsVersionTag && // tag for cmsVersion + signedData[i + 3 + sequenceLength] == cmsVersionLength && // CMS version value length + signedData[cmsVersionIndex] == cmsVersionV3) // Version is 3, needs to be updated + { + // Update cmsVersion to 1 + signedData[cmsVersionIndex] = cmsVersionV1; + return signedData; // Return the updated data + } + } + } + + // Throw an exception if cmsVersion 3 was not found or could not be updated + throw new Exception("Did not find cmsVersion 3 to update."); + } } }