Share via


Communicator 類別的程式碼分析 (CNG 範例)

Communicator.cs 檔案封裝入 Cryptography Next Generation (CNG) 安全通訊範例的加密和解密方法,其中僅包含一個類別,名為 Communicator。這個類別包含下列小節中所討論的成員和方法:

  • 類別成員

  • 類別建構函式

  • Dispose 方法

  • StoreDSKey 方法

  • Send_or_Receive_PublicCryptoKey 方法

  • SendMessage 方法

  • ReceiveMessage 方法

如需範例概觀和本主題所提及版本的說明,請參閱 Cryptography Next Generation (CNG) 安全通訊範例

類別成員

Communicator 類別包含下列 Private 成員:

  • CngKey m_DSKey

    這個成員是由 Communicator.StoreDSKey 用來儲存數位簽章金鑰。

  • ECDiffieHellmanCng m_ECDH_Cng

    這個成員是由建構函式用來儲存 ECDiffieHellmanCng 類別的執行個體。ReceiveMessage 和 SendMessage 方法會使用這個成員衍生金鑰內容。

  • string m_ECDH_local_publicKey_XML

    這個成員是由建構函式用來儲存本機公用 Elliptic Curve Diffie-Hellman (ECDH) 密碼編譯金鑰的 XML 字串表示。Alice、Bob 和 Mallory 會使用 Send_or_Receive_PublicCryptoKey 方法交換這個 XML 字串。

  • ECDiffieHellmanPublicKey m_ECDH_remote_publicKey

    這個成員是由 Send_or_Receive_PublicCryptoKey 方法用來儲存遠端公用 ECDH 密碼編譯金鑰。

Communicator 類別也會提供下列 Public 成員:

  • ChannelManager ChMgr

    這個成員是由 Alice、Bob 和 Mallory 用來提供具名管道服務。

類別建構函式

public Communicator(string mode, string ChannelName)

建構函式會建立下列三個物件:

  • ECDiffieHellmanCng 類別的執行個體,搭配使用金鑰大小為 521 位元的隨機金鑰組:

    m_ECDH_Cng = new ECDiffieHellmanCng(521)
    

    產生的物件會繫結到 Communicator 類別做為 Private 成員。每個 Communicator 物件會建立這個類別的單一執行個體。

    Alice 和 Bob 會各自建立單一的 Communicator 物件,並各自存取單一的 m_ECDH_Cng 成員。Mallory 會建立兩個 Communicator 物件:一個用來與 Alice 搭配,一個用來與 Bob 搭配。因此,Mallory 可以存取兩個 m_ECDH_Cng 成員。

  • 做為私用 XML 字串的 ECDH 公開金鑰:

    m_ECDH_XmlString = m_ECDH_CryptoKey.ToXmlString(ECKeyXmlFormat.Rfc4050)
    

    ECDiffieHellmanCng.ToXmlString 方法會藉由使用 ECKeyXmlFormat.Rfc4050 格式序列化公開 ECDH 金鑰。

    在範例的版本 2 到 5,Alice 會藉由使用下列程式碼陳述式,在 Run 方法中將產生的 XML 字串傳送給 Bob:

    Alice.Send_or_Receive_PublicCryptoKey("send", MyColor);
    
  • ChannelManager 類別的公用執行個體:

    ChMgr = new ChannelManager(mode, ChannelName)
    

    建構函式會接受下列兩個參數:

    • mode:字串,用於表示如何建立具名管道。這個參數可以是 "server" 或 "client"。

    • ChannelName:字串,用於提供名稱以識別新的具名管道。

Dispose 方法

public void Dispose()

Communicator 類別會繼承 IDisposable 介面並提供 Dispose 方法,以即刻釋放敏感性資源。其中包括 m_ECDH_Cng、m_ECDH_local_publicKey_XML 和 ChMgr 物件。

每個物件是在 C# using 陳述式內建立的,以確保物件會在超出範圍時立即釋放。

StoreDSKey 方法

public void StoreDSKey(byte[] DSKeyBlob)

這個方法會接受包含在位元組陣列中的二進位大型物件 (BLOB)。它會藉由使用 CngKey.Import(array<Byte[], CngKeyBlobFormat) 方法,擷取金鑰 BLOB 中的數位簽章金鑰。接著以下列程式碼儲存金鑰:

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

依據下列版本,Alice、Bob 和 Mallory 會在其 Run 方法中呼叫 StoreDSKey 方法:

  • 在版本 3 到 5,Alice、Bob 和 Mallory 會使用這個方法儲存公開傳輸的相同金鑰。

  • 在版本 4 和 5,Alice 和 Bob 會二度呼叫這個方法,並使用私下傳輸的金鑰覆寫 m_DSKey 成員。

Send_or_Receive_PublicCryptoKey 方法

Send_or_Receive_PublicCryptoKey(string mode, int color)

這個方法會接受兩個參數:

  • mode:字串,用於表示是要建立管道伺服器傳送金鑰,還是要建立管道用戶端接收金鑰。這個參數可以是 "server" 或 "client"。

  • color:整數,用於指定顯示金鑰時使用的色彩。

Send_or_Receive_PublicCryptoKey 方法與 CommunicatorReceiveMessage 和 SendMessage 方法類似,不同之處在於前者會傳送或接收未加密的密碼編譯金鑰,而非加密訊息。ReceiveMessage 和 SendMessage 無法用於密碼編譯金鑰,因為這些方法會嘗試加密和解密金鑰。

交換金鑰後,範例的版本 3 到 5 會驗證金鑰的數位簽章。版本 3 使用的數位簽章是透過 PublicChannel 具名管道而傳輸的。Mallory 會攔截並使用這個簽章,簽署他傳送給 Alice 和 Bob 的替代金鑰和訊息。版本 3 一定會成功通過簽章驗證,因為 Alice、Bob 和 Mallory 使用相同的數位簽章金鑰。

