Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.
Original URL: https://blogs.msdn.microsoft.com/alejacma/2011/02/18/how-to-verify-signatures-using-a-temporary-keyset-in-net/
Post name: How to verify signatures using a temporary keyset in .NET
Original author: Alejandro Campos Magencio
Posting date: 2011-02-18T04:25:23+00:00


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 selectingthe 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)


Share this article:

Comments:

Comments are closed.