2

I'm not able to sign data with the Service Application private key I downloaded from the Google Developer console. I get the following error:

OAuthTests.TestCrypto.testSha256SignWithGoogleKey:
System.Security.Cryptography.CryptographicException : Invalid algorithm specified.

at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.Utils._SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 dwFlags)
at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, String str)
at OAuthTests.TestCrypto.testSha256SignWithGoogleKey() in e:\Development\MiscellaneousProjects\RSSNewsFeeder\Oauth\OAuthTests.cs:line 43

Yes, I've asked this question before but not getting much help and since Stack's forum model is not easy to add onto an existing thread it seems that my best best to reword the question is to do just do that; reword and ask a new question.

I have written three unit tests (code below). The first unit test shows that I can sign and verify data using RSACryptoServiceProvider with SHA256, but this test does not use my Google certificate's private key.
When I use the Google's private key certificate and test (2nd test below), the code errors (error message above).
The 3rd test demonstrates using Google's private key and testing using SHA1 and this works! But not valid according to the specs.

Is there something wrong with the code below or is there something wrong with the certificate, or perhaps its my OS or other environmental issue? I'm developing in Windows C# 3.5 on a Windows 8.1 machine.

** THIS WORKS **
Not using Google Certificate

        var cert = new X509Certificate2(@"E:\Development\MiscellaneousProjects\RSSNewsFeeder\Samples\9d16ba9bd04468b4cd0dd241e34b980643fd5b21-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
        byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };
        using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey)
        {
            byte[] signature = rsa.SignData(data, "SHA256");

            if (rsa.VerifyData(data, "SHA256", signature))
            {
                Console.WriteLine("RSA-SHA256 signature verified");
            }
            else
            {
                Console.WriteLine("RSA-SHA256 signature failed to verify");
            }
        }

** THIS FAILS **
Using Google certificate and SHA256
Fails At: byte[] signature = rsa.SignData(data, "SHA256");

    [Test]
    public void testSha256SignWithGoogleKey()
    {
        var cert = new X509Certificate2(@"....41e34b980643fd5b21-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
        byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };
        using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key)
        {
            byte[] signature = rsa.SignData(data, "SHA256");

            if (rsa.VerifyData(data, "SHA256", signature))
            {
                Console.WriteLine("RSA-SHA256 signature verified");
            }
            else
            {
                Console.WriteLine("RSA-SHA256 signature failed to verify");
            }
        }
    }

** THIS WORKS **
Using Google certificate but SHA1

[Test]
public void testShaSignWithGoogleKey()
{
    var cert = new X509Certificate2(@"....dd241e34b980643fd5b21-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
    byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };
    using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey)
    {
        byte[] signature = rsa.SignData(data, "SHA1");

        if (rsa.VerifyData(data, "SHA1", signature))
        {
            Console.WriteLine("RSA-SHA1 signature verified");
        }
        else
        {
            Console.WriteLine("RSA-SHA1 signature failed to verify");
        }

    }
}
kstubs
  • 808
  • 4
  • 18
  • "Fails" how? Error message from .NET, or from Google, or what? – erickson May 06 '14 at 17:42
  • Invalid algorithm specified - This failure occurs in .NET – kstubs May 06 '14 at 17:47
  • I see the error originates in the `SignHash` method, but I don't see that method invoked in any of your code samples. It looks like there's a mismatch in the information you're providing here. – erickson May 06 '14 at 17:51
  • It fails here (2nd test) byte[] signature = rsa.SignData(data, "SHA256"); – kstubs May 06 '14 at 17:57

1 Answers1

1

First of all, there's a mistake in your example #2: you are trying to use public key for signing. And you should get the error: "Object contains only the public half of a key pair. A private key must also be provided."

But I suppose it was just a copy/paste mistake, and you already tried with private key.

The RSACryptoServiceProvider obtained from Google's certificate PrivateKey uses "Microsoft Base Cryptographic Provider v1.0", while newly created RSACryptoServiceProvider object uses "Microsoft Enhanced RSA and AES Cryptographic Provider".

The trick to workaround this is to export the bare math from cert's RSACSP to a new RSACSP object:

[Test]
public void testSha256SignWithGoogleKey()
{
    var cert = new X509Certificate2(@"....41e34b980643fd5b21-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);

    byte[] data = new byte[] { 0, 1, 2, 3, 4, 5 };

    using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey)
    {
        using (RSACryptoServiceProvider myRsa = new RSACryptoServiceProvider())
        {
            myRsa.ImportParameters(rsa.ExportParameters(true));

            byte[] signature = myRsa.SignData(data, "SHA256");

            if (myRsa.VerifyData(data, "SHA256", signature))
            {
                Console.WriteLine("RSA-SHA256 signature verified");
            }
            else
            {
                Console.WriteLine("RSA-SHA256 signature failed to verify");
            }
        }
    }
}  
mistika
  • 2,363
  • 2
  • 21
  • 25
  • This requires the key to be exportable, which, I believe, is not ideal from a security perspective. This answer to a similar question provides what seems to be a good alternative: http://stackoverflow.com/a/27637121/543814 – Timo Feb 20 '15 at 10:33
  • @Timo, in this example the file is loaded from a file, not a certificate store. The requirement to be exportable is not applicable here, because private key that is stored in the file is always 'exportable'. – mistika Feb 26 '15 at 20:25
  • good point. I did not realize that this exportability only applied to keys in a store, but it makes sense, come to think of it. – Timo Feb 26 '15 at 20:59