How to verify signatures using a temporary keyset in .NET

Hi all,

Some time ago a customer of mine had issues to verify signatures with RSACryptoServiceProvider when users had a mandatory and/or roaming profile, as he was getting the following exception:

"System.Security.Cryptography.CryptographicException: Cryptographic Service Provider (CSP) for this implementation could not be acquired".

I already talked about this: RSACryptoServiceProvider fails when used with mandatory profiles. There I mentioned the following workaround:

The way to i.e. verify signatures using temporary keysets in .NET would be to use CryptoAPI directly through P/Invoke (Platform Invoke), instead of using RSACryptoServiceProvider.

So I created the following sample that uses CryptoAPI via P/Invoke to be able to verify signatures using a temporary keyset. Note that the sample creates the signature with RSACryptoServiceProvider, and verifies it with CryptVerifySignature API after selecting the temporary keyset.

Form1.cs:

 using System;

using System.Xml;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Security.Cryptography;

using System.Runtime.InteropServices;

using System.IO;



namespace WindowsApplication1

{

 /// <summary>

 /// Summary description for Form1.

 /// </summary>

 public class Form1 : System.Windows.Forms.Form

 {

  private System.Windows.Forms.GroupBox groupBox1;

  private System.Windows.Forms.GroupBox groupBox2;

  private System.Windows.Forms.Button button11;

  private System.Windows.Forms.Button button21;

  private System.Windows.Forms.TextBox textBox11;

  private System.Windows.Forms.Label label11;

  private System.Windows.Forms.TextBox textBox21;

  private System.Windows.Forms.Label label31;

  private System.Windows.Forms.TextBox textBox31;

  private System.Windows.Forms.Label label21;

  private System.Windows.Forms.Button button22;

  private System.Windows.Forms.TextBox textBox12;

  private System.Windows.Forms.Label label12;

  private System.Windows.Forms.TextBox textBox22;

  private System.Windows.Forms.Label label32;

  private System.Windows.Forms.TextBox textBox32;

  private System.Windows.Forms.Label label22;

  private System.Windows.Forms.Button button12;

  /// <summary>

  /// Required designer variable.

  /// </summary>

  private System.ComponentModel.Container components = null;



  public Form1()

  {

   //

   // Required for Windows Form Designer support

   //

   InitializeComponent();



   //

   // TODO: Add any constructor code after InitializeComponent call

   //

  }



  /// <summary>

  /// Clean up any resources being used.

  /// </summary>

  protected override void Dispose( bool disposing )

  {

   if( disposing )

   {

    if (components != null) 

    {

     components.Dispose();

    }

   }

   base.Dispose( disposing );

  }



  #region Windows Form Designer generated code

  /// <summary>

  /// Required method for Designer support - do not modify

  /// the contents of this method with the code editor.

  /// </summary>

  private void InitializeComponent()

