Cryptography in .NET : Digital Signatures

I have previously written a number of articles on Cryptography in .NET, like the following :

Part 1 – Advanced Encryption Standard (AES)

Part 2 – RSA

Part 3 – Random Numbers and Hashes

Part 4 – Hybrid Encryption Protocols

Block Encrypter .NET Library for secure AES Encryption

In this article I will show you how to create and use Digital Signatures in .NET.

A digital signature is a mathematical scheme that demonstrates the authenticity of a message or document. A valid digital signature gives the recipient reason to believe that the message was created by a known sender, such that the sender cannot deny having sent the message (authentication and non-repudiation) and that the message was not altered in transit (integrity). Digital signatures are commonly used for software distribution, financial transactions, and in other cases where it is important to detect forgery or tampering.

Digital signatures are often used to implement a digital analog to hand written signatures. In broader terms this refers to any electronic data that carries the intent of a signature. Digital signatures employ a type of asymmetric cryptography. For messages sent through a non-secure channel, a properly implemented digital signature gives the receiver reason to believe the message was sent by the claimed sender. Digital signatures are equivalent to traditional handwritten signatures in many respects, but properly implemented digital signatures are more difficult to forge than the handwritten type. Digital signature schemes, in the sense used here, are cryptographic based, and must be implemented properly to be effective. Digital signatures can also provide non-repudiation, meaning that the signer cannot successfully claim they did not sign a message, while also claiming their private key remains secret.

Example Digital Signature Flow

Example Digital Signature Flow

A digital signature scheme consists of three algorithms

  • A key generation algorithm that generates a private and public key, such as RSA.
  • A signing algorithm that, given a message and a private key, produces a signature.
  • A signature verifying algorithm that, given a message, public key and a signature, either accepts or rejects the message’s claim to authenticity.

Two main properties are required. First, the authenticity of a signature generated from a fixed message and fixed private key can be verified by using the corresponding public key. Secondly, it should be computationally infeasible to generate a valid signature for a party without knowing that party’s private key. A digital signature is an authentication mechanism that enables the creator of the message to attach a code that act as a signature. It is formed by taking the hash of message and encrypting the message with creator’s private key.

public void AssignNewKey()
{
    using (var rsa = new RSACryptoServiceProvider(2048))
    {
        rsa.PersistKeyInCsp = false;
        publicKey = rsa.ExportParameters(false);
        privateKey = rsa.ExportParameters(true);
    }
}

For the digital signature example project there is a class called DigialSignature. The first method in this class is AssignNewKey. This method will generate a public and private key pair to be used for creating and verifying the digital signature. Next there is the SignData method. This is the method that will create a digital signature. Let’s say you want to sign some data that represents a document, although what you are signing doesn’t matter as it is represented as a byte array. You don’t sign the actual data itself; you sign a hash of the data.

public byte[] SignData(byte[]hashOfDataToSign)
{
    using (var rsa = new RSACryptoServiceProvider(2048))
    {
        rsa.PersistKeyInCsp = false;
        rsa.ImportParameters(privateKey);

        var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
        rsaFormatter.SetHashAlgorithm("SHA256");

        return rsaFormatter.CreateSignature(hashOfDataToSign);
    }
}

The SignData method takes a byte array which is the hash of the data you wish to sign. The first thing that happens in this method is an instance of the RSACryptoServiceProvider class is created and the already created private key (from the AssignNewKey method) is loaded into that instance. Then an instance of RSAPKCS1SignatureFormatter is created and the instance of RSACryptoServiceProvider is passed in.

Next the hash algorithm has to be set on the RSAPKCS1SignatureFormatter  instance. The algorithm set here has to match the hash algorithm you used to hash your data before creating the digital signature. For example, if you hash your data with SHA1 then the string passed into SetHashAlgorithm needs to be “SHA1”, if you used SHA256 (which this example program does), then you need to pass “SHA256” into SetHashAlgorithm.

Note: If you have a mismatch between the hash algorithm used to hash your data and the algorithm set with the SetHashAlgorithm method, a CryptographicException will be thrown with the message Bad Hash

Once this has been done, the last thing to do is call CreateSignature on the RSAPKCS1SignatureFormatter instance. This will return a byte array containing your digital signature.

The next method in the DigitalSignature class is the VerifySignature method. This method is used to verify that a digital signature is valid for a particular hash of the data you want to verify.

public bool VerifySignature(byte[]hashOfDataToSign, byte[] signature)
{
    using (var rsa = new RSACryptoServiceProvider(2048))
    {
        rsa.ImportParameters(publicKey);

        var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
        rsaDeformatter.SetHashAlgorithm("SHA256");

        return rsaDeformatter.VerifySignature(hashOfDataToSign, signature);
    }
}

The VerifySignature method takes 2 parameters, a byte array containing a hash of the data that the signature was originally created for, and a byte array of the digital signature itself. First an instance of the RSACryptoServiceProvider class is created. Then the public key that was generated with the AssignNewKey method is imported into the rsa instance.

Then an instance of the RSAPKCS1SignatureDeformatter is created and the hash algorithm is set to “SHA256”. Then to verify the signature you called VeryifySignature on the RSAPKCS1SignatureDeformatter instance but providing the hashed of the data that was signed and the actual signature itself. If the signature is valid, true is returned, and false if the signature is not valid.

