Communicator.cs 原始程式碼 (CNG 範例)
Communicator.cs 檔案提供您 Cryptography Next Generation (CNG) 安全通訊範例所使用之 Communicator 類別的原始程式碼。其中包含下列程式碼。
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