.Net

时间:2016-12-02 02:25:26

标签: .net rsa bouncycastle signature sha512

我有一个" SHA512withRSA"签名,已签名的数据以及由第三方发送给我的公钥(以base64格式)。我需要使用公钥验证给定数据的签名是否正确。

基本问题是如何在.Net中验证这一点(最好是C#代码)?

仅使用.Net框架是否可行(似乎没有,看来RSACryptoServiceProvider是相关类,但它只支持SHA1作为签名算法)?

如果没有,我可以使用哪些库来实现这一目标?我一直在尝试BouncyCastle,但我无法找到解释如何清楚地解决这个问题的文档。几乎所有文档都是关于处理证书的,我既没有也不想要证书。

编辑:新示例代码(仍然失败)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace SignatureVerificationTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = DoWorkAsync();
            Console.ReadLine();
        }

        public static async Task DoWorkAsync()
        {
            var rawNotificationData = "merchantOrderId=e7ea4e4c-7ffa-4b19-8af9-ad875c493620&status=AUTHORISED&signature=hgvtCb6RjmhtumXTNuARW8fotZVoIJ%2FAaqXEow1pxA0Ic5afe0j2lGHO%2F6Q7rnPXqd728mMsmkjQSyxGDho7imxYGHEyEudt%2B%2FThOboAj%2FhePNLNfCeJR7DS7LIzO7SCTDkkTHZ%2F5sD5JhBATY3EFADrLnzzlF445mNRP%2Bv2vi3ogVC5k3oYfPCSmvOzd8DFUp6E6cwZelBGQjRHwVJf07%2B8x4esYxW%2FJii3z6quXzaKpImLFt3jaDTe109uwTwsv8bRBh%2F%2FSjFlHPaEr5QTAjTDA0IrMP6OggGTDkukW8sr7PUmQoq%2B4%2FLEIlHqXbuwZgJ2zr7fN75CcwWaj6FHfdiVvmeKGshO11DnHX7rN%2FTU36z7D7jRp%2Fykd4q409MorZUfZspHuXp2XRXo1732OZKRdYkX73eDphFKQUbmhGCf9wQq3xxxlw6Qr3ClTAi8SbOeY8IQIul%2Bp3x0X5G%2FiJmtiMcHMErxxCx2Y84OozrbvMEQP8qzY4FLUuV%2BKv9oragcXfDvpkJ4EAAqZPufXGZ3zCyk5yBJEXy6kqZ7ht16bpuD0aQMBlq2eTyQCOgtRvOPCFJYwCVJ8z43xQShffKa9Tj7gSgPE9LfvD3%2Ba4NdEh0Hg5yV7A7wABLySC%2F3thXvxVfVthNJEfdHZPhpAN2i3C5sql09R27k5SEV96c%3D";

            //Find the signature in the data, and url decode the value
            var x = rawNotificationData.Split(new char[] { '&' });

            string notificationSignature = null;
            foreach (var kvp in x)
            {
                var parts = kvp.Split(new char[] { '=' });
                if (parts[0] == "signature")
                {
                    notificationSignature = Uri.UnescapeDataString(parts[1]);
                    break;
                }
            }

            // Get the portion of the string that was signed
            // Should be;
            // merchantOrderId=e7ea4e4c-7ffa-4b19-8af9-ad875c493620&status=AUTHORISED
            var signedData = rawNotificationData.Substring(0, rawNotificationData.IndexOf("&signature=", StringComparison.OrdinalIgnoreCase));


            //Define the public key (base 64 encoded string)
            var publicKey = @"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv3kDhkB2yHn/4N1KtNsm
8IuWuqfnOIDxOnR2txELEtKL1YqSTiAtoBHWfvGg2OMN1gu0/owecT7d9+TnXx7M
NE4CGp3UOLkdTzRifDMrMh5T9WVvyKfV4Chnv/tkmK7BMSe+yS4T+3IQVBv0zFKu
Hm9Y8Zicc5sIkTRdpgeg1wbYZkbKA5qZcYkKUKpWHKmWVMyIqqhwAcKFA9dc0j8f
h6EM3JWQhFOFkXngUHX4O+L2mJ7rnYsMZzujoK3fNk/HIHYtJiZbkbEOzlDGhzp0
ZackirqZ2iqPRSgZsVcyqTthNiYxjkOvB5AYb/Ww+3T/UQ2zaB0ac7dgLKrq8BL5
HpVke93F4kQQ6M1BmIWfLiQt3FLRX+LxkcNC5q1SaPW8D/7lnYACM0JErrpA8/Dc
7U7Th6R8Wk99V8O0FN6+t9gr3lwG99Hw7AykstlPWMdFDch+ljM4JGf/hvxcZDlp
ACSRPf1BXNON1E6WMofyAXNSsuQ3kG/go0f5pzix+K/484y4PPKizbWTGWElK9sJ
d2jDFEGG5iCoFzJNLvEvuvzHHhP+cM5y7LTX7D0B8LPdj1kIEboTAIfdhQ1IVVYa
mSo7WTB/sHgxGk9+8K3EboGAzSmgIqlLrMZob1h8/32LsVSxKhgxs01tGotB7rJO
MmwP7rPHQjvq0NoPMTFPd+ECAwEAAQ==";

            // Decode the base64 key into raw bytes
            // (Decodes to 550 bytes)
            var rawKey = Convert.FromBase64String(publicKey);

            //Create an RSA crypto provider instance initialised with the key
            //Decodes to a 512 byte modulus and 3 (1, 0, 1) byte exponent
            using (var rsa = CreateRsa(rawKey))
            {
                bool signatureVerified = rsa.VerifyData
                (
                    System.Text.UTF8Encoding.UTF8.GetBytes(signedData),
                    CryptoConfig.MapNameToOID("SHA512"),
                    Convert.FromBase64String(notificationSignature)
                );

                //Fail if the signature didn't verify
                Debug.Assert(signatureVerified, "Signature did not verify.");
            }
        }

        private static readonly byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };

        private static RSACryptoServiceProvider CreateRsa(byte[] publicKey)
        {
            /*

            This code parses the publickey bytes to extract the relevant bits of the public key, the initialises 
            the rsa crypto provider with the key.
            Taken from "CreateRsa" below is modified from http://www.jensign.com/JavaScience/dotnet/pempublic/

            Original license below applies.

            Copyright(c)  2006 - 2014   JavaScience Consulting, Michel Gallant

            Permission is hereby granted, free of charge, to any person obtaining a copy
            of this software and associated documentation files(the "Software"), to deal
            in the Software without restriction, including without limitation the rights
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
            copies of the Software, and to permit persons to whom the Software is
            furnished to do so, subject to the following conditions:

            The above copyright notice and this permission notice shall be included in
            all copies or substantial portions of the Software.

            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
            THE SOFTWARE.
            */

            var mem = new System.IO.MemoryStream(publicKey);
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
            byte bt = 0;
            ushort twobytes = 0;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();  //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16(); //advance 2 bytes
            else
                return null;

            var seq = binr.ReadBytes(15);   //read the Sequence OID
            if (!CompareBytearrays(seq, SeqOID))  //make sure Sequence for OID is correct
                return null;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
                binr.ReadByte();  //advance 1 byte
            else if (twobytes == 0x8203)
                binr.ReadInt16(); //advance 2 bytes
            else
                return null;

            bt = binr.ReadByte();
            if (bt != 0x00)   //expect null byte next
                return null;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();  //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16(); //advance 2 bytes
            else
                return null;

            twobytes = binr.ReadUInt16();
            byte lowbyte = 0x00;
            byte highbyte = 0x00;

            if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
            else if (twobytes == 0x8202)
            {
                highbyte = binr.ReadByte(); //advance 2 bytes
                lowbyte = binr.ReadByte();
            }
            else
                return null;
            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
            int modsize = BitConverter.ToInt32(modint, 0);

            int firstbyte = binr.PeekChar();
            if (firstbyte == 0x00)
            { //if first byte (highest order) of modulus is zero, don't include it
                binr.ReadByte();  //skip this null byte
                modsize -= 1; //reduce modulus buffer size by 1
            }

            byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes

            if (binr.ReadByte() != 0x02)      //expect an Integer for the exponent data
                return null;
            int expbytes = (int)binr.ReadByte();    // should only need one byte for actual exponent data (for all useful values)
            byte[] exponent = binr.ReadBytes(expbytes);

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSAParameters RSAKeyInfo = new RSAParameters();
            RSAKeyInfo.Modulus = modulus;
            RSAKeyInfo.Exponent = exponent;
            RSA.ImportParameters(RSAKeyInfo);
            RSA.PersistKeyInCsp = false;
            return RSA;
        }

        private static bool CompareBytearrays(byte[] a, byte[] b)
        {
            if (a.Length != b.Length)
                return false;
            int i = 0;
            foreach (byte c in a)
            {
                if (c != b[i])
                    return false;
                i++;
            }
            return true;
        }

    }
}

