Análisis de código de la clase Communicator (Ejemplo CNG)

El archivo Communicator.cs encapsula los métodos de cifrado y descifrado en el ejemplo de comunicación segura de criptografía de próxima generación (CNG).Se compone de una única clase, que se denomina Communicator.Esta clase contiene los miembros y métodos que se explican en las secciones siguientes:

  • Miembros de la clase

  • Constructor de la clase

  • Método Dispose

  • Método StoreDSKey

  • Método Send_or_Receive_PublicCryptoKey

  • Método SendMessage

  • Método ReceiveMessage

Vea Ejemplo de comunicación segura de criptografía de próxima generación (CNG) para consultar información general sobre el ejemplo y descripciones de las versiones mencionadas en este tema.

Miembros de la clase

La clase Communicator contiene los tres miembros privados siguientes:

  • CngKey m_DSKey

    Communicator.StoreDSKey utiliza este miembro para almacenar una clave de firma digital.

  • ECDiffieHellmanCng m_ECDH_Cng

    El constructor utiliza este miembro para almacenar una instancia de la clase ECDiffieHellmanCng.Los métodos SendMessage y ReceiveMessage utilizan este miembro para derivar el material de clave.

  • string m_ECDH_local_publicKey_XML

    El constructor utiliza este miembro para almacenar una representación de cadena XML de la clave criptográfica pública Diffie-Hellman de curva elíptica (ECDH).Alice, Bob y Mallory utilizan el método Send_or_Receive_PublicCryptoKey para intercambiar esta cadena XML.

  • ECDiffieHellmanPublicKey m_ECDH_remote_publicKey

    El método Send_or_Receive_PublicCryptoKey utiliza este miembro para almacenar una clave criptográfica pública ECDH remota.

La clase Communicator también proporciona el miembro público el siguiente:

  • ChannelManager ChMgr

    Alice, Bob y Mallory utilizan este miembro para proporcionar servicios de canalización con nombre.

Constructor de la clase

public Communicator(string mode, string ChannelName)

El constructor crea los tres objetos siguientes:

  • Una instancia de la clase ECDiffieHellmanCng con un par de claves aleatorio, que utiliza un tamaño de clave de 521 bits:

    m_ECDH_Cng = new ECDiffieHellmanCng(521)
    

    El objeto resultante se enlaza a la clase Communicator como un miembro privado.Cada objeto Communicator crea una única instancia de esta clase.

    Alice y Bob crean cada uno un único objeto Communicator y pueden tener acceso a un único miembro m_ECDH_Cng.Mallory crea dos objetos Communicator: uno para utilizarlo con Alice y el otro para utilizarlo con Bob.Por tanto, Mallory puede tener acceso a dos miembros m_ECDH_Cng.

  • La clave pública ECDH como cadena XML privada:

    m_ECDH_XmlString = m_ECDH_CryptoKey.ToXmlString(ECKeyXmlFormat.Rfc4050)
    

    El método ECDiffieHellmanCng.ToXmlString serializa la clave pública ECDH utilizando el formato ECKeyXmlFormat.Rfc4050.

    En las versiones 1 a 5 del ejemplo, Alice envía la cadena XML resultante a Bob en su método Run utilizando la instrucción de código siguiente:

    Alice.Send_or_Receive_PublicCryptoKey("send", MyColor);
    
  • Una instancia pública de la clase ChannelManager:

    ChMgr = new ChannelManager(mode, ChannelName)
    

    El constructor acepta los dos parámetros siguientes:

    • mode: Cadena que indica cómo se crea la canalización con nombre.Este parámetro puede ser "servidor" o "cliente".

    • ChannelName: Cadena que proporciona un nombre para identificar la nueva canalización con nombre.

Método Dispose

public void Dispose()

La clase Communicator hereda la interfaz IDisposable y proporciona el método Dispose para liberar inmediatamente los recursos críticos.Entre ellos se incluyen los objetos m_ECDH_Cng, m_ECDH_local_publicKey_XML y ChMgr.

Cada objeto se crea dentro de una instrucción using de C# para asegurar que el objeto se liberará inmediatamente cuando salga del ámbito.

