Share via


Communicator 클래스의 코드 분석(CNG 예제)

업데이트: 2008년 7월

Communicator.cs 파일은 CNG(Cryptography Next Generation) 보안 통신 예제의 암호화 및 암호 해독 메서드를 캡슐화합니다. 이 파일은 Communicator라는 클래스 하나로만 구성되어 있습니다. 이 클래스에는 다음 단원에서 설명하는 멤버와 메서드가 들어 있습니다.

  • 클래스 멤버

  • 클래스 생성자

  • Dispose 메서드

  • StoreDSKey 메서드

  • Send_or_Receive_PublicCryptoKey 메서드

  • SendMessage 메서드

  • ReceiveMessage 메서드

이 항목에 언급된 예제에 대한 개요 및 버전에 대한 설명은 CNG(Cryptography Next Generation) 보안 통신 예제를 참조하십시오.

클래스 멤버

Communicator 클래스에는 다음과 같은 전용 멤버가 들어 있습니다.

  • CngKey m_DSKey

    이 멤버는 Communicator.StoreDSKey에서 디지털 서명 키를 저장하는 데 사용됩니다.

  • ECDiffieHellmanCng m_ECDH_Cng

    이 멤버는 생성자에서 ECDiffieHellmanCng 클래스의 인스턴스를 저장하는 데 사용됩니다. ReceiveMessage 및 SendMessage 메서드는 이 멤버를 사용하여 키 자료를 파생합니다.

  • string m_ECDH_local_publicKey_XML

    이 멤버는 생성자에서 로컬 공개 ECDH(Elliptic Curve Diffie-Hellman) 암호화 키의 XML 문자열 표현을 저장하는 데 사용됩니다. Alice, Bob 및 Mallory는 Send_or_Receive_PublicCryptoKey 메서드를 사용하여 이 XML 문자열을 교환합니다.

  • ECDiffieHellmanPublicKey m_ECDH_remote_publicKey

    이 멤버는 Send_or_Receive_PublicCryptoKey 메서드에서 원격 공개 ECDH 암호화 키를 저장하는 데 사용됩니다.

Communicator 클래스는 다음과 같은 공용 멤버도 제공합니다.

  • ChannelManager ChMgr

    이 멤버는 Alice, Bob 및 Mallory가 명명된 파이프 서비스를 제공하는 데 사용됩니다.

클래스 생성자

public Communicator(string mode, string ChannelName)

생성자는 다음과 같은 세 개의 개체를 만듭니다.

  • 521비트의 키 크기와 난수 키 쌍을 사용한 ECDiffieHellmanCng 클래스의 인스턴스

    m_ECDH_Cng = new ECDiffieHellmanCng(521)
    

    결과 개체는 Communicator 클래스에 전용 멤버로 바인딩됩니다. 각 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 키를 serialize합니다.

    예제의 버전 2-5에서는 Alice가 다음 코드 명령문을 사용하여 Run 메서드를 통해 Bob에게 결과 XML 문자열을 전송합니다.

    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. 다음 코드를 사용하여 메시지를 유니코드 바이트 배열로 변환합니다.

    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. 암호화 텍스트 및 초기화 벡터를 저장합니다.

    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. 다음과 같이 출력 메시지를 준비합니다.

    • 메시지의 초기화 벡터, 암호화 텍스트 및 서명의 길이를 보유할 3바이트 배열을 만듭니다.

    • 이전 단계의 길이 배열, 초기화 벡터, 암호화 텍스트 및 서명을 보유할 네 개의 System.Collections.Generic.List<T> 개체를 만듭니다.

    • 네 개의 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 클래스의 전용 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에서는 암호화 키의 서명이 잘못된 경우 메시지를 전송하고 유효성을 검사하기 전에 프로그램이 종료됩니다.

참고 항목

개념

CNG(Cryptography Next Generation) 보안 통신 예제

Communicator.cs 소스 코드(CNG 예제)

소스 코드 개요(CNG 예제)

단계별 키 및 메시지 교환(CNG 예제)

예상 출력(CNG 예제)

변경 기록

날짜

변경 내용

이유

2008년 7월

항목이 추가되었습니다.

향상된 기능 관련 정보