Wednesday 21 February 2018

C# - RSA Digital Signatures

I am grateful to Adnan Samuel who has penned a Code Project article called Implementing Digital Signing in .NET which uses .NET cryptography classes to show example of encrypting and digitally signing a message. I am indebted to this code base but because I don't need encryption I simplified the code, also I folded all the code from separate class files into one file so it is easier to paste in the reader's instance of Visual Studio.

Another change I made is to segment the logic because actually in my use case one half on the code will run on a server and the other half will run on the client and the given code base tended to keep global variables hanging around and pass them between the two halves and I thought this unrealistic. I have added an extra textbox on the form to show the public key being exported.

Also I changed some of the textboxes to be writeable and so tamperable, so that one can tamper with the values and the signature report to provoke a verification fail.

Also another change is to convert to C# because since the time of writing of the article (2004) C# has stretched its lead over VB (IMHO). [Also I like the partial class split way the Forms are defined in C#].

Also, I have updated the SHA hash from SHA1 to SHA256 more in line with current standards of FIPS 140/2 Suite B

Here is amended code, create a C# Windows Forms (.NET Framework) project and then paste in the following code into Form1.cs


// Based on code by Adnan Samuel, Code Project,
// https://www.codeproject.com/script/Membership/View.aspx?mid=1307566