  {

   this.button11 = new System.Windows.Forms.Button();

   this.button21 = new System.Windows.Forms.Button();

   this.textBox11 = new System.Windows.Forms.TextBox();

   this.label11 = new System.Windows.Forms.Label();

   this.textBox21 = new System.Windows.Forms.TextBox();

   this.label31 = new System.Windows.Forms.Label();

   this.textBox31 = new System.Windows.Forms.TextBox();

   this.label21 = new System.Windows.Forms.Label();

   this.groupBox1 = new System.Windows.Forms.GroupBox();

   this.groupBox2 = new System.Windows.Forms.GroupBox();

   this.button22 = new System.Windows.Forms.Button();

   this.textBox12 = new System.Windows.Forms.TextBox();

   this.label12 = new System.Windows.Forms.Label();

   this.textBox22 = new System.Windows.Forms.TextBox();

   this.label32 = new System.Windows.Forms.Label();

   this.textBox32 = new System.Windows.Forms.TextBox();

   this.label22 = new System.Windows.Forms.Label();

   this.button12 = new System.Windows.Forms.Button();

   this.groupBox1.SuspendLayout();

   this.groupBox2.SuspendLayout();

   this.SuspendLayout();

   // 

   // button11

   // 

   this.button11.Location = new System.Drawing.Point(8, 64);

   this.button11.Name = "button11";

   this.button11.Size = new System.Drawing.Size(216, 32);

   this.button11.TabIndex = 0;

   this.button11.Text = "Sign";

   this.button11.Click += new System.EventHandler(this.button11_Click);

   // 

   // button21

   // 

   this.button21.Location = new System.Drawing.Point(8, 360);

   this.button21.Name = "button21";

   this.button21.Size = new System.Drawing.Size(216, 32);

   this.button21.TabIndex = 1;

   this.button21.Text = "Verify";

   this.button21.Click += new System.EventHandler(this.button21_Click);

   // 

   // textBox11

   // 

   this.textBox11.Location = new System.Drawing.Point(8, 40);

   this.textBox11.Name = "textBox11";

   this.textBox11.Size = new System.Drawing.Size(216, 20);

   this.textBox11.TabIndex = 2;

   this.textBox11.Text = "OwT4Zk1hjsnSDOBdGSlkxdWGgoc=";

   // 

   // label11

   // 

   this.label11.Location = new System.Drawing.Point(8, 24);

   this.label11.Name = "label11";

   this.label11.Size = new System.Drawing.Size(104, 16);

   this.label11.TabIndex = 3;

   this.label11.Text = "Hash (Base64)";

   // 

   // textBox21

   // 

   this.textBox21.Location = new System.Drawing.Point(8, 120);

   this.textBox21.Multiline = true;

   this.textBox21.Name = "textBox21";

   this.textBox21.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;

   this.textBox21.Size = new System.Drawing.Size(216, 104);

   this.textBox21.TabIndex = 4;

   this.textBox21.Text = "";

   // 

   // label31

   // 

   this.label31.Location = new System.Drawing.Point(8, 232);

   this.label31.Name = "label31";

   this.label31.Size = new System.Drawing.Size(168, 16);

   this.label31.TabIndex = 5;

   this.label31.Text = "Signature (Base64)";

   // 

   // textBox31

   // 

   this.textBox31.Location = new System.Drawing.Point(8, 248);

   this.textBox31.Multiline = true;

   this.textBox31.Name = "textBox31";

   this.textBox31.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;

   this.textBox31.Size = new System.Drawing.Size(216, 104);

   this.textBox31.TabIndex = 6;

   this.textBox31.Text = "";

   // 

   // label21

   // 

   this.label21.Location = new System.Drawing.Point(8, 104);

   this.label21.Name = "label21";

   this.label21.Size = new System.Drawing.Size(104, 16);

   this.label21.TabIndex = 7;

   this.label21.Text = "RSA object (XML)";

   // 

   // groupBox1

   // 

   this.groupBox1.Controls.Add(this.button21);

   this.groupBox1.Controls.Add(this.textBox11);

   this.groupBox1.Controls.Add(this.label11);

   this.groupBox1.Controls.Add(this.textBox21);

   this.groupBox1.Controls.Add(this.label31);

   this.groupBox1.Controls.Add(this.textBox31);

   this.groupBox1.Controls.Add(this.label21);

   this.groupBox1.Controls.Add(this.button11);

   this.groupBox1.Location = new System.Drawing.Point(8, 8);

   this.groupBox1.Name = "groupBox1";

   this.groupBox1.Size = new System.Drawing.Size(232, 400);

   this.groupBox1.TabIndex = 8;

   this.groupBox1.TabStop = false;

   this.groupBox1.Text = "No P/Invoke";

   // 

   // groupBox2

   // 

   this.groupBox2.Controls.Add(this.button22);

   this.groupBox2.Controls.Add(this.textBox12);

   this.groupBox2.Controls.Add(this.label12);

   this.groupBox2.Controls.Add(this.textBox22);

   this.groupBox2.Controls.Add(this.label32);

   this.groupBox2.Controls.Add(this.textBox32);

   this.groupBox2.Controls.Add(this.label22);

   this.groupBox2.Controls.Add(this.button12);

   this.groupBox2.Location = new System.Drawing.Point(248, 8);

   this.groupBox2.Name = "groupBox2";

   this.groupBox2.Size = new System.Drawing.Size(232, 400);

   this.groupBox2.TabIndex = 9;

   this.groupBox2.TabStop = false;

   this.groupBox2.Text = "P/Invoke";

   // 

   // button22

   // 

   this.button22.Location = new System.Drawing.Point(8, 360);

   this.button22.Name = "button22";

   this.button22.Size = new System.Drawing.Size(216, 32);

   this.button22.TabIndex = 1;

   this.button22.Text = "Verify";

   this.button22.Click += new System.EventHandler(this.button22_Click);

   // 

   // textBox12

   // 

   this.textBox12.Location = new System.Drawing.Point(8, 40);

   this.textBox12.Name = "textBox12";

   this.textBox12.Size = new System.Drawing.Size(216, 20);

   this.textBox12.TabIndex = 2;

   this.textBox12.Text = "OwT4Zk1hjsnSDOBdGSlkxdWGgoc=";

   // 

   // label12

   // 

   this.label12.Location = new System.Drawing.Point(8, 24);

   this.label12.Name = "label12";

   this.label12.Size = new System.Drawing.Size(104, 16);

   this.label12.TabIndex = 3;

   this.label12.Text = "Hash (Base64)";

   // 

   // textBox22

   // 

   this.textBox22.Location = new System.Drawing.Point(8, 120);

   this.textBox22.Multiline = true;

   this.textBox22.Name = "textBox22";

   this.textBox22.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;

   this.textBox22.Size = new System.Drawing.Size(216, 104);

   this.textBox22.TabIndex = 4;

   this.textBox22.Text = "";

   // 

   // label32

   // 

   this.label32.Location = new System.Drawing.Point(8, 232);

   this.label32.Name = "label32";

   this.label32.Size = new System.Drawing.Size(168, 16);

   this.label32.TabIndex = 5;

   this.label32.Text = "Signature (Base64)";

   // 

   // textBox32

   // 

   this.textBox32.Location = new System.Drawing.Point(8, 248);

   this.textBox32.Multiline = true;

   this.textBox32.Name = "textBox32";

   this.textBox32.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;

   this.textBox32.Size = new System.Drawing.Size(216, 104);

   this.textBox32.TabIndex = 6;

   this.textBox32.Text = "";

   // 

   // label22

   // 

   this.label22.Location = new System.Drawing.Point(8, 104);

   this.label22.Name = "label22";

   this.label22.Size = new System.Drawing.Size(104, 16);

   this.label22.TabIndex = 7;

   this.label22.Text = "RSA object (XML)";

   // 

   // button12

   // 

   this.button12.Location = new System.Drawing.Point(8, 64);

   this.button12.Name = "button12";

   this.button12.Size = new System.Drawing.Size(216, 32);

   this.button12.TabIndex = 0;

   this.button12.Text = "Sign";

   this.button12.Click += new System.EventHandler(this.button12_Click);

   // 

   // Form1

   // 

   this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

   this.ClientSize = new System.Drawing.Size(488, 414);

   this.Controls.Add(this.groupBox1);

   this.Controls.Add(this.groupBox2);

   this.Name = "Form1";

   this.Text = "Form1";

   this.groupBox1.ResumeLayout(false);

   this.groupBox2.ResumeLayout(false);

   this.ResumeLayout(false);



  }

