메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

.NET의 암호화 API 사용하기(2)

한빛미디어

|

2007-06-20

|

by HANBIT

13,288

제공 : 한빛 네트워크
저자 : Wei-Meng Lee
역자 : 이대엽
원문 : Using the Cryptography APIs in .NET

[이전 기사 보기]
.NET의 암호화 API 사용하기(1)

대칭형 암호화(Symmetric Encryption)

대칭형 암호화는 간혹 비밀키 암호화(private key encryption)로 알려져 있기도 하다. 비밀키 암호화를 이용하여 여러분은 여러분만이 아는 키를 사용하여 비밀 메시지를 암호화한다. 그 메시지를 복호화 하려면 동일한 키를 사용해야 한다. 비밀키 암호화는 키 자체의 기밀성을 유지할 수 있을 때 효과적이다. 만약 너무 많은 사람들이 키를 알게 되면 그 효과는 떨어진다.

여러분이 비밀키를 이용하여 비밀 메시지를 멀리 떨어진 친구인 수잔에게 보내려 한다고 상상해 보자. 수잔이 비밀 메시지를 복호화하려면 비밀 키를 알아야만 하기 때문에 여러분은 비밀키를 수잔에게 보낼 필요가 있다. 그런데 만약 어쩌다 키의 기밀성이 훼손된다면(다른 누군가가 대화를 엿듣거나 하여), 그 메시지는 더 이상 안전하지 않다. 게다가 만약 수잔이 다른 친구에게 비밀키에 관해 얘기라도 해준다면 수잔의 친구 역시 그 메시지를 복호화할 수 있게 된다. 비밀키 암호화의 잠재적인 약점에도 불구하고 비밀키 암호화의 구현은 계산적인 측면에서 매우 쉽기 때문에 너무 많은 자원을 차지하지는 않는다.

비밀키 암호화(대칭형)에 대하여, .NET 프레임워크에서는 DES, RC2, Rijndael, TripleDES 알고리즘을 지원하고 있다.

대칭형 암호화를 보여주기 위하여 아래의 SymmetricEncryption() 함수에서 RijndaelManaged 클래스를 이용할 것이다. 함수를 사용하려면 세 개의 파라미터가 필요한데, 암호화할 문자열과 비밀키, 그리고 초기화 벡터이다(initialization vector; IV). IV는 암호화 과정에서 사용되는 난수로서 암호화 과정이 완료된 후에 어떠한 두 문자열도 동일한 암호문(cipher text, 암호화된 텍스트)을 만들어내지 않을 것을 보장한다. 여러분은 나중에 암호문을 복호화할 때 동일한 IV를 필요로 할 것이다.
    Private Function SymmetricEncryption( _
       ByVal str As String, _
       ByVal key As Byte(), _
       ByVal IV As Byte()) As String

        Dim memStream As New IO.MemoryStream
        Try
            "--- RijndaelManaged 클래스의 새 인스턴스 생성 ---
            Dim RMCrypto As New RijndaelManaged

            "--- CryptoStream 클래스의 새 인스턴스 생성 ---
            Dim CryptStream As New CryptoStream(memStream, _
                RMCrypto.CreateEncryptor(key, IV), _
                CryptoStreamMode.Write)
            Dim SWriter As New StreamWriter(CryptStream)

            "--- 문자열 암호화 ---
            SWriter.Write(str)
            SWriter.Close()
            CryptStream.Close()

            "--- 암호화된 데이터를 문자열로 반환 ---
            Return System.Convert.ToBase64String(memStream.ToArray)
        Catch err As Exception
            Console.WriteLine(err.ToString)
            Return (String.Empty)
        End Try
    End Function
이전에 알아본 함수에서는 암호화된 문자열이 Base64 형식으로 인코딩된 문자열로 반환된다. 주의할 것은 RijndaelManaged 클래스에서 허용가능한 키의 크기이다. 여러분은 다음의 코드를 이용하여 허용가능한 키의 크기를 확인해 볼 수 있다:
        Dim ks() As KeySizes
        Dim RMCrypto As New RijndaelManaged
        ks = RMCrypto.LegalKeySizes

        "--- 여러가지 키의 크기를 출력 ---
        Console.WriteLine(ks(0).MaxSize)   " 256
        Console.WriteLine(ks(0).MinSize)   " 128
        Console.WriteLine(ks(0).SkipSize)  "  64
유효한 키의 크기는 다음과 같다: 16 바이트(128비트), 24바이트(128비트 + 64비트), 32바이트(256비트)

또한 여러분은 시스템이 자동으로 임의의 키와 IV(앞서 알아본 예제에서 파라미터로 전달해야 하는)를 생성하도록 할 수도 있다.
        "--- 키 생성 ---
        RMCrypto.GenerateKey()
        Dim key As Byte() = RMCrypto.Key
        Console.WriteLine("Key : " & System.Convert.ToBase64String(key))

        "---IV 생성 ---
        RMCrypto.GenerateIV()
        Dim IV As Byte() = RMCrypto.IV
        Console.WriteLine("IV : " & System.Convert.ToBase64String(IV))