2 个答案:

答案 0 :(得分:1)

RSACryptoServiceProvider可以执行此操作,前提是您将密钥存储在PROV_RSA_AES(24)中,而不是PROV_RSA_FULL(1)。

new RSACryptoServiceProvider()应该已经制作了PROV_RSA_AES,所以我认为导入密钥错误的唯一方法是你还指定了CspParameters输入(并明确更改了{ {1}}值)。

如果您有现有的RSACryptoServiceProvider对象,则可以使用

打开一个新对象
dwProvType

或者,如果你使用的是.NET 4.6+,请切换到CspParameters keyParams = new CspParameters(); CspKeyContainerInfo keyInfo = currentKey.CspKeyContainerInfo; keyParams.KeyContainerName = keyInfo.KeyContainerName; keyParams.ProviderType = 24 /*PROV_RSA_AES*/; keyParams.KeyNumber = (int)keyInfo.KeyNumber; keyParams.Flags = CspProviderFlags.UseExistingKey; if (keyInfo.MachineKeyStore) keyParams.Flags |= CspProviderFlags.UseMachineKeyStore; return new RSACryptoServiceProvider(keyParams); ,它对SHA-2签名有更稳定的支持。

答案 1 :(得分:0)

在与提供信息的第三方讨论后,我看到了错误的公钥。切换到正确的公钥后,验证现在成功。