Usage of this class is demonstrated in the following code block.

class Program
{
    static void Main(string[] args)
    {
        var document = Encoding.UTF8.GetBytes("Document to Sign");
        byte[] hashedDocument;

        using (var sha256 = SHA256.Create())
        {
            hashedDocument = sha256.ComputeHash(document);
        }

        var digitalSignature = new DigitalSignature();
        digitalSignature.AssignNewKey();

        var signature = digitalSignature.SignData(hashedDocument);
        var verified = digitalSignature.VerifySignature(hashedDocument, signature);

        Console.WriteLine("Digital Signature Demonstration in .NET");
        Console.WriteLine("---------------------------------------");
        Console.WriteLine();
        Console.WriteLine();
        Console.WriteLine("   Original Text = " + System.Text.Encoding.Default.GetString(document));
        Console.WriteLine();
        Console.WriteLine("   Digital Signature = " + Convert.ToBase64String(signature));
        Console.WriteLine();

        if (verified)
        {
            Console.WriteLine("The digital signature has been correctly verified.");
        }
        else
        {
            Console.WriteLine("The digital signature has NOT been correctly verified.");
        }

        Console.ReadLine();
    }
}

When this program is executed and a valid digital signature is verified you will see the following output.

Digital signature valid verification

Digital signature valid verification

To prove that the VerifySignature method will return false for an invalid signature, you can deliberately tamper with the signature in the debugger by changing one of the values in the debugger, as shown in the following screenshot.

Digital signature tampered with in the debugger

Digital signature tampered with in the debugger

When the program is allowed to carry on executing with the tampered digital signature, you will see the following output.

Invalid digital signature

Invalid digital signature

The complete Digital Signature class source code is a follows :

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace CryptographyInDotNet
{
    public sealed class DigitalSignature
    {
        private RSAParameters publicKey;
        private RSAParameters privateKey;

        public void AssignNewKey()
        {
            using (var rsa = new RSACryptoServiceProvider(2048))
            {                
                rsa.PersistKeyInCsp = false;               
                publicKey = rsa.ExportParameters(false);
                privateKey = rsa.ExportParameters(true);                
            }
        }

        public byte[] SignData(byte[] hashOfDataToSign)
        {
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.PersistKeyInCsp = false;
                rsa.ImportParameters(privateKey);
                
                var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);                
                rsaFormatter.SetHashAlgorithm("SHA256");

                return rsaFormatter.CreateSignature(hashOfDataToSign);
            }
        }

        public bool VerifySignature(byte[] hashOfDataToSign, byte[] signature)
        {
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.ImportParameters(publicKey);

                var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
                rsaDeformatter.SetHashAlgorithm("SHA256");

                return rsaDeformatter.VerifySignature(hashOfDataToSign, signature);
            }
        }   
    }
}

10 thoughts on “Cryptography in .NET : Digital Signatures

  1. Pingback: Cryptography in .NET : Hybrid Encryption Protocols | Stephen Haunts { Coding in the Trenches }

  2. Pingback: An Introduction To Trouble-Free Secrets Of idm full version free - IDM Terbaru

  3. Nn

    How can we make fixed private key and a fixed public key to be used every time rather than generating new keys each time we signed a text ??

    Thanks

    Reply
  4. Mark

    Thank you for the post, it is very useful..
    I have a question , If I want to use OpennSSL how to call the private and public key?

    Reply
    1. Stephen Haunts Post author

      You can use Open SSL to generate a self signed PFX certificate that contains the public and private key which can then be loaded into your certificate container. Then you can read those certs and use with RSA and digital signatures in .NET. Here is some sample code for using certs with RSA in .NET. I should really turn this into another blog article.

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Security.Cryptography;
      using System.Security.Cryptography.X509Certificates;
      using System.Text;
      using System.Threading.Tasks;

      namespace TestRSAwithCerts
      {
      class Program
      {
      private static RSACryptoServiceProvider rsa_public;
      private static RSACryptoServiceProvider rsa_private;

      static void Main(string[] args)
      {
      X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
      store.Open(OpenFlags.OpenExistingOnly);
      X509Certificate2Collection certificates = store.Certificates;

      int a = certificates.Count;

      rsa_public = (RSACryptoServiceProvider)certificates[1].PublicKey.Key;
      rsa_private = (RSACryptoServiceProvider)certificates[1].PrivateKey;

      string ciphertext = EncryptData(“Mary had a little lamb”);
      string plaintext = DecryptData(ciphertext);
      }

      public static string EncryptData(string data2Encrypt)
      {
      byte[] cipherbytes;

      byte[] plainbytes = Encoding.UTF8.GetBytes(data2Encrypt);
      cipherbytes = rsa_public.Encrypt(plainbytes, false);

      return Convert.ToBase64String(cipherbytes);
      }

      public static string DecryptData(string data2Decrypt)
      {
      byte[] plain;

      byte[] encodedCipherText = Convert.FromBase64String(data2Decrypt);
      plain = rsa_private.Decrypt(encodedCipherText, false);

      return Encoding.UTF8.GetString(plain);
      }
      }
      }

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s