만약 IV가 사용될 때 null이면 자동적으로 GenerateIV()가 호출된다. 유효한 IV의 크기는 16 바이트이다.

RijndaelManaged 클래스를 이용하여 암호화된 문자열을 복호화하기 위해서는 다음의 SymmetricDecryption() 함수를 사용할 수 있다:
    Private Function SymmetricDecryption( _
       ByVal str As String, _
       ByVal key As Byte(), _
       ByVal IV As Byte()) _
       As String
        Try
            Dim s As String

            "--- 암호화된 문자열을 바이트 배열로 변환 ---
            Dim b As Byte() = System.Convert.FromBase64String(str)

            "--- 복호화하기 위해 바이트 배열을 메모리 스트림으로 변환 ---
            Dim memStream As New MemoryStream(b)
            Dim RMCrypto As New RijndaelManaged
            Dim CryptStream As New CryptoStream(memStream, _
                RMCrypto.CreateDecryptor(key, IV), _
                CryptoStreamMode.Read)

            "--- 스트림을 복호화 ---
            Dim SReader As New StreamReader(CryptStream)
            s = SReader.ReadToEnd

            "--- 복호화된 스트림을 문자열로 변환 ---
            s.ToString()
            SReader.Close()
            Return s
        Catch err As Exception
            Console.WriteLine(err.ToString)
            Return String.Empty
        End Try
    End Function
다음의 코드는 문자열을 암호화하고 복호화하기 위해 SymmetricEncryption() 함수와 SymmetricDecryption() 함수를 어떻게 사용하는지 보여준다.
        "--- 문자열 암호화 ---
        Dim cipherText As String = _
           SymmetricEncryption("This is a string", key, IV)
        Console.WriteLine("Ciphertext: " & cipherText)

        "--- 문자열 복호화 ---
        Console.WriteLine("Original string: " & _
           SymmetricDecryption(cipherText, key, IV))
[그림 2]는 출력 결과를 보여준다.


[그림 2] 대칭형 암호화 및 복호화 실행 화면

비대칭형 암호화(Asymmetric Encryption)

비밀키 암호화는 암호화 과정에서 사용되는 키가 기밀성을 유지해야 할 필요가 있다. 여러분이 비밀 메시지를 보내고자 하는 수신인에게 비밀 메시지를 전송하는 좀 더 효과적인 방법은 비대칭형 암호화(공개 키 암호화(public key encryption)로도 알려져 있음)를 사용하는 것이다. 공개키 암호화 방식에서는 한 쌍의 키를 필요로 한다. 한 쌍의 키는 비밀키와 공개키로 구성되어 있으며 수학적으로 서로 관련되어 있는데, 공개키로 암호화된 메시지는 오직 그것에 대응되는 비밀키를 이용해야만 복호화가 가능하다. 그 역도 성립되는데, 비밀키로 암호화된 메시지는 오직 대응되는 공개키를 이용해야만 복호화가 가능하다. 각각의 시나리오에 대한 예시를 살펴보도록 하자.

수잔에게 메시지를 보내기 전에 수잔은 비밀키와 공개키를 포함하는 한 쌍의 키를 생성할 필요가 있다. 다음으로 수잔은 공개키는 여러분에게(그리고 수잔의 친구 모두에게도) 배포할 수 있지만 비밀키는 수잔만 간직한다. 여러분이 메시지를 수잔에게 보내고 싶을 경우에는 수잔의 공개 키를 이용하여 메시지를 암호화한 다음 그것을 수잔에게 보낸다. 암호화된 메시지를 받게 되면 수잔은 간직하고 있던 비밀키를 이용하여 메시지를 복호화하기 시작한다. 이 경우 수잔은 그 메시지를 복호화할 수 있는 유일한 사람인데, 왜냐하면 공개키로 암호화한 메시지는 그것에 대응되는 비밀키를 이용해야만 복호화가 가능한 방식으로 키 쌍이 작동하기 때문이다. 게다가 비밀키를 서로 교환할 필요가 없으므로 키의 기밀성이 훼손될 위험이 없어진다.

위 시나리오의 반대 상황도 일어날 수 있다. 이번에는 수잔이 그녀의 비밀키로 암호화된 메시지를 여러분에게 보낸다고 가정해 보자. 메시지를 복호화하려면 여러분은 공개키가 필요하다. 시나리오가 중복된 것처럼 보일 수 있는데, 왜냐하면 공개키는 비밀스럽지 않으며 이 사실은 누구나 알고 있다. 그렇지만 이 방법을 이용하면 전달되는 메시지가 중간에 누군가에 의해 조작되지 않았으며 정말로 수잔에게서 온 것임이 보장된다. 만약 메시지가 변경되었다면 여러분은 그것을 복호화할 수 없을 것이다. 여러분이 공개키를 이용하여 메시지를 복호화할 수 있다는 사실이 그 메시지가 변경되지 않았음을 입증해 준다.

