相关文章推荐
要出家的煎饼果子  ·  CREATE INDEX ...·  2 周前    · 
阳刚的小狗  ·  Java8 Stream ...·  2 周前    · 
读研的人字拖  ·  Java 读取 .properties ...·  1 周前    · 
眼睛小的木耳  ·  spring boot test ...·  1 周前    · 
大方的手电筒  ·  demo.c 和 demo.cc 和 ...·  4 月前    · 
文武双全的足球  ·  Deprecated features ...·  9 月前    · 

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

  • Glossary
  • Visualization of Diffie-Hellman
  • 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
  • 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 (I nitialization 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;
        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
            return this.publicKey;
    public byte[] IV
            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.

    I am a software developer that loves creating new things, solving solutions and reading about the latest technologies. I am currently working on MVC projects, creating .NET APIs, deploying applications, writing SQL queries, debugging Windows Services, developing Xamarin mobile applications and working with a bunch of great people. Outside of work I am a brother and father. Hi. Could you please post code for the generic methods of Encrypt and Decrypt you spoke about?
    Thanks
    Sign In·View Thread  Thanks for the code sample, it helps "de-encrypting" the concepts.
    I still have few question if I may. In your example, what would be reffered as the "Common Paint" (color yellow in the diagram)? Would it be the IV ? HEnce, what would be refered as the "mixed color" (in the diagram again)?
    Kind regards,
    Benoit
    Sign In·View Thread  If both key and IV of the other party (that information that is going to be shared publically via an insecure channel) is used to decrypt the secret message, a man in the middle who spies those public key information, could also decrypt the information; so this would not make sense at all, right? Shouldn´t it be the other way around, that encryption will be done using the public key and IV information, and decryption will be done using any derived key material (the information that did not go public, which should be same for both parties)?
    Sign In·View Thread  Console.WriteLine("DecryptedMessage: {0}", decryptedMessage);
    and then clicking the test name and "output" in the Test Explorer displays that standard output properly in the Test Output window.
    Looking forward to your next article.
    Sign In·View Thread 
    Thanks for your reply asiwel.
    I was hoping the readers would know how to use the "Ctrl+." shortcut ("Quick Actions and Refactorings ...") VS-feature to add those required dependencies. I'll put the tip in my next follow up article in this series! Smile | :)
    Also, the reason it might complete without hitting the breakpoints in the test could be you are not running in Debug mode or you are not running a "Debug Test". You can run a debug test by hovering over the "Test" menu item at the top, then "Debug > All Tests" or right-clicking on the test in the test explorer and selecting "Debug Test".
    Regards
    Sign In·View Thread 
    Thanks for your quick reply. Yes, adding those dependencies was not a problem, just a tiny moment of surprise.
    And, indeed, I was not running a "Debug Test." Works fine, just as you described, when you do that!
    Encryption is not an area I know very much about. Can you send any object from one app to another using this approach? Or does it just work for text strings or streams (text, XML, JSon, etc.)? In other words, can I send (to myself in this case) a simple or a complex object without serializing it, etc?
    Sign In·View Thread 
    Glad you got it working.
    I don't believe you can send complex objects without serializing them.
    You could change the Encrypt method to a generic function:
    public byte[] Encrypt<T>(byte[] publicKey, T secretMessage)
    And use the following methods to serialize/deserialize T:
    Pierre Nortje wrote:
    I don't believe you can send complex objects without serializing them.
    You could change the Encrypt method to a generic function:
    I think that idea and the simple code you provided would work fine most of the time. For instance, you could send an encrypted dictionary or list to a receiving app that way.
    Thank you for an interesting article.
    [EDIT] In fact, I just copied your test method and used your ToByteArray and FromByteArray methods to serialize and later deserialize a small dictionary. Then after slight changes in the Encrypt and the Decrypt methods (to use byte arrays rather than strings), everything worked fine. So I sent myself an encrypted dictionary object and successfully decrypted it as a new dictionary of the same type. It would be easy, I think, to make both Encrypt and Decrypt generic along the lines you suggested:
    Pierre Nortje wrote:
    public byte[] Encrypt<T>(byte[] publicKey, T secretMessage)


    modified 8-Dec-17 22:05pm.

    Sign In·View Thread  Web01 2.8:2023-03-27:1