Code Analysis of the Communicator Class (CNG Example)

The Communicator.cs file encapsulates the encryption and decryption methods for the Cryptography Next Generation (CNG) secure communication example. It consists of only one class, which is named Communicator. This class contains the members and methods discussed in the following sections:

  • Class members

  • Class constructor

  • Dispose method

  • StoreDSKey method

  • Send_or_Receive_PublicCryptoKey method

  • SendMessage method

  • ReceiveMessage method

See Cryptography Next Generation (CNG) Secure Communication Example for an overview of the example and descriptions of the versions mentioned in this topic.

Class Members

The Communicator class contains the following private members:

  • CngKey m_DSKey

    This member is used by Communicator.StoreDSKey to store a digital signature key.

  • ECDiffieHellmanCng m_ECDH_Cng

    This member is used by the constructor to store an instance of the ECDiffieHellmanCng class. The ReceiveMessage and SendMessage methods use this member to derive key material.

  • string m_ECDH_local_publicKey_XML

    This member is used by the constructor to store an XML string representation of the local public Elliptic Curve Diffie-Hellman (ECDH) cryptographic key. Alice, Bob, and Mallory use the Send_or_Receive_PublicCryptoKey method to exchange this XML string.

  • ECDiffieHellmanPublicKey m_ECDH_remote_publicKey

    This member is used by the Send_or_Receive_PublicCryptoKey method to store a remote public ECDH cryptographic key.

The Communicator class also provides the following public member:

  • ChannelManager ChMgr

    This member is used by Alice, Bob, and Mallory to provide named pipe services.

Class Constructor

public Communicator(string mode, string ChannelName)

The constructor creates the following three objects:

  • An instance of the ECDiffieHellmanCng class with a random key pair, using a key size of 521 bits:

    m_ECDH_Cng = new ECDiffieHellmanCng(521)
    

    The resulting object is bound to the Communicator class as a private member. Each Communicator object creates a single instance of this class.

    Alice and Bob each create a single Communicator object and can each access a single m_ECDH_Cng member. Mallory creates two Communicator objects: one for use with Alice, and the other for use with Bob. Therefore, Mallory can access two m_ECDH_Cng members.

  • The ECDH public key as a private XML string:

    m_ECDH_XmlString = m_ECDH_CryptoKey.ToXmlString(ECKeyXmlFormat.Rfc4050)
    

    The ECDiffieHellmanCng.ToXmlString method serializes the public ECDH key by using the ECKeyXmlFormat.Rfc4050 format.

    In versions 2 through 5 of the example, Alice sends the resulting XML string to Bob in her Run method by using the following code statement:

    Alice.Send_or_Receive_PublicCryptoKey("send", MyColor);
    
  • A public instance of the ChannelManager class:

    ChMgr = new ChannelManager(mode, ChannelName)
    

    The constructor accepts the following two parameters:

    • mode: A string that indicates how to create the named pipe. This parameter can be either "server" or "client".

    • ChannelName: A string that provides a name to identify the new named pipe.

Dispose Method

public void Dispose()

The Communicator class inherits the IDisposable interface and provides the Dispose method to immediately free sensitive resources. This includes the m_ECDH_Cng, m_ECDH_local_publicKey_XML, and ChMgr objects.

Each object is created within a C# using statement to ensure that the object will be released immediately when it goes out of scope.

StoreDSKey Method

public void StoreDSKey(byte[] DSKeyBlob)

This method accepts a binary large object (BLOB) that is contained in a byte array. It extracts a digital signature key from the key BLOB by using the CngKey.Import(array<Byte[], CngKeyBlobFormat) method. The key is then stored by the following code:

m_DSKey = CngKey.Import(DSKeyBlob,CngKeyBlobFormat.Pkcs8PrivateBlob);

The StoreDSKey method is called by Alice, Bob, and Mallory in their Run methods, by the following versions:

  • In versions 3 through 5, Alice, Bob, and Mallory use this method to store the same, publicly transmitted key.

  • In versions 4 and 5, Alice and Bob call this method a second time, and overwrite the m_DSKey member with a privately transmitted key.

Send_or_Receive_PublicCryptoKey Method

Send_or_Receive_PublicCryptoKey(string mode, int color)

This method accepts two parameters:

  • mode: A string that indicates whether to create a pipe server to send a key, or create a pipe client to receive a key. This parameter can be either "server" or "client".

  • color: An integer that specifies the color that is used to display the key.

The Send_or_Receive_PublicCryptoKey method is similar to the Communicator ReceiveMessage and SendMessage methods, except that it sends or receives unencrypted cryptographic keys instead of encrypted messages. ReceiveMessage and SendMessage cannot be used for cryptographic keys because these methods would try to encrypt and decrypt the keys.

