Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.
Original URL: https://blogs.msdn.microsoft.com/alejacma/2010/12/21/more-on-net-cms-classes-and-sha-2-algorithms/
Post name: More on .NET CMS classes and SHA-2 algorithms
Original author: Alejandro Campos Magencio
Posting date: 2010-12-21T09:06:00+00:00


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 callingCryptMsgOpenToDecode APIbehind the sceneswith 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 validHCRYPTPROV 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 modificationsto verify the signature in XP and Vista: he modified theOpenMessageToDecode 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 inAcquireRsaEnhVerifyContext 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)


Share this article:

Comments:

Comments are closed.