  #endregion



  /// <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]

  static void Main() 

  {

   Application.Run(new Form1());

  }



  private void button11_Click(object sender, System.EventArgs e)

  {

   textBox21.Text = "";

   textBox31.Text = "";



   // Create RSA object and show it

   RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

   textBox21.Text = RSA.ToXmlString(false);



   // Sign hash

   RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);

   RSAFormatter.SetHashAlgorithm("SHA1");

   byte[] signedHashValue = RSAFormatter.CreateSignature(

    Convert.FromBase64String(textBox11.Text)); 

  

   // Show signature

   textBox31.Text = Convert.ToBase64String(signedHashValue);  

  }



  private void button21_Click(object sender, System.EventArgs e)

  {

   // Re-create RSA object

   RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

   RSA.FromXmlString(textBox21.Text);



   // Verify signature 

   RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);

   RSADeformatter.SetHashAlgorithm("SHA1");

   if (RSADeformatter.VerifySignature(

    Convert.FromBase64String(textBox11.Text), 

    Convert.FromBase64String(textBox31.Text))

    )

   { 

    MessageBox.Show("Signature verified!");

   } 

   else 

   {

    MessageBox.Show("Signature NOT verified!");

   }

  }





  private void button12_Click(object sender, System.EventArgs e)

  {

   textBox22.Text = "";

   textBox32.Text = "";



   // Create RSA object and show it

   RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

   textBox22.Text = RSA.ToXmlString(false);



   // Sign hash

   RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);

   RSAFormatter.SetHashAlgorithm("SHA1");

   byte[] signedHashValue = RSAFormatter.CreateSignature(

    Convert.FromBase64String(textBox12.Text)); 

  

   // Show signature

   textBox32.Text = Convert.ToBase64String(signedHashValue);   

  }



  private void button22_Click(object sender, System.EventArgs e)

  {

   // Variables

   IntPtr hProv = IntPtr.Zero;

   String pszContainer = null;

   String pszProvider = null;

   UInt32 dwProvType = 0;

   UInt32 dwFlags = 0;

   IntPtr hHash = IntPtr.Zero;

   Byte[] pbHash = null;

   IntPtr hPubKey = IntPtr.Zero;

   Byte[] pbSignature = null;

   Byte[] pbModulus = null;

   Byte[] pbExponent = null;

   Byte[] pbPubKey = null;



   try 

   {

    // Acquire CSP

    hProv = IntPtr.Zero;  

    pszContainer = null; // Required for crypt_verifycontext 

    pszProvider = null; // Can use null for default provider

    dwProvType  = Win32.PROV_RSA_FULL;

    dwFlags = Win32.CRYPT_VERIFYCONTEXT; //No private key access required.



    if (!Win32.CryptAcquireContext(ref hProv, pszContainer, pszProvider, dwProvType, dwFlags))

    {

     throw new Exception("CryptAcquireContext error", new Win32Exception(Marshal.GetLastWin32Error()));

    }



    // Create hash object

    hHash = IntPtr.Zero;



    if (!Win32.CryptCreateHash(hProv, Win32.CALG_SHA1, IntPtr.Zero, 0, ref hHash))

    {

     throw new Exception("CryptCreateHash error", new Win32Exception(Marshal.GetLastWin32Error()));

    }



    // Fill hash object with our hash

    pbHash = Convert.FromBase64String(textBox12.Text);



    if (!Win32.CryptSetHashParam(hHash, Win32.HP_HASHVAL, pbHash, 0))

    {

     throw new Exception("CryptSetHashParam error", new Win32Exception(Marshal.GetLastWin32Error()));

    }



    // Import public key

    //

    // Public Key BLOB:

    // "

    //  PUBLICKEYSTRUC blobheader;

    //  RSAPUBKEY rsapubkey;

    //  BYTE modulus[rsapubkey.bitlen/8];

    // "

    XmlDocument xml = new XmlDocument();

    xml.InnerXml = textBox22.Text;

    pbModulus = Convert.FromBase64String(xml.FirstChild.ChildNodes.Item(0).InnerText);

    pbExponent = Convert.FromBase64String(xml.FirstChild.ChildNodes.Item(1).InnerText);



    MemoryStream keyBlob = new MemoryStream(

     Marshal.SizeOf(typeof(Win32.PUBLICKEYSTRUC)) +

     Marshal.SizeOf(typeof(Win32.RSAPUBKEY)) +

     pbModulus.Length

    );

    BinaryWriter bw = new BinaryWriter(keyBlob);



    bw.Write((Byte)Win32.PUBLICKEYBLOB);  // blobheader.bType

    bw.Write((Byte)Win32.CUR_BLOB_VERSION);  // blobheader.bVersion

    bw.Write((UInt16)0);      // blobheader.reserved

    bw.Write((UInt32)Win32.CALG_RSA_SIGN);  // blobheader.aiKeyAlg



    bw.Write((UInt32)0x31415352);    // rsapubkey.magic = "RSA1"

    bw.Write((UInt32)(pbModulus.Length * 8)); // rsapubkey.bitlen

    

    // rsapubkey.pubexp 

    Byte[] buffer = new Byte[Marshal.SizeOf(typeof(UInt32))];

    Array.Copy(pbExponent, 0, buffer, 0, pbExponent.Length);

    bw.Write(buffer);



    // modulus

    Array.Reverse(pbModulus); 

    bw.Write(pbModulus);      



    pbPubKey = keyBlob.ToArray();



    if (!Win32.CryptImportKey(hProv, pbPubKey, pbPubKey.Length, IntPtr.Zero, 0, ref hPubKey))

    {

     throw new Exception("CryptImportKey error", new Win32Exception(Marshal.GetLastWin32Error()));

    }



    // Verify signature

    pbSignature = Convert.FromBase64String(textBox32.Text);

    Array.Reverse(pbSignature);



    if (!Win32.CryptVerifySignature(hHash, pbSignature, pbSignature.Length, hPubKey, null, 0))

    {

     throw new Exception("CryptVerifySignature error", new Win32Exception(Marshal.GetLastWin32Error()));

    } 

    else 

    {

     MessageBox.Show("Signature verified!!!");

    }

   } 

   catch (Exception ex)

   {

    // Any errors?

    string msg;

    if (ex.InnerException == null) 

    {

     msg = ex.Message;

    } 

    else 

    {

     msg = ex.Message + " --> " + ex.InnerException.Message;

    }

    MessageBox.Show(msg);

   }

   finally 

   {

    // Destroy hash

    if (hHash != IntPtr.Zero) 

    {

     Win32.CryptDestroyHash(hHash);

    }



    // Destroy public key

    if (hPubKey != IntPtr.Zero) 

    {

     Win32.CryptDestroyKey(hPubKey);

    }



    // Release the CSP

    //

    if (hProv != IntPtr.Zero) 

    {

     Win32.CryptReleaseContext(hProv, 0);

    }  

   }

  }

 }

}


