Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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)