Método StoreDSKey

public void StoreDSKey(byte[] DSKeyBlob)

Este método acepta un objeto binario grande (BLOB) que está incluido en una matriz de bytes.Extrae una clave de firma digital del BLOB de la clave mediante el método CngKey.Import(array<Byte[], CngKeyBlobFormat).La clave se almacena a continuación mediante el código siguiente:

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

Alice, Bob y Mallory llaman al método StoreDSKey en sus métodos Run en las versiones siguientes:

  • En las versiones 3 a 5, Alice, Bob y Mallory utilizan este método para almacenar la misma clave transmitida públicamente.

  • En las versiones 4 y 5, Alice y Bob llaman a este método por segunda vez y sobrescriben el miembro m_DSKey con una clave transmitida de forma privada.

Método Send_or_Receive_PublicCryptoKey

Send_or_Receive_PublicCryptoKey(string mode, int color)

Este método acepta dos parámetros:

  • mode: Cadena que indica si se va a crear un servidor de canalización para que envíe una clave o se va a crear un cliente de canalización para que reciba una clave.Este parámetro puede ser "servidor" o "cliente".

  • color: Entero que especifica el color que se utiliza para mostrar la clave.

El método Send_or_Receive_PublicCryptoKey es similar a los métodos CommunicatorReceiveMessage y SendMessage, excepto que envía o recibe claves criptográficas sin cifrar en lugar de mensajes cifrados.ReceiveMessage y SendMessage no se pueden usar para claves criptográficas porque estos métodos intentarían cifrar y descifrar las claves.

Una vez que se intercambian las claves, las versiones 3 a 5 del ejemplo validan las firmas digitales de las claves.La versión 3 utiliza una firma digital que se transmite a través de la canalización con nombre PublicChannel.Mallory intercepta y utiliza esta firma para firmar las claves y los mensajes suplantados que envía a Alice y Bob.La versión 3 siempre realiza correctamente la validación de la firma porque Alice, Bob y Mallory utilizan la misma clave de firma digital.

Nota

Las versiones 4 y 5 utilizan una firma digital privada para firmar claves y mensajes y muestran advertencias de seguridad.Estas versiones solo se proporcionan a Alice y Bob.Por tanto, Mallory no sabe que se han detectado sus interceptaciones.

Método SendMessage

public bool SendMessage(string plainTextMessage, bool fShowMsg)

Alice, Bob y Mallory llaman a este método desde sus métodos Run.Este método cifra los mensajes, los firma digitalmente y los envía siguiendo estos pasos:

  1. Muestra el mensaje de texto simple si el valor fShowMsg es true.

  2. Convierte el mensaje en una matriz de bytes Unicode mediante el código siguiente:

    byte[] UnicodeText = Encoding.Unicode.GetBytes(plainTextMessage);
    
  3. Determina si el mensaje debe enviarse como texto simple.Si se ejecuta la versión 1 del ejemplo, SendMessage devuelve después de enviar el mensaje mediante el código siguiente:

    ChMgr.SendMessage(UnicodeText);
    
  4. Deriva el material de clave llamando al método ECDiffieHellmanCng.DeriveKeyMaterial(ECDiffieHellmanPublicKey):

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey)
    
  5. Crea un objeto Aes temporal:

    Aes aes = new AesCryptoServiceProvider()
    
  6. Inicializa el objeto Aes con el material de clave que se derivó en el paso 4:

    aes.Key = aesKey;
    
  7. Crea un objeto MemoryStream temporal que va a contener una cadena cifrada.

  8. Crea un objeto CryptoStream temporal y lo utiliza para cifrar el mensaje y escribirlo en el objeto MemoryStream.

  9. Guarda el texto cifrado y el vector de inicialización:

    iv = aes.IV
    ciphertext = ms.ToArray();
    
  10. Si se ejecuta la versión 3, 4 o 5 del ejemplo, firma el texto cifrado del modo siguiente:

    • Crea un objeto ECDsaCng.ECDsaCng(CngKey) temporal:

      ECDsaCng ecdsa = new ECDsaCng(m_DSKey)
      
    • Inicializa la propiedad HashAlgorithm del objeto como un algoritmo hash seguro Sha512:

      ecdsa.HashAlgorithm = CngAlgorithm.Sha512
      
    • Crea una firma digital mediante la firma del texto cifrado:

      signature = ecdsa.SignData(ciphertext);
      
  11. Prepara el mensaje de salida del modo siguiente:

    • Crea una matriz de tres bytes que contiene la longitud del vector de inicialización del mensaje, la longitud del texto cifrado y la longitud de la firma.

    • Crea cuatro objetos System.Collections.Generic.List<T> que contienen la matriz de longitudes del paso anterior, el vector de la inicialización, el texto cifrado y la firma.

    • Concatena los cuatro objetos System.Collections.Generic.List<T> y los convierte en una matriz de bytes con un único mensaje de salida:

      byte[] message = list1.ToArray();
      
  12. Envía el mensaje de salida:

    ChMgr.SendMessage(message)
    