注意事項注意事項

版本 4 和 5 則使用私密的數位簽章來簽署金鑰和訊息,並顯示安全性警告。只有 Alice 和 Bob 接獲這兩個版本。因此,Mallory 並不知道自己的攔截作業已被偵測到。

SendMessage 方法

public bool SendMessage(string plainTextMessage, bool fShowMsg)

Alice、Bob 和 Mallory 會在其 Run 方法中呼叫這個方法。它會依據下列這些步驟加密、數位簽署並傳送訊息:

  1. 在 fShowMsg 值為 true 時顯示純文字訊息。

  2. 使用下列程式碼,將訊息轉換為 Unicode 位元組陣列:

    byte[] UnicodeText = Encoding.Unicode.GetBytes(plainTextMessage);
    
  3. 判斷訊息是否要以純文字傳送。如果範例執行的是版本 1,則使用下列程式碼在 SendMessage 傳送訊息後傳回:

    ChMgr.SendMessage(UnicodeText);
    
  4. 藉由呼叫 ECDiffieHellmanCng.DeriveKeyMaterial(ECDiffieHellmanPublicKey) 方法衍生金鑰內容:

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey)
    
  5. 建立暫存 Aes 物件:

    Aes aes = new AesCryptoServiceProvider()
    
  6. 使用步驟 4 衍生的金鑰內容,初始化 Aes 物件:

    aes.Key = aesKey;
    
  7. 建立暫存 MemoryStream 物件以存放加密字串。

  8. 建立暫存 CryptoStream 物件,並用來加密訊息再寫入到 MemoryStream 物件。

  9. 儲存密碼文字 (Ciphertext) 和初始化向量:

    iv = aes.IV
    ciphertext = ms.ToArray();
    
  10. 如果範例執行的是版本 3、4 或 5,依據下列作業簽署密碼文字:

    • 建立暫存 ECDsaCng.ECDsaCng(CngKey) 物件:

      ECDsaCng ecdsa = new ECDsaCng(m_DSKey)
      
    • 將物件的 HashAlgorithm 屬性初始化為 Sha512 安全雜湊演算法:

      ecdsa.HashAlgorithm = CngAlgorithm.Sha512
      
    • 藉由簽署密碼文字建立數位簽章:

      signature = ecdsa.SignData(ciphertext);
      
  11. 依據下列作業準備輸出訊息:

    • 建立三位元組陣列,存放訊息初始化向量、密碼文字和簽章的長度。

    • 建立 4 個 System.Collections.Generic.List<T> 物件,存放前一步驟的長度陣列、初始化向量、密碼文字和簽章。

    • 串連 4 個 System.Collections.Generic.List<T> 物件,並轉換為單一輸出訊息位元組陣列:

      byte[] message = list1.ToArray();
      
  12. 傳送輸出訊息:

    ChMgr.SendMessage(message)
    

ReceiveMessage 方法

public string ReceiveMessage()

Alice、Bob 和 Mallory 會在其 Run 方法中呼叫這個方法。它會接收和解密訊息,以及驗證其數位簽章。

訊息並不會傳遞做為這個方法的引數。而是由 Communicator 類別的 ChannelManager 成員使用下列程式碼來讀取訊息:

byteBuffer = ChMgr.ReadMessage();

ReceiveMessage 方法會執行下列步驟:

  1. 判斷訊息是否是以純文字傳送。如果範例執行的是版本 1,則訊息是純文字的,不需要解密。在這種情況下,會藉由使用下列程式碼,將訊息轉換為 ASCII 字串後傳回:

    AsciiMessage = Encoding.Unicode.GetString(byteBuffer);
    
  2. 將訊息拆解為幾部分。在版本 2 到 5 中會加密訊息。在這種情況下,訊息會拆解為三個分開的位元組陣列。第一個陣列包含初始化向量,第二個陣列包含密碼文字,而第三個陣列則包含密碼文字的數位簽章。

  3. 如果有設定 fVerbose 旗標,則以 ASCII 文字顯示初始化向量、密碼文字和簽章。

  4. 衍生金鑰內容。Communicator 類別的 Private 成員 ECDiffieHellmanCng (m_ECDH_Cng) 會使用 ECDiffieHellmanCng.DeriveKeyMaterial 方法衍生共用金鑰內容,如下列程式碼所示:

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey);
    
  5. 藉由執行個體化 AesCryptoServiceProvider 物件建立 Aes

    Aes aes = new AesCryptoServiceProvider()
    
  6. 使用前面步驟衍生的金鑰內容和初始化向量,初始化 Aes 物件。

  7. 藉由使用 System.IO.MemoryStreamSystem.Security.Cryptography.CryptoStream 物件解密訊息。

  8. 顯示解密訊息。

  9. 在版本 3 到 5 中,會藉由使用數位簽章驗證訊息的密碼文字。版本 3 並不會顯示安全性警告,因為 Alice、Bob 和 Mallory 使用相同的簽章。版本 4 會在訊息的密碼文字具有無效簽章時,顯示安全性警告。然而,因為只有 Alice 和 Bob 有接獲版本 4,所以 Mallory 絕對不會看到任何警告。在版本 5 傳輸和驗證任何訊息前,簽署無效的密碼編譯金鑰會讓程式終止。

請參閱

概念

Cryptography Next Generation (CNG) 安全通訊範例

Communicator.cs 原始程式碼 (CNG 範例)

原始程式碼概觀 (CNG 範例)

金鑰和訊息的逐步交換 (CNG 範例)

預期的輸出 (CNG 範例)