title: SHA512withRSA Signature Verification in .Net
body: <p>I have a "SHA512withRSA" signature, the data that was signed, and a public key (in base64 form), sent to me by a 3rd party. I need to verify the signature is correct for the given data, using the public key.</p>
  
  <p>Basic question is how do I verify this in .Net (preferably C# code)?</p>
  
  <p>Is this possible with only the .Net framework (it seems not, it appears RSACryptoServiceProvider is the relevant class but it only supports SHA1 as a signing algorithm)?</p>
  
  <p>If not, which libraries can I use to achieve this? I've been trying BouncyCastle, but I can't find documentation that explains how to do this clearly. Almost all the documentation is about dealing with certificates, and I neither have nor want certificates.</p>
  
  <p>Edit: New sample code (still fails)</p>
  
  <pre><code>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&amp;status=AUTHORISED&amp;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[] { '&amp;' });
  
              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&amp;status=AUTHORISED
              var signedData = rawNotificationData.Substring(0, rawNotificationData.IndexOf("&amp;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;
          }
  
      }
  }
  </code></pre>