Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implement Diffie-Hellman in C#

0.00/5 (No votes)
5 Dec 2017 2  
Lets looks at the Diffie-Hellman key exchange to securely send private information over a public channel.

Introduction

Lets create a cryptographic class which has two methods - encrypt and decrypt. These two methods will allow you to exchange your public key with the other party and decrypt the secret messages with your private key. The secret message will be encrypted using standard AES encryption.

Table of Contents

  1. Glossary
  2. Visualization of Diffie-Hellman
  3. Using the code
    • Step 01 - Create a Class Library
    • Step 02 - Add fields
    • Step 03 - Add a constructor
    • Step 04 - Expose Public Key and IV
    • Step 05 - Create an Encrypt method
    • Step 06 - Create a Decrypt method
    • Step 07 - Dispose unmanaged resources
    • Step 08 - Create a test class
  4. Final Words

Glossary

AES (Advanced Encryption Standard) - Originally called "Rijndael", is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology (NIST) in 2001. The algorithm described by AES is a symmetric-key algorithm, meaning the same key is used for both encrypting and decrypting the data.

CNG (Cryptography Next Generation) - A cryptographic development platform that allows developers to create, update, and use custom cryptography algorithms in cryptography-related applications.

Diffie-Hellman - A method of securely exchanging cryptographic keys over a public channel and was one of the first public-key protocols as originally conceptualized by Ralph Merkle and named after Whitfield Diffie and Martin Hellman.

IV (Initialization Vector) - An arbitrary number that can be used along with a secret key for data encryption. This number, also called a nonce, is employed only one time in any session. (We will be using the other party's IV and public key to decrypt the secret message.)

Visualization of Diffie-Hellman

Using the Code

Step 01 - Create a Class Library

Open Visual Studio and go to "File > New > Project" and select "Class Library".

Give your project a name (e.g. SecureKeyExchange) and click "OK".

After you project is created, rename the "Class1.cs" file to "DiffieHellman.cs".

 

Step 02 - Add fields

We need to add three fields; one that contains a reference to the Aes-class, the second field to store a reference to the ECDiffieHellmanCng-class and the last fields to store our public key.

The Aes-reference will be used to encrypt/decrypt the messages. The ECDiffieHellmanCng-reference will be used to create a derived key between the two parties.

Add the following three fields to your class:

private Aes aes = null;
private ECDiffieHellmanCng diffieHellman = null;

private readonly byte[] publicKey;

 

Step 03 - Add a constructor

Our constructor should now initialize these fields:

public DiffieHellman()
{
    this.aes = new AesCryptoServiceProvider();

    this.diffieHellman = new ECDiffieHellmanCng
    {
        KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash,
        HashAlgorithm = CngAlgorithm.Sha256
    };

    // This is the public key we will send to the other party
    this.publicKey = this.diffieHellman.PublicKey.ToByteArray();
}

Once the ECDiffieHellmanCng instance has been initialized, we can set our publicKey field to the PublicKey of the ECDiffieHellmanCng instance. We are going to send this public key along with the secret message to the other party.

Step 04 - Expose Public Key and IV

Lets expose both our public key and IV through properties. Add the following properties respectively:

public byte[] PublicKey
{
    get
    {
        return this.publicKey;
    }
}

public byte[] IV
{
    get
    {
        return this.aes.IV;
    }
}

These properties will be sent to the other party to decrypt the secret message using their own private key.

Step 05 - Create an Encrypt method

We are going to create a method that takes the public key of the other party as well as the secret message to encrypt.

We will use the other party's public key to generate a derived key (see "Common secret" in the paint analogy above) which will be used to encrypt the message. Add the Encrypt function:

public byte[] Encrypt(byte[] publicKey, string secretMessage)
{
    byte[] encryptedMessage;
    var key = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
    var derivedKey = this.diffieHellman.DeriveKeyMaterial(key); // "Common secret"

    this.aes.Key = derivedKey;

    using (var cipherText = new MemoryStream())
    {
        using (var encryptor = this.aes.CreateEncryptor())
        {
            using (var cryptoStream = new CryptoStream(cipherText, encryptor, CryptoStreamMode.Write))
            {
                byte[] ciphertextMessage = Encoding.UTF8.GetBytes(secretMessage);
                cryptoStream.Write(ciphertextMessage, 0, ciphertextMessage.Length);
            }
        }

        encryptedMessage = cipherText.ToArray();
    }

    return encryptedMessage;
}

Now our message is encrypted and we can send it to the other party. But first need to add a function to decrypt this secret message.

Step 06 - Create a Decrypt method

Our Decrypt function will take in 3 parameters: The public key and IV of the other party as well as the secret message. Lets add the function:

public string Decrypt(byte[] publicKey, byte[] encryptedMessage, byte[] iv)
{
    string decryptedMessage;
    var key = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
    var derivedKey = this.diffieHellman.DeriveKeyMaterial(key);

    this.aes.Key = derivedKey;
    this.aes.IV = iv;

    using (var plainText = new MemoryStream())
    {
        using (var decryptor = this.aes.CreateDecryptor())
        {
            using (var cryptoStream = new CryptoStream(plainText, decryptor, CryptoStreamMode.Write))
            {
                cryptoStream.Write(encryptedMessage, 0, encryptedMessage.Length);
            }
        }

        decryptedMessage = Encoding.UTF8.GetString(plainText.ToArray());
    }

    return decryptedMessage;
}

We can now decrypt the secret message.

Step 07 - Dispose unmanaged resources

The last piece of code we still need to add is to implement the IDisposable interface to clean up our unmanaged resources.

Lets add the interface to our DiffieHellman class:

public class DiffieHellman : IDisposable

And add the implementation:

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        if (this.aes != null)
            this.aes.Dispose();

        if (this.diffieHellman != null)
            this.diffieHellman.Dispose();
    }
}

Our class is complete. Now we need to create a test class to test our functionality.

Step 08 - Create a test class

Right click on the solution and select "Add > New Project > Unit Test Project" and give your project a name (e.g "SecureKeyExchange.Tests"). Rename your "UnitTest1.cs" to "DiffieHellmanTests.cs" and add a reference to the "SecureKeyExchange" project. To do this, right-click on the "References" node under the test project and select "Add Reference > Projects > Select the project > OK".

Add the following test method to our test class:

[TestMethod]
public void Encrypt_Decrypt()
{
    string text = "Hello World!";

    using (var bob = new DiffieHellman())
    {
        using (var alice = new DiffieHellman())
        {
            // Bob uses Alice's public key to encrypt his message.
            byte[] secretMessage = bob.Encrypt(alice.PublicKey, text);

            // Alice uses Bob's public key and IV to decrypt the secret message.
            string decryptedMessage = alice.Decrypt(bob.PublicKey, secretMessage, bob.IV);
        }
    }
}

We can now add a breakpoint and debug our test (press Ctrl+R,Ctrl+A) to see the results:

Final Words

The Diffie-Hellman key exchange allows us to send secret information over a public channel. In my next post, we will look at how to implement this into a real world scenario.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here