Signing Data with Google Service Account Private K

2019-02-25 18:40发布

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");
        }

    }
}

1条回答
Evening l夕情丶
2楼-- · 2019-02-25 19:02

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");
            }
        }
    }
}  
查看更多
登录 后发表回答