Win32.cs:

 using System;

using System.Windows.Forms;

using System.Runtime.InteropServices;



public class Win32

{

 #region CONSTS



 public const UInt32 CALG_SHA1 = (4 << 13) | 4;

 public const UInt32 CALG_RSA_SIGN = (1 << 13) | (2 << 9);

 public const UInt32 PROV_RSA_FULL = 0x00000001;

 public const UInt32 CRYPT_VERIFYCONTEXT = 0xF0000000; //No private key access required

 public const UInt32 X509_ASN_ENCODING = 0x00000001;

 public const UInt32 PKCS_7_ASN_ENCODING = 0x00010000;

 public const UInt32 HP_HASHVAL = 0x00000002;

 public const UInt32 HP_HASHSIZE = 0x00000004;

 public const UInt32 PUBLICKEYBLOBEX = 0x0A;

 public const UInt32 PUBLICKEYBLOB = 0x06;

 public const UInt32 CUR_BLOB_VERSION = 0x02;

 public const UInt32 CRYPT_EXPORTABLE = 0x00000001;



 #endregion



 #region STRUCTS



 [StructLayout(LayoutKind.Sequential)]

 public struct PUBLICKEYSTRUC

 {

  public Byte bType;

  public Byte bVersion;

  public UInt16 reserved;