using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        // instance of class ServerSide (Bob)
        public ServerSide mySender = new ServerSide();

        // instance of class ClientSide (Alice)
        public ClientSide myReceiver = new ClientSide();


        public Form1()
        {
            InitializeComponent();
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            myReceiver = null;
            mySender = null;
            Application.Exit();
        }

        private void btn2_GenerateSignatureBlock_Click(object sender, EventArgs e)
        {
            //* Hash and Sign only
            Byte[] nonencryptedSignature;
            nonencryptedSignature = mySender.HashAndSign(Encoding.UTF8.GetBytes(
                                                              txtPlainText.Text));

            txtNonEncryptedSignatureBlock.Text = Convert.ToBase64String(
                                                           nonencryptedSignature);

            grpPublicKey.Text = $"Public Key ({mySender.KeySize} bit)";
            txtPublicKey.Text = mySender.ToString2();

            btn3b_VerifySignature.Enabled = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            btn3b_VerifySignature.Enabled = false;

            // build the link
            LinkLabel.Link link = new LinkLabel.Link();
            link.LinkData = 
            "https://www.codeproject.com/Articles/8868/Implementing-Digital-Signing-in-NET";
            linkLabel1.Links.Add(link);
        }

        private void btn3b_VerifySignature_Click(object sender, EventArgs e)
        {
            Byte[] signature;
            signature = Convert.FromBase64String(
                                              txtNonEncryptedSignatureBlock.Text);

            Byte[] plainTextAsBytes;
            plainTextAsBytes = Encoding.UTF8.GetBytes(txtPlainText.Text);

            if (myReceiver.VerifyHash(txtPublicKey.Text, plainTextAsBytes, 
                                                                       signature))
            {
                txtPlainTextReceiver.Text = txtPlainText.Text;
                MessageBox.Show("Signature Valid", "Verification Results", 
                                MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                txtPlainTextReceiver.Text = "";
                MessageBox.Show("Invalid Signature", "Verification Results", 
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void linkLabel1_LinkClicked(object sender, 
                                                  LinkLabelLinkClickedEventArgs e)
        {
            // Send the URL to the operating system.
            Process.Start(e.Link.LinkData as string);
        }
    }

    public class ServerSide //Bob
    {
        //========================================================================
        //  Bob  is the Server who will hash and sign license documents
        //========================================================================
        private RSACryptoServiceProvider rsaCSPServerSide = 
                                                   new RSACryptoServiceProvider(); 

        public RSAParameters PublicParameters
        {
            get
            {
                return rsaCSPServerSide.ExportParameters(false);
            }
        }

        public int KeySize
        {
            get
            {
                return rsaCSPServerSide.KeySize;
            }
        }

        public byte[] HashAndSign(byte[] input)
        {
            // create new instance of SHA256 hash algorithm to compute hash
            SHA256Managed hash = new SHA256Managed();

            // a byte array to store hash value
            byte[] hashedData;

            // compute hash with algorithm specified as here we have SHA256
            hashedData = hash.ComputeHash(input);

            // sign Data using private key and  OID is simple name 
            // of the algorithm for which to get the object identifier (OID)
            return rsaCSPServerSide.SignHash(hashedData, 
                                                CryptoConfig.MapNameToOID("SHA256"));
        }

        public string ToString2()
        {
            return rsaCSPServerSide.ToXmlString(false);
        }

    }


    public class ClientSide
    {
        //'========================================================================
        //' Alice is Client who will import Public Key and verify signed hash
        //'========================================================================

        // Manually performs hash and then verifies hashed value.
        public Boolean VerifyHash(String rsaParamsXml, byte[] signedData, 
                                                                  byte[] signature)
        {
            // create new instance of RSACryptoServiceProvider
            RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider();

            // create new instance of SHA256 hash algorithm to compute hash
            SHA256Managed hash = new SHA256Managed();

            // a byte array to store hash value
            byte[] hashedData;

            // import  public key params into instance of RSACryptoServiceProvider
            rsaCSP.FromXmlString(rsaParamsXml);


            // compute hash with algorithm specified as here we have SHA256
            hashedData = hash.ComputeHash(signedData);

            // Sign Data using public key and  OID is simple name of the algorithm 
            // for which to get the object identifier (OID)
            return rsaCSP.VerifyHash(hashedData, CryptoConfig.MapNameToOID("SHA256"), 
                                                                        signature);
        }
    }
}


and the code behind the designer, Form1.Designer.cs. Sorry for small font, designer generated code tends to spill over the column of my blog. There's nothing much to see here though.

namespace WindowsFormsApp1
{
    partial class Form1
    {
        /// 
        /// Required designer variable.
        /// 
        private System.ComponentModel.IContainer components = null;


        /// 
        /// Clean up any resources being used.
        /// 
        /// true if managed resources should be disposed; 
        /// otherwise, false.
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// 
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// 
        private void InitializeComponent()
        {
            this.grpServer = new System.Windows.Forms.GroupBox();
            this.txtNonEncryptedSignatureBlock = new System.Windows.Forms.TextBox();
            this.btn2_GenerateSignatureBlock = new System.Windows.Forms.Button();
            this.txtPlainText = new System.Windows.Forms.TextBox();
            this.lblEnterPlaintextForSigning = new System.Windows.Forms.Label();
            this.lblTitle = new System.Windows.Forms.Label();
            this.grpClientSide = new System.Windows.Forms.GroupBox();
            this.txtPlainTextReceiver = new System.Windows.Forms.TextBox();
            this.btn3b_VerifySignature = new System.Windows.Forms.Button();
            this.btnExit = new System.Windows.Forms.Button();
            this.grpPublicKey = new System.Windows.Forms.GroupBox();
            this.txtPublicKey = new System.Windows.Forms.TextBox();
            this.lblIOriginalAuthor = new System.Windows.Forms.Label();
            this.linkLabel1 = new System.Windows.Forms.LinkLabel();
            this.grpServer.SuspendLayout();
            this.grpClientSide.SuspendLayout();
            this.grpPublicKey.SuspendLayout();
            this.SuspendLayout();
            // 
            // grpServer
            // 
            this.grpServer.BackColor = System.Drawing.SystemColors.Control;
            this.grpServer.Controls.Add(this.txtNonEncryptedSignatureBlock);
            this.grpServer.Controls.Add(this.btn2_GenerateSignatureBlock);
            this.grpServer.Controls.Add(this.txtPlainText);
            this.grpServer.Controls.Add(this.lblEnterPlaintextForSigning);
            this.grpServer.ForeColor = System.Drawing.SystemColors.WindowText;
            this.grpServer.Location = new System.Drawing.Point(12, 39);
            this.grpServer.Name = "grpServer";
            this.grpServer.Size = new System.Drawing.Size(826, 184);
            this.grpServer.TabIndex = 9;
            this.grpServer.TabStop = false;
            this.grpServer.Text = "ServerSide (Bob)";
            // 
            // txtNonEncryptedSignatureBlock
            // 
            this.txtNonEncryptedSignatureBlock.BackColor = System.Drawing.SystemColors.Control;
            this.txtNonEncryptedSignatureBlock.ForeColor = System.Drawing.SystemColors.WindowText;
            this.txtNonEncryptedSignatureBlock.Location = new System.Drawing.Point(192, 130);
            this.txtNonEncryptedSignatureBlock.Multiline = true;
            this.txtNonEncryptedSignatureBlock.Name = "txtNonEncryptedSignatureBlock";
            this.txtNonEncryptedSignatureBlock.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.txtNonEncryptedSignatureBlock.Size = new System.Drawing.Size(628, 48);
            this.txtNonEncryptedSignatureBlock.TabIndex = 15;
            // 
            // btn2_GenerateSignatureBlock
            // 
            this.btn2_GenerateSignatureBlock.BackColor = System.Drawing.SystemColors.Control;
            this.btn2_GenerateSignatureBlock.ForeColor = System.Drawing.SystemColors.WindowText;
            this.btn2_GenerateSignatureBlock.Location = new System.Drawing.Point(11, 130);
            this.btn2_GenerateSignatureBlock.Name = "btn2_GenerateSignatureBlock";
            this.btn2_GenerateSignatureBlock.Size = new System.Drawing.Size(160, 48);
            this.btn2_GenerateSignatureBlock.TabIndex = 10;
            this.btn2_GenerateSignatureBlock.Text = "Generate &Signature Block using Sender\'s (Bob) Private Key";
            this.btn2_GenerateSignatureBlock.UseVisualStyleBackColor = false;
            this.btn2_GenerateSignatureBlock.Click += new System.EventHandler(this.btn2_GenerateSignatureBlock_Click);
            // 
            // txtPlainText
            // 
            this.txtPlainText.BackColor = System.Drawing.SystemColors.Control;
            this.txtPlainText.ForeColor = System.Drawing.SystemColors.WindowText;
            this.txtPlainText.Location = new System.Drawing.Point(192, 20);
            this.txtPlainText.Multiline = true;
            this.txtPlainText.Name = "txtPlainText";
            this.txtPlainText.Size = new System.Drawing.Size(628, 104);
            this.txtPlainText.TabIndex = 8;
            this.txtPlainText.Text = "Hello";
            // 
            // lblEnterPlaintextForSigning
            // 
            this.lblEnterPlaintextForSigning.BackColor = System.Drawing.SystemColors.Control;
            this.lblEnterPlaintextForSigning.ForeColor = System.Drawing.SystemColors.WindowText;
            this.lblEnterPlaintextForSigning.Location = new System.Drawing.Point(8, 24);
            this.lblEnterPlaintextForSigning.Name = "lblEnterPlaintextForSigning";
            this.lblEnterPlaintextForSigning.Size = new System.Drawing.Size(168, 32);
            this.lblEnterPlaintextForSigning.TabIndex = 7;
            this.lblEnterPlaintextForSigning.Text = "Enter Plaintext for signing :";
            // 
            // lblTitle
            // 
            this.lblTitle.BackColor = System.Drawing.SystemColors.Control;
            this.lblTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lblTitle.ForeColor = System.Drawing.SystemColors.WindowText;
            this.lblTitle.Location = new System.Drawing.Point(286, 4);
            this.lblTitle.Name = "lblTitle";
            this.lblTitle.Size = new System.Drawing.Size(176, 32);
            this.lblTitle.TabIndex = 8;
            this.lblTitle.Text = "Digital Signatures";
            this.lblTitle.TextAlign = System.Drawing.ContentAlignment.TopCenter;
            // 
            // grpClientSide
            // 
            this.grpClientSide.BackColor = System.Drawing.SystemColors.Control;
            this.grpClientSide.Controls.Add(this.txtPlainTextReceiver);
            this.grpClientSide.Controls.Add(this.btn3b_VerifySignature);
            this.grpClientSide.ForeColor = System.Drawing.SystemColors.WindowText;
            this.grpClientSide.Location = new System.Drawing.Point(12, 424);
            this.grpClientSide.Name = "grpClientSide";
            this.grpClientSide.Size = new System.Drawing.Size(826, 119);
            this.grpClientSide.TabIndex = 11;
            this.grpClientSide.TabStop = false;
            this.grpClientSide.Text = "ClientSide (Alice)";
            // 
            // txtPlainTextReceiver
            // 
            this.txtPlainTextReceiver.BackColor = System.Drawing.SystemColors.Control;
            this.txtPlainTextReceiver.ForeColor = System.Drawing.SystemColors.WindowText;
            this.txtPlainTextReceiver.Location = new System.Drawing.Point(167, 20);
            this.txtPlainTextReceiver.Multiline = true;
            this.txtPlainTextReceiver.Name = "txtPlainTextReceiver";
            this.txtPlainTextReceiver.ReadOnly = true;
            this.txtPlainTextReceiver.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.txtPlainTextReceiver.Size = new System.Drawing.Size(643, 93);
            this.txtPlainTextReceiver.TabIndex = 13;
            // 
            // btn3b_VerifySignature
            // 
            this.btn3b_VerifySignature.BackColor = System.Drawing.SystemColors.Control;
            this.btn3b_VerifySignature.ForeColor = System.Drawing.SystemColors.WindowText;
            this.btn3b_VerifySignature.Location = new System.Drawing.Point(6, 20);
            this.btn3b_VerifySignature.Name = "btn3b_VerifySignature";
            this.btn3b_VerifySignature.Size = new System.Drawing.Size(155, 61);
            this.btn3b_VerifySignature.TabIndex = 0;
            this.btn3b_VerifySignature.Text = "Verify Signature block using sender\'s (Bob) Public Key";
            this.btn3b_VerifySignature.UseVisualStyleBackColor = false;
            this.btn3b_VerifySignature.Click += new System.EventHandler(this.btn3b_VerifySignature_Click);
            // 
            // btnExit
            // 
            this.btnExit.BackColor = System.Drawing.SystemColors.Control;
            this.btnExit.ForeColor = System.Drawing.SystemColors.WindowText;
            this.btnExit.Location = new System.Drawing.Point(696, 549);
            this.btnExit.Name = "btnExit";
            this.btnExit.Size = new System.Drawing.Size(136, 24);
            this.btnExit.TabIndex = 10;
            this.btnExit.Text = "E&xit";
            this.btnExit.UseVisualStyleBackColor = false;
            this.btnExit.Click += new System.EventHandler(this.btnExit_Click);
            // 
            // grpPublicKey
            // 
            this.grpPublicKey.BackColor = System.Drawing.SystemColors.Control;
            this.grpPublicKey.Controls.Add(this.txtPublicKey);
            this.grpPublicKey.ForeColor = System.Drawing.SystemColors.WindowText;
            this.grpPublicKey.Location = new System.Drawing.Point(12, 243);
            this.grpPublicKey.Name = "grpPublicKey";
            this.grpPublicKey.Size = new System.Drawing.Size(826, 175);
            this.grpPublicKey.TabIndex = 12;
            this.grpPublicKey.TabStop = false;
            this.grpPublicKey.Text = "Public Key";
            // 
            // txtPublicKey
            // 
            this.txtPublicKey.BackColor = System.Drawing.SystemColors.Control;
            this.txtPublicKey.ForeColor = System.Drawing.SystemColors.WindowText;
            this.txtPublicKey.Location = new System.Drawing.Point(11, 20);
            this.txtPublicKey.Multiline = true;
            this.txtPublicKey.Name = "txtPublicKey";
            this.txtPublicKey.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.txtPublicKey.Size = new System.Drawing.Size(809, 149);
            this.txtPublicKey.TabIndex = 13;
            // 
            // lblIOriginalAuthor
            // 
            this.lblIOriginalAuthor.AutoSize = true;
            this.lblIOriginalAuthor.Location = new System.Drawing.Point(13, 559);
            this.lblIOriginalAuthor.Name = "lblIOriginalAuthor";
            this.lblIOriginalAuthor.Size = new System.Drawing.Size(165, 13);
            this.lblIOriginalAuthor.TabIndex = 13;
            this.lblIOriginalAuthor.Text = "Based on code by Adnan Samuel";
            // 
            // linkLabel1
            // 
            this.linkLabel1.AutoSize = true;
            this.linkLabel1.Location = new System.Drawing.Point(184, 559);
            this.linkLabel1.Name = "linkLabel1";
            this.linkLabel1.Size = new System.Drawing.Size(248, 13);
            this.linkLabel1.TabIndex = 14;
            this.linkLabel1.TabStop = true;
            this.linkLabel1.Text = "Code Project - Implementing Digital Signing in .NET";
            this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(857, 583);
            this.Controls.Add(this.linkLabel1);
            this.Controls.Add(this.lblIOriginalAuthor);
            this.Controls.Add(this.grpServer);
            this.Controls.Add(this.lblTitle);
            this.Controls.Add(this.grpClientSide);
            this.Controls.Add(this.btnExit);
            this.Controls.Add(this.grpPublicKey);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.grpServer.ResumeLayout(false);
            this.grpServer.PerformLayout();
            this.grpClientSide.ResumeLayout(false);
            this.grpClientSide.PerformLayout();
            this.grpPublicKey.ResumeLayout(false);
            this.grpPublicKey.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        internal System.Windows.Forms.GroupBox grpServer;
        internal System.Windows.Forms.TextBox txtNonEncryptedSignatureBlock;
        internal System.Windows.Forms.Button btn2_GenerateSignatureBlock;
        internal System.Windows.Forms.TextBox txtPlainText;
        internal System.Windows.Forms.Label lblEnterPlaintextForSigning;
        internal System.Windows.Forms.Label lblTitle;
        internal System.Windows.Forms.GroupBox grpClientSide;
        internal System.Windows.Forms.TextBox txtPlainTextReceiver;
        internal System.Windows.Forms.Button btn3b_VerifySignature;
        internal System.Windows.Forms.Button btnExit;
        internal System.Windows.Forms.GroupBox grpPublicKey;
        internal System.Windows.Forms.TextBox txtPublicKey;
        private System.Windows.Forms.Label lblIOriginalAuthor;
        private System.Windows.Forms.LinkLabel linkLabel1;
    }
}



No comments:

Post a Comment