전산분야에서 공개키 암호화는 정보를 안전하게 암호화하는 한가지 방법이다. 그렇지만 그것은 계산상으로 많은 비용이 드는 방법인데, 키 쌍을 생성하는 것과 암호화하고 복호화하는데 시간이 많이 걸리기 때문이다. 공개키 암호화는 일반적으로 적은 양의 민감한 정보를 암호화하는데 사용된다.

공개키(비대칭) 암호화에 대하여 .NET 프레임워크에서는 DSA와 RSA 알고리즘을 지원하고 있다. 비대칭형 암호화를 보여주기 위해 RSA 알고리즘을 사용해볼 것이다. 그렇게 하기 위해서는 다음의 AsymmetricEncryption() 함수를 작성하였다. 이 함수는 암호화할 문자열과 공개키로 사용할 문자열의 두 개의 파라미터를 받아들인다:
    Private Function AsymmetricEncryption( _
       ByVal str As String, _
       ByVal publicKey As String) _
       As String

        "--- RSACryptoServiceProvider의 인스턴스 생성 ---
        Try
            Dim RSA As New RSACryptoServiceProvider

            "--- 공개키를 불러옴 ---
            RSA.FromXmlString(publicKey)

            Dim EncryptedStr() As Byte

            "--- 문자열을 암호화 ---
            EncryptedStr = RSA.Encrypt(ASCII.GetBytes(str), _
                           False)

            "--- 암호화된 바이트 배열을 문자열로 변환 ---
            Return System.Convert.ToBase64String(EncryptedStr)

        Catch err As Exception
            Console.WriteLine(err.ToString)
            Return String.Empty
        End Try

    End Function
암호화된 문자열은 Base64 인코딩으로 반환된다. 공개키를 사용하여 암호화된 문자열을 복호화하기 위해 다음의 AsymmetricDecryption() 함수를 정의하였다. 이 함수는 암호화된 문자열과 비밀키로 사용할 문자열의 두 파라미터를 받아들이며 복호화된 문자열을 반환한다.
    Private Function AsymmetricDecryption( _
       ByVal str As String, _
       ByVal privateKey As String) As String

        Try
            "--- RSACryptoServiceProvider의 인스턴스 생성 ---
            Dim RSA As New RSACryptoServiceProvider

            "--- 비밀키를 불러옴 ---
            RSA.FromXmlString(privateKey)

            "--- 문자열을 복호화 ---
            Dim DecryptedStr As Byte() = _
                RSA.Decrypt(System.Convert.FromBase64String(str), False)

            "--- 복호화된 바이트 배열을 문자열로 변환 ---
            Return ASCII.GetString(DecryptedStr)
        Catch err As Exception
            Console.WriteLine(err.ToString)
            Return String.Empty
        End Try
    End Function
다음의 코드는 문자열을 암호화하고 복호화하기 위해 AsymmetricEncryption() 함수와 AsymmetricDecryption() 함수를 어떻게 사용하는지 보여준다:
        Dim publicKey, privateKey As String
        Dim RSA As New RSACryptoServiceProvider()

        "--- 공개키를 획득 ---
        publicKey = RSA.ToXmlString(False)           
        Console.WriteLine("Public key: " & publicKey)
        Console.WriteLine()

        "--- 비밀키와 공개키를 획득 ---
        privateKey = RSA.ToXmlString(True)  
        Console.WriteLine("Private key: " & privateKey)
        Console.WriteLine()

        "--- 문자열을 암호화 ---
        Dim cipherText As String = _
           AsymmetricEncryption("This is a string", publicKey)
        Console.WriteLine("Ciphertext: " & cipherText)
        Console.WriteLine()

        "--- 문자열을 복호화 ---
        Console.WriteLine("Original string: " & _
           AsymmetricDecryption(cipherText, privateKey))
        Console.WriteLine()
여러분은 RSACryptoServiceProvider 클래스에서 ToXmlString() 메서드를 이용하여 RSA 알고리즘에 의해 만들어진 공개키와 비밀키를 획득할 수 있다. 이 메서드는 Boolean 변수를 인자로 취하며 변수의 값으로 False가 전달될 경우 공개키를 반환한다. 만약 변수의 값으로 True가 주어지면 비밀키와 공개키 모두를 반환한다.

[그림 3]은 출력결과를 보여준다.


[그림 3] 비대칭형 암호화 및 복호화 실행 화면

요약

이 기사에서는 여러분의 .NET 애플리케이션을 보호할 때 사용하는 몇 가지 공통 함수들에 대하여 다뤄보았다. .NET 프레임워크는 주요 암호화 알고리즘에 대한 구현체를 모두 제공해주므로 여러분이 이러한 보안 기능을 여러분의 .NET 애플리케이션에 통합하기 위해 별도의 시간을 소모할 이유는 전혀 없을 것이다.


저자 Wei-Meng Lee (weimenglee.blogspot.com)는 공학자이며 최신 마이크로소프트 기술에 대한 실무 교육을 전문으로 하는 회사인 Developer Learning Solutions의 설립자이기도 하다.
TAG :
댓글 입력
자료실

최근 본 책0