Hi all,

 

We don’t officially support the .NET CMS classes with SHA-2 algorithms. This won’t work on Vista and later with third-party CSPs, for instance:

"An internal error ocurred" when using SHA-2 algorithms with SignedCMS

"Invalid provider type specified" when using CNG providers with .NET CMS classes

 

But it will work on Vista and later with MS providers (e.g. with a cert associated to "Microsoft Enhanced RSA and AES Cryptographic Provider")

 

Now, in a Windows XP scenario we have no CNG, so the issue is a bit different. The support to SHA-2 itself on Windows XP is a bit limited:

SHA-2 support on Windows XP

 

So on XP we need a CSP of type PROV_RSA_AES like "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" (implemented in Rsaenh.dll) to work with SHA-2. 

 

The other day a customer of mine was getting an “Invalid algorithm specified”  when calling signedCms.CheckSignature to verify SHA256 signed files on Windows XP SP3

Debugging the issue I saw that .NET ends up calling CryptMsgOpenToDecode API behind the scenes with a NULL hCryptProv. That has the following implications:

 

CryptMsgOpenToDecode Function

"

Windows Server 2003, Windows XP, and Windows 2000: Specifies a handle for the cryptographic provider to use for hashing the message. For signed messages, hCryptProv is used for signature verification.This parameter's data type is HCRYPTPROV.

Unless there is a strong reason for passing in a specific cryptographic provider in hCryptProv, set this parameter to NULL. Passing in NULL causes the default RSA or DSS provider to be acquired before performing hash, signature verification, or recipient encryption operations.

"

 

And I could verify that we indeed end up getting the default CSP for the PROV_RSA_FULL provider type. So basically, with current .NET and Windows XP SP3 designs, we will never be able to use a provider of type PROV_RSA_AES with SignedCMS.

 

Summing up, current version of SignedCMS won’t support SHA-2 algorithms on Windows XP SP3

 

So we took my LargeCMS sample as a base for a workaround:

How to call CryptMsg API in streaming mode - LargeCMS full sample

 

The idea was to forget about SignedCMS by calling CryptMsg API directly. This way we could pass a valid HCRYPTPROV handle of a CSP that works with SHA-2 (like our "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)") to CryptMsgOpenToDecode API. 

 

My customer was able to use my sample with some minor modifications to verify the signature in XP and Vista: he modified the OpenMessageToDecode and the CheckSignature methods to pass in a handle to the proper CSP that supports SHA-2. The SignedLargeCMS.VerifySignature method in the sample now becomes:

 

public void VerifySignature(FileStream dataFile, FileStream encodedFile, FileStream decodedFile)
{
SafeCSPHandle hCryptProv = SafeCSPHandle.Null;

try
{
hCryptProv = Win32.AcquireRsaEnhVerifyContext();

Decode(hCryptProv.DangerousGetHandle(), dataFile, encodedFile, decodedFile);

// Check all signature in the message
Win32.CheckSignatures2(hCryptProv.DangerousGetHandle(), m_hMsg);
}
finally
{
hCryptProv.Dispose();
}

// Check parameters
if (m_hMsg.IsInvalid)
{
throw new Exception("CheckSignature error: Decode method must be called first");
}
}

 

 

The AcquireRsaEnhVerifyContext method is in Win32Helper file:

public static SafeCSPHandle AcquireRsaEnhVerifyContext()
{
SafeCSPHandle hCryptProv = SafeCSPHandle.Null;

CryptAcquireContext(
out hCryptProv,
null,
"Microsoft Enhanced RSA and AES Cryptographic Provider",
PROV_RSA_ENH, // type 24, 0x00000018
CRYPT_VERIFYCONTEXT);
if ((hCryptProv == null) || (hCryptProv.IsInvalid))
{
throw new Exception("CryptAcquireContext error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
}

return hCryptProv;
}

 

Note that in AcquireRsaEnhVerifyContext we will have to use "Microsoft Enhanced RSA and AES Cryptographic Provider" on Vista and later, and "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" on XP.

 

 

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)