Communicator.cs Source Code (CNG Example)
The Communicator.cs file provides the source code for the Communicator class, which is used by the Cryptography Next Generation (CNG) secure communication example. It contains the following code.
using System; // Required for the IDisposable interface
using System.Collections.Generic; // Required for the List class
using System.Text; // Required for the Encoding class
using System.IO; // Required for the MemoryStream class
using System.Diagnostics; // Required for the Debug.Assert calls
using System.Security.Cryptography; // Required for the CNG APIs
public partial class CNG_SecureCommunicationExample
{
internal sealed class Communicator : IDisposable
{
private CngKey m_DSKey;
private ECDiffieHellmanCng m_ECDH_Cng;
private string m_ECDH_local_publicKey_XML;
private ECDiffieHellmanPublicKey m_ECDH_remote_publicKey;
public ChannelManager ChMgr;
public Communicator(string mode, string ChannelName)
{
m_ECDH_Cng = new ECDiffieHellmanCng(521);
m_ECDH_local_publicKey_XML = m_ECDH_Cng.ToXmlString(ECKeyXmlFormat.Rfc4050);
ChMgr = new ChannelManager(mode, ChannelName);
}
public void Dispose()
{
if (m_ECDH_Cng != null)
(m_ECDH_Cng as IDisposable).Dispose();
if (m_ECDH_remote_publicKey != null)
(m_ECDH_remote_publicKey as IDisposable).Dispose();
if (ChMgr != null)
(ChMgr as IDisposable).Dispose();
}
public void StoreDSKey(byte[] DSKeyBlob)
{
Debug.Assert(DSKeyBlob != null, "DSKeyBlob != null");
m_DSKey = CngKey.Import(DSKeyBlob,
CngKeyBlobFormat.Pkcs8PrivateBlob);
}
public bool Send_or_Receive_PublicCryptoKey(string mode, int color)
{
string xmlECDH;
byte[] signature = null; // Sign the ciphertext with the public signature key
if ("send" == mode)
{
xmlECDH = m_ECDH_local_publicKey_XML;
Byte[] message = Encoding.ASCII.GetBytes(m_ECDH_local_publicKey_XML);
if(3 <= Version)
{
using (ECDsaCng ecdsa = new ECDsaCng(m_DSKey)) // EDCsaCng(CngKey)
{
ecdsa.HashAlgorithm = CngAlgorithm.Sha512;
signature = ecdsa.SignData(message); // Create a digital signature for the encrypted message
}
string messageLength = message.Length.ToString();
byte[] bLengths = new byte[1]; // Create a byte array to hold the three lengths
bLengths[0] = (byte)messageLength.Length;
List<byte> list1 = new List<byte>(bLengths); // Turn all four arrays into List objects
List<byte> list2 = new List<byte>(Encoding.ASCII.GetBytes(messageLength));
List<byte> list3 = new List<byte>(message);
List<byte> list4 = new List<byte>(signature);
list1.AddRange(list2); // Concatenate the four arrays
list1.AddRange(list3);
list1.AddRange(list4);
message = list1.ToArray(); // Create the master output array
}
ChMgr.SendMessage(message);
}
else // Mode = receive
{
byte[] input = ChMgr.ReadMessage();
if (0 == input.Length) // Application control for Version 5 to keep Mallory from
return false; // hanging when Alice discovers a bad signature
if(3 <= Version)
{
List<byte> list = new List<byte>(input);
int iLength = (int)input[0]; // The first byte specifies the count of bytes
// required to hold the message length. These
// message length bytes immediately follow the first byte.
Byte[] message_Length = new Byte[iLength]; // Allocate a buffer large enough for the message
list.CopyTo(1, message_Length, 0, iLength); // Copy the message into the buffer
string s_message_Length =
Encoding.ASCII.GetString(message_Length);
int count = Convert.ToInt32(s_message_Length);
Byte[] TheMessage = new Byte[count];
list.CopyTo(4, TheMessage, 0, count); // Retrieve the message starting from the byte 4 (0-based)
Byte[] TheSignature = new Byte[input.Length - 4 - count];
list.CopyTo(4 + count, TheSignature, 0, // Retrieve the signature from the input
input.Length - 4 - count);
using (ECDsaCng ecdsa = new ECDsaCng(m_DSKey)) // Verify the ciphertext by hashing the ciphertext,
{ // and comparing the result with the signature
ecdsa.HashAlgorithm = CngAlgorithm.Sha512;
if (!ecdsa.VerifyData(TheMessage, TheSignature))
{
if ("Alice Green" == MyName || "Bob White" == MyName)
if (4 <= Version)
Display(" ======== SECURITY ERROR!!===========\n" +
"Cryptographic Key: Failure verifying digital signature.\n" +
(5 == Version ? "Contact your security administrator.\n" +
"TERMINATING SESSION\n\n" : "" )+ "\n\n",0) ;
if (5 == Version)
{
System.Threading.Thread.Sleep(2000);
return false;
}
}
}
xmlECDH = Encoding.ASCII.GetString(TheMessage);
}
else xmlECDH = Encoding.ASCII.GetString(input); // Cryptographic key is not signed.
m_ECDH_remote_publicKey = ECDiffieHellmanCngPublicKey.FromXmlString(xmlECDH);
}
if (fVerbose)
{
Display("Here it is: an ECDH public KeyBlob\n" +
"encoded within an XML string:\n\n");
Display(xmlECDH + "\n\n", color);
}
return true;
}
/*
public void Store_bArray_ECDH_remotePublicKey(Byte[] bArray)
{
// m_ECDH_remote_publicKey = (ECDiffieHellmanPublicKey)enc.GetBytes(s);
// ECDiffieHellmanPublicKey Alice_ECDH_publicKey = Alice.m_ECDH_local_publicKey;
// byte[] bArray = enc.GetBytes(s);
// m_ECDH_remote_publicKey = new ECDiffieHellmanPublicKey(bArray); // Abstract Base Class: can't instantiate
// m_ECDH_remote_publicKey = ECDiffieHellmanCngPublicKey.FromByteArray(bArray,CngKeyBlobFormat.GenericPublicBlob);
m_ECDH_remote_publicKey = ECDiffieHellmanCngPublicKey.FromByteArray(bArray,CngKeyBlobFormat.EccPublicBlob);
}
*/
//-------------------------------------------------------------------------------------------------
private byte[] iv;
private byte[] ciphertext;
private byte[] signature;
public bool SendMessage(string plainTextMessage, bool fShowMsg)
{
Debug.Assert(plainTextMessage != null, "plainTextMessage != null");
if (fShowMsg)
Display(":> " + plainTextMessage + "\n", MyColor);
if (1 != Version)
if (fVerbose)
Display(sep1,1);
byte[] UnicodeText = Encoding.Unicode.GetBytes(plainTextMessage);// Convert the message string into a Unicode byte array.
if (1 == Version)
return ChMgr.SendMessage(UnicodeText); // Send the plaintext (unencrypted, unsigned) message.
// Generate a shared AES session key with the recipiant.
// This key will be used to encrypt the message.
byte[] iv = null; // The AES type will generate a random Initialization
// Vector (IV) for us.
byte[] aesKey = // ECDiffieHellmanCng.DeriveKeyMaterial Method.
m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey); // Derives the key material that is generated from
// the secret agreement between two parties.
byte[] ciphertext = null; // Output buffer to hold the encrypted message.
using (Aes aes = new AesCryptoServiceProvider()) // Do the encryption.
{
aes.Key = aesKey;
using (MemoryStream ms = new MemoryStream()) // Create temporary managed memory for the encrypted string.
using (CryptoStream cs = new CryptoStream(ms,
aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(UnicodeText, 0, UnicodeText.Length); // Perform the encryption, and write the results to memory.
cs.FlushFinalBlock(); // Update the temporary memory and clear the cs buffer.
iv = aes.IV; // Save the IV and ciphertext.
ciphertext = ms.ToArray();
}
}
byte[] signature = null; // Sign the ciphertext with the public signature key
if (3 <= Version)
using (ECDsaCng ecdsa = new ECDsaCng(m_DSKey)) // EDCsaCng(CngKey)
{
ecdsa.HashAlgorithm = CngAlgorithm.Sha512;
signature = ecdsa.SignData(ciphertext); // Create a digital signature for the encrypted message
}
byte[] bLengths = new byte[3]; // Create a byte array to hold the three lengths
bLengths[0] = (byte)iv.Length;
bLengths[1] = (byte)ciphertext.Length;
bLengths[2] = (byte)(3 <= Version?signature.Length:0); // Add signature length if Version is 3 or 4
List<byte> list1 = new List<byte> (bLengths); // Create four list objects from the arrays
List<byte> list2 = new List<byte> (iv);
List<byte> list3 = new List<byte> (ciphertext);
List<byte> list4 = 3 <= Version?new List<byte>(signature):null;
list1.AddRange(list2); // Concatenate the four arrays
list1.AddRange(list3);
if (3 <= Version)
list1.AddRange(list4);
byte[] message = list1.ToArray(); // Create the master output array
return ChMgr.SendMessage(message); // Send the master output array
} // End SendMessage
public string ReceiveMessage()
{
Byte[] byteBuffer; // Utility byte buffer
byteBuffer = ChMgr.ReadMessage(); // Read the message.
if (0 == byteBuffer.Length) // The other party closed the command window
{ // with CTRL-C or SYS-EXIT while they had focus
Display("Connection has been closed\n\n"); // (while we were waiting to receive a message)
return "";
}
if (1 == Version) // Not encrypted
{
string AsciiMessage = Encoding.Unicode.GetString(byteBuffer);
Display(" " + AsciiMessage + "\n", OtherColor);
return AsciiMessage;
}
List<byte> list = new List<byte>(byteBuffer);
try
{
// Create three byte arrays to hold the message
// components. The size of these three byte arrays
// are contained in the first three bytes of the message.
iv = new Byte[byteBuffer[0]]; // Storage for the initialization vector.
ciphertext = new Byte[byteBuffer[1]]; // Storage for the ciphertext.
signature = new Byte[byteBuffer[2]]; // Storage for the signature.
}
catch (IndexOutOfRangeException e)
{
Display("Cryptographic error encountered\n\n");
Display(e + "Cryptographic error encountered\n\n");
return "";
}
int ndx = 3;
// CopyTo: 0-based index in source, destination,
// 0-based ndx in destination, elements to copy.
list.CopyTo(ndx, iv, 0, byteBuffer[0]); // Get the initialization vector (IV).
ndx += byteBuffer[0];
list.CopyTo(ndx, ciphertext, 0, byteBuffer[1]); // Get the ciphertext (ciphertext).
ndx += byteBuffer[1];
list.CopyTo(ndx, signature, 0, byteBuffer[2]); // Get the message's digital signature (signature).
Debug.Assert(iv != null, "iv != null");
Debug.Assert(ciphertext != null, "ciphertext != null");
Debug.Assert(signature != null, "signature != null");
if (fVerbose)
{
ASCIIEncoding enc = new ASCIIEncoding();
string s = enc.GetString(iv);
Display(" Incoming Message:\n\n",7);
Display(" Initialization vector: ",7);
Display(s + "\n\n", 7);
s = enc.GetString(ciphertext);
Display(" Ciphertext:\n",7);
Display(s + "\n\n", 7);
s = enc.GetString(signature);
if ("" != s)
{
Display(" Signature:\n",7);
Display(s + "\n\n", 7);
}
Display(" Incoming Decoded message:\n\n", 7);
}
// Derive the key that will be used to decrypt the message.
byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey);
// We've got the key material, now we can decrypt the message.
byte[] plaintext = null;
using (Aes aes = new AesCryptoServiceProvider())
{
aes.Key = aesKey;
aes.IV = iv;
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms,
aes.CreateDecryptor(),
CryptoStreamMode.Write))
{
cs.Write(ciphertext, 0, ciphertext.Length);
cs.FlushFinalBlock();
plaintext = ms.ToArray();
}
}
string message = Encoding.Unicode.GetString(plaintext); // Convert the raw plaintext back to the transmitted message
Display(" " + message + "\n", OtherColor);
if (3 <= Version)
using (ECDsaCng ecdsa = new ECDsaCng(m_DSKey)) // Verify the ciphertext by hashing the ciphertext,
{ // and comparing the result with the signature
ecdsa.HashAlgorithm = CngAlgorithm.Sha512;
if (!ecdsa.VerifyData(ciphertext, signature))
if ("Alice Green" == MyName || "Bob White" == MyName)
Display("SECURITY WARNING! "
+"Received signature did not verify.\n\n",0);
}
if (fVerbose)
Display(sep1,1);
return message;
}
//-------------------------------------------------------------------------------------------------
} // End Communicator class
} // End Communicator.cs: public partial class CNG_SecureCommunicationExample
See Also
Tasks
How to: Build and Run the CNG Example
Concepts
Source Code Overview (CNG Example)
Cryptography Next Generation (CNG) Secure Communication Example