After the keys have been exchanged, versions 3 through 5 of the example validate the keys' digital signatures. Version 3 uses a digital signature that is transmitted over the PublicChannel named pipe. This signature is intercepted and used by Mallory to sign the substituted keys and messages that he sends to Alice and Bob. Version 3 always succeeds in its signature validation because Alice, Bob, and Mallory use the same digital signature key.

Note

Versions 4 and 5 use a private digital signature to sign keys and messages, and display security warnings. These versions are given only to Alice and Bob. Therefore, Mallory does not know that his interceptions have been detected.

SendMessage Method

public bool SendMessage(string plainTextMessage, bool fShowMsg)

This method is called by Alice, Bob, and Mallory from their Run methods. It encrypts, digitally signs, and sends messages by following these steps:

  1. Displays the plaintext message if the fShowMsg value is true.

  2. Converts the message to a Unicode byte array by using the following code:

    byte[] UnicodeText = Encoding.Unicode.GetBytes(plainTextMessage);
    
  3. Determines whether the message should be sent in plaintext. If the example is running version 1, SendMessage returns after it sends the message by using the following code:

    ChMgr.SendMessage(UnicodeText);
    
  4. Derives key material by calling the ECDiffieHellmanCng.DeriveKeyMaterial(ECDiffieHellmanPublicKey) method:

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey)
    
  5. Creates a temporary Aes object:

    Aes aes = new AesCryptoServiceProvider()
    
  6. Initializes the Aes object with the key material that was derived in step 4:

    aes.Key = aesKey;
    
  7. Creates a temporary MemoryStream object to hold an encrypted string.

  8. Creates a temporary CryptoStream object and uses it to encrypt the message and write it to the MemoryStream object.

  9. Saves the ciphertext and initialization vector:

    iv = aes.IV
    ciphertext = ms.ToArray();
    
  10. Signs the ciphertext as follows if the example is running version 3, 4, or 5:

    • Creates a temporary ECDsaCng.ECDsaCng(CngKey) object:

      ECDsaCng ecdsa = new ECDsaCng(m_DSKey)
      
    • Initializes the object's HashAlgorithm property as a Sha512 secure hash algorithm:

      ecdsa.HashAlgorithm = CngAlgorithm.Sha512
      
    • Creates a digital signature by signing the ciphertext:

      signature = ecdsa.SignData(ciphertext);
      
  11. Prepares the output message as follows:

    • Creates a three-byte array to hold the lengths of the message's initialization vector, ciphertext, and signature.

    • Creates four System.Collections.Generic.List<T> objects to hold the length array from the previous step, the initialization vector, the ciphertext, and the signature.

    • Concatenates the four System.Collections.Generic.List<T> objects and converts them to a single output message byte array:

      byte[] message = list1.ToArray();
      
  12. Sends the output message:

    ChMgr.SendMessage(message)
    

ReceiveMessage Method

public string ReceiveMessage()

This method is called by Alice, Bob, and Mallory from their Run methods. It receives and decrypts messages, and validates their digital signatures.

Messages are not passed as parameters to this method. Instead, the ChannelManager member of the Communicator class reads the message by using the following code:

byteBuffer = ChMgr.ReadMessage();

The ReceiveMessage method performs the following steps:

  1. Determines whether the message was sent in plaintext. If version 1 of the example is running, the message is in plaintext and decryption is not required. In this case, the message is returned after it is converted to an ASCII string by using the following code:

    AsciiMessage = Encoding.Unicode.GetString(byteBuffer);
    
  2. Breaks the message into components. In versions 2 through 5, the message will be encrypted. In this case, the message is broken into three separate byte arrays. The first array contains the initialization vector, the second array contains the ciphertext, and the third array contains the ciphertext's digital signature.

  3. Displays the initialization vector, ciphertext, and signature as ASCII text if the fVerbose flag is set.

  4. Derives key material. The private ECDiffieHellmanCng member, m_ECDH_Cng, of the Communicator class uses the ECDiffieHellmanCng.DeriveKeyMaterial method to derive shared key material, as shown by the following code:

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey);
    
  5. Creates an Aes object by instantiating a AesCryptoServiceProvider object:

    Aes aes = new AesCryptoServiceProvider()
    
  6. Initializes the Aes object with the key material and initialization vector derived from previous steps.

  7. Decrypts the message by using System.IO.MemoryStream and System.Security.Cryptography.CryptoStream objects.

  8. Displays the decrypted message.

  9. Validates the message's ciphertext in versions 3 through 5 by using the digital signature. Version 3 does not display security warnings because Alice, Bob, and Mallory use the same signature. Version 4 displays security warnings when a message's ciphertext has an invalid signature. However, only Alice and Bob are provided with version 4, so Mallory never sees any warnings. In version 5, invalidly signed cryptographic keys cause program termination before any messages are transmitted and validated.

See Also

Concepts

Cryptography Next Generation (CNG) Secure Communication Example

Communicator.cs Source Code (CNG Example)

Source Code Overview (CNG Example)

Step-by-Step Key and Message Exchange (CNG Example)

Expected Output (CNG Example)