Coding Memo

패킷 암호화(비트연산, AES) 본문

etc

패킷 암호화(비트연산, AES)

minttea25 2023. 12. 14. 12:53

서버와 클라이언트 사이에 패킷을 암호화 하여 전송을 하려고 계획했다.

 

생각한 2가지 방법은 일단 모두 대칭키 방식이다.

첫 번째는 단순히 바이트 배열에 대한 비트연산을 활용하는 방법,

두 번째는 AES 암호화를 사용하는 방법.


 

간단한 비트 연산

 

xor 연산을 이용하여 암호화 및 복호화하는 방법이다.

public byte[] Encrypt(byte[] data, byte[] key)
{
    byte[] encrypted = new byte[data.Length];
    for (int i = 0; i < data.Length; i++)
    {
        encrypted[i] = (byte)(data[i] ^ key[i % key.Length]);
    }
    return encrypted;
}

public byte[] Decrypt(byte[] data, byte[] key)
{
    return Encrypt(data, key);
}

 

구현이 간단하고 빠를 것으로 생각된다. 그러나 다음의 취약점이 예상되기도 한다.

1. key 값 노출

2. 키 값 예측이나 패턴을 찾아내는 공격에 취약

 


AES (Advanced Encryption Standard) 사용

 

System.Security.Cryptography.dll 어셈블리에 포함된 암호화 방법이고 AES 암호화 방식을 사용한다. AES 암호화 방식에는 Key와 IV(Initialize Vector) 값을 가지고 암호화 및 복호화가 진행된다.

https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography?view=net-8.0

 

System.Security.Cryptography Namespace

Provides cryptographic services, including secure encoding and decoding of data, as well as many other operations, such as hashing, random number generation, and message authentication. For more information, see Cryptographic Services.

learn.microsoft.com

 

사용방법은 간단하다. Create()를 이용하면 된다.

public static byte[] Encrypt(byte[] plainBytes, byte[] key, byte[] iv)
{
    using (Aes aes = Aes.Create())
    {

        aes.Key = key;
        aes.IV = iv;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        byte[] encrpypted;
        using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
        {
            encrpypted = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);

            return encrpypted;
        }
    }
}

public static byte[] Decrypt(byte[] encryptedBytes, byte[] key, byte[] iv)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        byte[] decrypted;
        using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
        {
            decrypted = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

            return decrypted;
        }
    }
}

 

key와 iv값은 base64 바이트 배열로 가지고 있어야 한다. 만약 올바르지 않은 key나 iv값에 대해 암호화/복호화를 하려고 하면 예외가 발생할 것이다.

이 두 값은 plaintext를 직접 convert할 수도 있고, Aes 클래스에 의해서 랜덤으로 생성할 수도 있다.

Aes 클래스 생성 시 무작위로 Key와 IV값이 설정된다. (아래에서 GenerateKey()와 GenerateIV()는 사실 불필요한 코드이기도 하다.

실행 때마다 무작위로 새롭게 만들기 때문에 실행결과는 매번 달라진다.

byte[] keyBytes;
byte[] ivBytes;
using (Aes myAes = Aes.Create())
{
    myAes.GenerateKey();
    myAes.GenerateIV();

    keyBytes = myAes.Key;
    ivBytes = myAes.IV;
    Console.WriteLine(Convert.ToBase64String(keyBytes));
    Console.WriteLine(Convert.ToBase64String(ivBytes));
}

Key, Iv

 

코드 자체는 간단하지만 단순히 비트 연산을 하는 것보다는 무거워서 빈번하게 실행될 경우 cpu 자원을 많이 사용할 가능성이 있을 것이다. 또한 2개의 값(key, iv)를 모두 안전하게 가지고 있어야 한다는 점이 유의할 점이다.

 


대칭키를 이용한 암호화 방식 2가지에 대한 코드를 살펴보았다.

 

아직도 어떻게 해야할지 잘 모르는 것은 ...

 

 

Key 값을 어떻게 저장할 것인가!?!??!?!?!?!

 

 

1. 코드에 직접 하드코딩하여 넣기

이 방법은 만천하에 key값을 공개하는 것과 마찬가지이다. 실제로 여러가지 프로그램으로 빌드된 어플리케이션의 코드를 알 수 있는 방법이 있다.

 

2. 코드가 아닌 메모리에 들고 있기

서버와의 통신을 통해 key값을 얻어오거나 파일을 통해 읽어서 메모리에 올리는 방법이 있다. 1번 방법처럼 대놓고 key를 공개하는 것은 아닐 수도 있지만 마찬가지로 별 차이가 없을 수 있다. 어플리케이션이 실행되고 있다는 것은 관련 값들이 메모리에 있다는 것이고 여러 툴을 이용해 현재 프로세스의 메모리 값을 확인하고 읽고 컨트롤 할 수 있는 위험이 남아있다.

 

 

대안

 

1. SecureString 사용

SecureString을 이용해 암호화/복호화 시에만 잠깐 메모리에서 사용하고 사용이 끝나면 즉시 메모리에서 제거하는 방법이다. 메모리에 저장하는 문자열 자체(char 집합체...)가 암호화 되어있긴 하다. (결국 이 것도 완벽한 보안을 제공하지는 않는다. 심지어 새 코드에서는 사용하지 않는 것을 추천한다고 나와있기도 하다.)

https://learn.microsoft.com/ko-kr/dotnet/api/system.security.securestring?view=net-8.0

 

SecureString Class (System.Security)

Represents text that should be kept confidential, such as by deleting it from computer memory when no longer needed. This class cannot be inherited.

learn.microsoft.com

 

https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

 

2. 키를 주기적으로 변경

주기적으로 키를 변경하여 공격자가 한 번 획득한 키로 오랫동안 공격하는 것을 방지할 수 있다.

 

3. 키를 안전한 저장소에 저장

마찬가지로 필요시에만 메모리에 로드하고 사용이 끝나면 삭제한다.

 

4. 메모리 암호화 기술 사용

?? (메모리에 저장된 데이터를 암호화하여 외부로부터 보호하는 방식이라는데 자세한 내용은 아직 잘 모르겠다.)

 


메모용

https://stackoverflow.com/questions/18324149/how-to-securely-handle-aes-key-and-iv-values

 

How to securely handle AES “Key” and “IV” values

If I use AES (System.Security.Cryptography) to simply encrypt and decrypt blob or memo fields in a SQL server, then where do I store the “Key” and “IV” values on the server? (File, Regkey, Dbase,.....

stackoverflow.com

https://stackoverflow.com/questions/18324149/how-to-securely-handle-aes-key-and-iv-values

 

How to securely handle AES “Key” and “IV” values

If I use AES (System.Security.Cryptography) to simply encrypt and decrypt blob or memo fields in a SQL server, then where do I store the “Key” and “IV” values on the server? (File, Regkey, Dbase,.....

stackoverflow.com

https://stackoverflow.com/questions/68257954/store-and-retrieve-aes-key-to-file

 

Store and retrieve AES key to file

how do you store AES key to file and then retrieve it? I have tried saving it as string to a text file then retrieve it back and convert it to byte, but the decryption never works… using (var provi...

stackoverflow.com