Método ReceiveMessage

public string ReceiveMessage()

Alice, Bob y Mallory llaman a este método desde sus métodos Run.Recibe los mensajes, los descifra y valida sus firmas digitales.

Los mensajes no se pasan como parámetros a este método.En su lugar, el miembro ChannelManager de la clase Communicator lee el mensaje con el código siguiente:

byteBuffer = ChMgr.ReadMessage();

El método ReceiveMessage lleva a cabo los pasos siguientes:

  1. Determina si el mensaje se envió como texto simple.Si se está ejecutando la versión 1 del ejemplo, el mensaje está en texto simple y no es necesario descifrarlo.En este caso, el mensaje se devuelve una vez que se convierte en una cadena ASCII mediante el código siguiente:

    AsciiMessage = Encoding.Unicode.GetString(byteBuffer);
    
  2. Divide el mensaje en distintos componentes.En las versiones 2 a 5, el mensaje estará cifrado.En este caso, el mensaje se divide en tres matrices de bytes independientes.La primera matriz contiene el vector de inicialización, la segunda matriz contiene el texto cifrado y la tercera matriz contiene la firma digital del texto cifrado.

  3. Muestra el vector de inicialización, el texto cifrado y la firma como texto ASCII si se establece el marcador fVerbose.

  4. Deriva el material de clave.El miembro ECDiffieHellmanCng privado, m_ECDH_Cng, de la clase Communicator utiliza el método ECDiffieHellmanCng.DeriveKeyMaterial para derivar el material de clave compartido, tal y como se muestra en el código siguiente:

    byte[] aesKey = m_ECDH_Cng.DeriveKeyMaterial(m_ECDH_remote_publicKey);
    
  5. Crea un objeto Aes creando instancias de un objeto AesCryptoServiceProvider:

    Aes aes = new AesCryptoServiceProvider()
    
  6. Inicializa el objeto Aes con el material de clave y el vector de inicialización derivado de los pasos anteriores.

  7. Descifra el mensaje utilizando objetos System.Security.Cryptography.CryptoStream y System.IO.MemoryStream.

  8. Muestra el mensaje descifrado.

  9. Valida el texto cifrado del mensaje en las versiones 3 a 5 utilizando la firma digital.La versión 3 no muestra las advertencias de seguridad porque Alice, Bob y Mallory utilizan la misma firma.La versión 4 muestra las advertencias de seguridad cuando el texto cifrado de un mensaje tiene una firma no válida.Sin embargo, solo Alice y Bob tienen la versión 4, por lo que Mallory nunca ve ninguna advertencia.En la versión 5, las claves criptográficas firmadas que no son válidas producen la finalización del programa antes de que un mensaje se transmita y se valide.

Vea también

Conceptos

Ejemplo de comunicación segura de criptografía de próxima generación (CNG)

Código fuente Communicator.cs (Ejemplo CNG)

Información general sobre el código fuente (Ejemplo CNG)

Intercambio de mensajes y claves paso a paso (Ejemplo CNG)

Resultado esperado (Ejemplo CNG)