  public UInt32 aiKeyAlg;

 }



 [StructLayout(LayoutKind.Sequential)]

 public struct RSAPUBKEY 

 {

  public UInt32 magic;

  public UInt32 bitlen;

  public UInt32 pubexp;

 }



 #endregion



 #region FUNCTIONS



 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

 public static extern bool CryptAcquireContext(

  ref IntPtr hProv,

  String pszContainer,

  String pszProvider,

  UInt32 dwProvType,

  UInt32 dwFlags

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptCreateHash(

  IntPtr hProv,

  UInt32 Algid,

  IntPtr hKey,

  UInt32 dwFlags,

  ref IntPtr phHash

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptGetHashParam(

  IntPtr hHash,

  UInt32 dwParam,

  ref UInt32 pbData,

  ref UInt32 pdwDataLen,

  UInt32 dwFlags

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptSetHashParam(

  IntPtr hHash,

  UInt32 dwParam,

  Byte[] pbData,

  UInt32 dwFlags

 );



 [DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]

 public static extern bool CryptImportPublicKeyInfo(

  IntPtr hCryptProv,

  UInt32 dwCertEncodingType,

  IntPtr pInfo,

  ref IntPtr phKey

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptImportKey(

  IntPtr hProv,

  Byte[] pbData,

  Int32 dwDataLen,

  IntPtr hPubKey,

  UInt32 dwFlags,

  ref IntPtr phKey

 );



 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]

 public static extern bool CryptVerifySignature(

  IntPtr hHash,

  Byte[] pbSignature,

  Int32 dwSigLen,

  IntPtr hPubKey,

  String sDescription,

  UInt32 dwFlags

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptDestroyKey(

  IntPtr hKey

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptDestroyHash(

  IntPtr hHash

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptReleaseContext(

  IntPtr hProv,

  UInt32 dwFlags

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptGenKey(

  IntPtr hProv,

  UInt32 Algid,

  UInt32 dwFlags,

  ref IntPtr phKey

 );



 [DllImport("advapi32.dll", SetLastError=true)]

 public static extern bool CryptExportKey(

  IntPtr hKey,

  IntPtr hExpKey,

  UInt32 dwBlobType,

  UInt32 dwFlags,

  Byte[] pbData,

  ref UInt32 pdwDataLen

 );



 // Helper function to convert struts & classes to byte array

 public static byte[] RawSerialize(object anything) 

 { 

  int rawsize = Marshal.SizeOf(anything); 

  IntPtr buffer = Marshal.AllocHGlobal(rawsize); 

  Marshal.StructureToPtr(anything, buffer, false); 

  byte[] rawdatas = new byte[rawsize]; 

  Marshal.Copy(buffer, rawdatas, 0, rawsize); 

  Marshal.FreeHGlobal(buffer); 

  return rawdatas; 

 } 



 #endregion 

}

 

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)