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)
Comments: