Search code examples
c#.netcryptographymscapi

Crypto API functions equivalent in C#


Earlier our Application-A was in C++ and a message was signed before sending it to Application-B using crypto API functions in C++ , exactly similar to the example described in http://msdn.microsoft.com/en-us/library/windows/desktop/aa382372%28v=vs.85%29.aspx.

This message was again verified by Application-B using Crypto API functions in C++ (the above example again talks about how to verify an already signed message).

Now we are in the process of converting/migrating the old C++ Application-A to C#. I already found a way to sign the message using P-Invoke in C# and when the signed message was verified by Application-B (using C++ CryptVerifySignatureMessage) everything is working fine. Example is available in - http://blogs.msdn.com/b/alejacma/archive/2008/02/21/how-to-sign-a-message-and-verify-a-message-signature-c.aspx .

As @CodeInChaos has mentioned in his comments i want the leave the interop work to the framework (without using P-Invoke or other 3rd party implementation like BountyCastle)

So would like to know whether .net offers any API to sign a message (as a learning perspective too) , if so how can i achieve it.

NOTE:

I already tried crypto wrapper API RSACryptoServiceProvider offered by .Net.

    private byte[] SignData(byte[] data, string certThumbPrint)
    {
        X509Certificate2 cert = GetCertificate(); // finds the certificate with thumbprint
        RSACryptoServiceProvider rsaCryptoServiceProvider = (RSACryptoServiceProvider)cert.PrivateKey;
        return rsaCryptoServiceProvider.SignData(data, new SHA1CryptoServiceProvider());
    }

But found a major difference with the return value (byte array) of CryptSignMessage from C++ and RSACryptoServiceProvider.SignData() method from C#.

• CryptSignMessage: The CryptSignMessage function creates a hash of the specified content, signs the hash, and then encodes both the original message content and the signed hash.

• RSA.SignData: Computes the hash value of the specified byte array using the specified hash algorithm, and signs the resulting hash value.

Because of this difference , the Application-B when it verifies the message it throws error saying 'invalid signing' .

So i cant use this RSACryptoServiceProvider type offered by .net. Is there any other way to achieve the same using any .NET API's ? (when using .net API the output byte array should be similar to that of output when using PInvoke example as mentioned above) so that Application-B can work without any issues.

Any help is appreciated.


Solution

  • After some long research i found a way to do it . If someone else is looking for how to sign a message using PKCS7 format using a certificate in C# then here it is,

        public byte[] SignMsg(
            Byte[] msg,
            X509Certificate2 signerCert)
        {
            //  Place message in a ContentInfo object.
            //  This is required to build a SignedCms object.
            ContentInfo contentInfo = new ContentInfo(msg);
    
            //  Instantiate SignedCms object with the ContentInfo above.
            //  Has default SubjectIdentifierType IssuerAndSerialNumber.
            //  Has default Detached property value false, so message is
            //  included in the encoded SignedCms.
            SignedCms signedCms = new SignedCms(contentInfo);
    
            //  Formulate a CmsSigner object, which has all the needed
            //  characteristics of the signer.
            CmsSigner cmsSigner = new CmsSigner(signerCert);
    
            //  Sign the PKCS #7 message.
            Console.Write("Computing signature with signer subject " +
                "name {0} ... ", signerCert.SubjectName.Name);
            signedCms.ComputeSignature(cmsSigner);
            Console.WriteLine("Done.");
    
            //  Encode the PKCS #7 message.
            return signedCms.Encode();
        }
    

    Found the information from the link http://msdn.microsoft.com/en-us/library/ms180961%28v=vs.85%29.aspx .