Description
Description
After calling EnvelopedCms.Decode
all its properties are supposed to be loaded up with the info from the byte-array CMS data passed in. But the ContentEncryptionAlgorithm.Parameters
is empty (zero-length array).
The issue was noted in this StackOverflow question.
Reproduction Steps
Below is a minimal repro of an AES-encrypted EnvelopedCMS.
var encryptedMessage = new byte[] {
// Offset 0x00000000 to 0x00000393
0x30, 0x82, 0x03, 0x90, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
0x01, 0x07, 0x03, 0xA0, 0x82, 0x03, 0x81, 0x30, 0x82, 0x03, 0x7D, 0x02,
0x01, 0x00, 0x31, 0x82, 0x01, 0x94, 0x30, 0x82, 0x01, 0x90, 0x02, 0x01,
0x00, 0x30, 0x78, 0x30, 0x60, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
0x04, 0x06, 0x13, 0x02, 0x49, 0x4E, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x08, 0x0C, 0x02, 0x4D, 0x48, 0x31, 0x0D, 0x30, 0x0B, 0x06,
0x03, 0x55, 0x04, 0x07, 0x0C, 0x04, 0x50, 0x55, 0x4E, 0x45, 0x31, 0x21,
0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x18, 0x49, 0x6E, 0x74,
0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4C, 0x74, 0x64, 0x31, 0x12, 0x30,
0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x6C, 0x6F, 0x63, 0x61,
0x6C, 0x68, 0x6F, 0x73, 0x74, 0x02, 0x14, 0x51, 0xB1, 0xB6, 0xA5, 0x42,
0x6A, 0xCE, 0x91, 0x3F, 0x57, 0x1F, 0xC5, 0xF1, 0x18, 0x14, 0xBE, 0x46,
0x89, 0x40, 0x87, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x07, 0x30, 0x00, 0x04, 0x82, 0x01, 0x00, 0x80, 0x45,
0xC3, 0x56, 0x3B, 0x28, 0x62, 0xAF, 0xDA, 0xAF, 0xD1, 0xC7, 0x09, 0xD1,
0x47, 0xB1, 0x1C, 0x56, 0xE6, 0x53, 0x24, 0x4D, 0xF0, 0x1F, 0x75, 0x21,
0xDD, 0x63, 0x0F, 0x86, 0xBC, 0xBB, 0xF2, 0xC0, 0x77, 0x8E, 0x2F, 0xEF,
0x21, 0x35, 0x2A, 0x2F, 0xC4, 0xBE, 0x4B, 0xEB, 0x88, 0x5E, 0x82, 0x1A,
0xDA, 0x71, 0xF2, 0x4F, 0x9B, 0x68, 0x4E, 0x64, 0x0D, 0x20, 0x31, 0x70,
0x3C, 0xF6, 0x42, 0x00, 0x9B, 0xB8, 0x24, 0xB4, 0xEF, 0x08, 0x61, 0x2A,
0x55, 0xCA, 0x0D, 0xE7, 0x45, 0x4D, 0xC0, 0x3B, 0x35, 0x6D, 0x50, 0x5A,
0x88, 0xEB, 0x73, 0x35, 0x86, 0x85, 0x23, 0xAF, 0x97, 0x5F, 0xB0, 0x62,
0xD2, 0x54, 0x29, 0x95, 0xC1, 0x5A, 0x42, 0xA3, 0x9C, 0x0A, 0x37, 0x2F,
0x42, 0x51, 0x24, 0x7F, 0xD4, 0xB2, 0x92, 0x3E, 0xAB, 0x9E, 0x7E, 0x79,
0x1D, 0xA5, 0x42, 0xC4, 0x2F, 0xB3, 0x68, 0x48, 0x7F, 0x0E, 0x49, 0xE0,
0xEF, 0xDC, 0x76, 0xB2, 0x86, 0x3B, 0x47, 0x73, 0x49, 0x0C, 0x98, 0xBA,
0x47, 0x24, 0x4B, 0xF0, 0x2A, 0xCE, 0xEF, 0xDD, 0x92, 0x61, 0x88, 0xE4,
0x81, 0xFE, 0x32, 0xBE, 0x08, 0x53, 0x25, 0xF7, 0xA5, 0x0C, 0xFC, 0xC9,
0x66, 0x77, 0xB1, 0x69, 0x57, 0x05, 0xE1, 0xD6, 0x3A, 0x0B, 0xA0, 0x6E,
0x6B, 0x90, 0x86, 0x5B, 0x48, 0x58, 0xD3, 0x28, 0xDA, 0x28, 0x0C, 0x5B,
0x7D, 0x86, 0xFF, 0x62, 0xE2, 0x84, 0xBA, 0x90, 0xBA, 0x43, 0x66, 0xF7,
0xAB, 0x83, 0xF0, 0xC8, 0xAF, 0xDE, 0x15, 0x03, 0xFB, 0x05, 0xA1, 0xD1,
0xDA, 0x2E, 0x04, 0x37, 0x33, 0x37, 0x14, 0x15, 0xBD, 0x75, 0x4F, 0x26,
0x3C, 0xF3, 0x6A, 0x1F, 0x7D, 0x8E, 0x23, 0xC4, 0xEB, 0xF3, 0xD8, 0x35,
0x25, 0x44, 0xE8, 0x1C, 0x5C, 0x5C, 0x14, 0x94, 0xAE, 0x2E, 0xDB, 0x52,
0xFB, 0xFC, 0x30, 0x82, 0x01, 0xDE, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x07, 0x01, 0x30, 0x1D, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x01, 0x2A, 0x04, 0x10, 0xA9, 0xAF, 0xB3, 0x34,
0xA7, 0x07, 0x4A, 0xBB, 0x58, 0xD5, 0xCF, 0xD3, 0xFF, 0xB4, 0xDE, 0x7E,
0x80, 0x82, 0x01, 0xB0, 0x46, 0x20, 0x3B, 0x68, 0x55, 0xEB, 0x5C, 0xDA,
0x67, 0xEE, 0x41, 0xAA, 0x06, 0x88, 0xD7, 0x95, 0x83, 0x12, 0xF4, 0x47,
0x17, 0x81, 0x08, 0xF7, 0x1B, 0x55, 0xEC, 0xF6, 0x12, 0xE2, 0x27, 0x05,
0x1D, 0x6A, 0xFA, 0x6C, 0x45, 0x43, 0x75, 0xE9, 0xFA, 0xF6, 0xA9, 0x14,
0x3E, 0x82, 0x3A, 0x29, 0xE8, 0xC7, 0xF8, 0x91, 0x2B, 0xD8, 0x42, 0x24,
0xEE, 0x8A, 0x4D, 0x06, 0xD0, 0x61, 0xA3, 0xF7, 0x2D, 0xF6, 0x81, 0xD2,
0xF1, 0x92, 0x95, 0xDD, 0x77, 0x01, 0xEB, 0xE4, 0x39, 0xBD, 0x84, 0x93,
0x10, 0xAB, 0x7D, 0xB9, 0xB6, 0x62, 0x51, 0x2F, 0xCE, 0x71, 0xE2, 0x2A,
0x23, 0x12, 0x6A, 0x36, 0xEF, 0x06, 0x2A, 0x89, 0x68, 0x22, 0x0B, 0x39,
0x23, 0x22, 0x6A, 0xF0, 0xBB, 0x3F, 0xA6, 0x25, 0xAA, 0xFE, 0xCD, 0x0F,
0x59, 0xF9, 0xD4, 0x48, 0x93, 0x03, 0x9E, 0xC4, 0xB8, 0xA3, 0x4D, 0x39,
0x78, 0x2E, 0xF8, 0xB8, 0x9D, 0x90, 0xA4, 0xB2, 0x5E, 0x24, 0x90, 0x48,
0x72, 0xD4, 0xB7, 0x51, 0x24, 0x61, 0x57, 0x72, 0x0F, 0xA4, 0xB3, 0x8F,
0x09, 0x48, 0xD4, 0xD5, 0x0F, 0x65, 0x9E, 0x8B, 0x2B, 0x8B, 0x3A, 0xDA,
0x52, 0xD0, 0xE3, 0x0E, 0x2B, 0x63, 0x31, 0xFC, 0xB5, 0x7B, 0xE2, 0x18,
0x3A, 0xB5, 0x0B, 0xD2, 0x90, 0x20, 0x53, 0x72, 0xCE, 0x05, 0xFA, 0x9B,
0xF2, 0xDC, 0xF6, 0x54, 0x7D, 0xD2, 0x80, 0x6F, 0xF0, 0x89, 0xF1, 0x33,
0x8F, 0x4D, 0x13, 0xDF, 0xAE, 0xA1, 0xC0, 0xC1, 0x9C, 0x23, 0x8A, 0x7D,
0xA2, 0x01, 0xB3, 0xDD, 0xE2, 0x45, 0xB3, 0x2C, 0xC8, 0x5F, 0xCF, 0xD4,
0x7C, 0x0E, 0xA2, 0x22, 0xF1, 0x1D, 0x7B, 0xA8, 0x84, 0x55, 0xBE, 0x5C,
0xCB, 0x61, 0x21, 0x97, 0xA0, 0x2C, 0x16, 0xE3, 0x26, 0x6A, 0x57, 0xA7,
0x44, 0x41, 0xA9, 0x4B, 0x18, 0xBE, 0x41, 0x94, 0x80, 0xF6, 0xCE, 0x0F,
0x6A, 0xD9, 0x07, 0x4E, 0xB6, 0x9A, 0xFA, 0x8E, 0x78, 0x95, 0x85, 0x3B,
0x53, 0x8D, 0xF4, 0x13, 0x11, 0x3A, 0x9B, 0x26, 0x99, 0xA9, 0xD5, 0x1F,
0x8C, 0x0B, 0xC0, 0x1F, 0xD1, 0xF0, 0xCA, 0xB4, 0x59, 0x5A, 0xE1, 0x5F,
0xCE, 0x80, 0x45, 0x28, 0x3D, 0xD1, 0xFD, 0x84, 0xF8, 0xE1, 0x02, 0x5C,
0xD4, 0x01, 0x7E, 0x63, 0x20, 0x3A, 0x4C, 0x0C, 0x2D, 0x25, 0x12, 0xCA,
0x51, 0xF0, 0x51, 0x1C, 0x0F, 0x65, 0x97, 0xCA, 0xD1, 0xF4, 0xCD, 0x01,
0x99, 0x65, 0xBA, 0x6A, 0x21, 0x16, 0xC1, 0xC6, 0xCB, 0x5D, 0x8F, 0xA1,
0x59, 0x89, 0x82, 0xD6, 0x2A, 0x75, 0x9B, 0xDF, 0x6F, 0xA9, 0x2A, 0x9E,
0xC7, 0x2A, 0x0D, 0xD9, 0x1B, 0x54, 0x21, 0x9E, 0xD6, 0xE2, 0xB2, 0x16,
0xE9, 0x4E, 0xAF, 0x57, 0x0F, 0xE3, 0x13, 0x5D, 0xD8, 0x64, 0x64, 0xA7,
0x5C, 0xAE, 0x5C, 0x04, 0x86, 0x6A, 0x2D, 0x52, 0x0D, 0xD2, 0x72, 0x8B,
0xF9, 0x8E, 0xBC, 0xEA, 0x0C, 0xA9, 0xEE, 0xCB, 0x23, 0x1A, 0xA6, 0x0C,
0x95, 0xDD, 0xFF, 0x70, 0xC6, 0xEC, 0xDA, 0x69, 0x8D, 0xC6, 0x51, 0x1A,
0x71, 0x2F, 0x97, 0x97, 0xF9, 0xB1, 0x1C, 0x9D, 0x1D, 0x06, 0xDB, 0x0C,
0x3F, 0x0D, 0xBF, 0xC2
};
var envelopedCms = new EnvelopedCms();
envelopedCms.Decode(encryptedMessage);
var parameters = envelopedCms.ContentEncryptionAlgorithm.Parameters;
Console.WriteLine($"envelopedCms.ContentEncryptionAlgorithm.Parameters: {parameters.Length}");
var decryptor = envelopedCms.GetType().GetField("_decryptorPal", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
.GetValue(envelopedCms);
var contentEncryption = decryptor!.GetType().GetField("_contentEncryptionAlgorithm", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
.GetValue(decryptor);
var parametersInternal = contentEncryption!.GetType().GetField("Parameters", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
.GetValue(contentEncryption);
Console.WriteLine($"envelopedCms._decryptorPal._contentEncryptionAlgorithm.Parameters.Length: {((ReadOnlyMemory<byte>)parametersInternal).Length}");
Expected behavior
The ContentEncryptionAlgorithm.Parameters
should return the encoded algorithm parameters.
Actual behavior
The ContentEncryptionAlgorithm.Parameters
can be seen as empty.
Known Workarounds
The CMS envelope can be manually read using AsnReader
using the code in the answer of that StackOverflow post.
Configuration
Windows 10 19045.5487
.NET 8.0.13 and .NET 9.0.2
x64
Other information
After some debugging, it seems the issue is that ToAlgorithmIdentifier
is not decoding the Parameters
unless the algorithm is RSA-OAEP. Why this is the case is unclear: the RFCs specify that any algorithm can have Parameters
.
public static AlgorithmIdentifier ToAlgorithmIdentifier(this CRYPT_ALGORITHM_IDENTIFIER cryptAlgorithmIdentifier)
{
.......
AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Oid.FromOidValue(oidValue, OidGroup.All), keyLength);
switch (oidValue)
{
case Oids.RsaOaep:
algorithmIdentifier.Parameters = cryptAlgorithmIdentifier.Parameters.ToByteArray();
break;
}
return algorithmIdentifier;
}
Weirdly, the code in DecryptorPalWindows.Decode
appears to partially compensate for this bug, by manually calling Parameters.ToByteArray
. But it only stores it in the internal _decryptorPal
, not in the returned AlgorithmIdentifier
.
AlgorithmIdentifierAsn contentEncryptionAlgorithmAsn;
using (SafeHandle sh = hCryptMsg.GetMsgParamAsMemory(CryptMsgParamType.CMSG_ENVELOPE_ALGORITHM_PARAM))
{
unsafe
{
CRYPT_ALGORITHM_IDENTIFIER* pCryptAlgorithmIdentifier = (CRYPT_ALGORITHM_IDENTIFIER*)(sh.DangerousGetHandle());
contentEncryptionAlgorithm = (*pCryptAlgorithmIdentifier).ToAlgorithmIdentifier();
contentEncryptionAlgorithmAsn.Algorithm = contentEncryptionAlgorithm.Oid.Value!;
contentEncryptionAlgorithmAsn.Parameters = (*pCryptAlgorithmIdentifier).Parameters.ToByteArray();
}
}
The fix:
It seems all that is needed is to change the code in ToAlgorithmIdentifier
to remove the switch
and unconditionally retrieve the Parameters
.
AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Oid.FromOidValue(oidValue, OidGroup.All), keyLength);
algorithmIdentifier.Parameters = cryptAlgorithmIdentifier.Parameters.ToByteArray();
return algorithmIdentifier;
}
The Decode
can then retrieve it from there:
CRYPT_ALGORITHM_IDENTIFIER* pCryptAlgorithmIdentifier = (CRYPT_ALGORITHM_IDENTIFIER*)(sh.DangerousGetHandle());
contentEncryptionAlgorithm = (*pCryptAlgorithmIdentifier).ToAlgorithmIdentifier();
contentEncryptionAlgorithmAsn.Algorithm = contentEncryptionAlgorithm.Oid.Value!;
contentEncryptionAlgorithmAsn.Parameters = contentEncryptionAlgorithm.Parameters;