Math

AES는 어떻게 15바이트를 암호화할까?

Falto 2024. 10. 8. 14:22

AES는 16바이트 블록 단위로 암호화/복호화하는 알고리듬이다. 근데 이 세상의 모든 데이터가 16바이트로 나누어 떨어지지는 않는다. 그럼 1, 2, 3, ..., 15바이트짜리 데이터는 암호화 못 하는건가? 이상하다. 분명 AES는 표준으로 채택될 만큼 범용성 있는 알고리듬인데, 무작위 데이터가 주어졌을 때 그것을 암호화할 수 있을 확률이 고작 0.0625%(1/16) 밖에 안 된다고? 그럴 리가 없다. 그러면 어떻게 15바이트, 1바이트, 6바이트 등과 같은 데이터들을 암호화하지? 뒤에 0을 붙이나? 사실 그것도 말이 안 된다. 0x10 00 00과 0x10 00, 0x10은 모두 다르기 때문이다. 바이트 0도 엄연히 크기를 차지하는 데이터다. 그럼 대체 어떻게 15바이트를 암호화할 수 있는거지? 나무위키에 따르면 PKCS#7 패딩을 이용한다고 한다. 설명하자면, 16바이트를 만드는데 1바이트가 부족하면 0x01을 뒤에 붙인다. 2바이트가 부족하면 0x02 02를 뒤에 붙이고, 3바이트가 부족하면 0x03 03 03을 뒤에 붙인다. 딱 16바이트로 나누어 떨어진다면 0x10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 을 뒤에 붙인다. 이 방법을 이용하면 16바이트로 나누어 떨어지지 않는 데이터들도 암호화할 수 있다. 그리고 이 사실로부터, 16바이트를 암호화하면 32바이트가 나올 수 있다는 사실도 알 수 있다.

using System;
using System.IO;
using System.Security.Cryptography;

namespace Aes_Example
{
    class AesExample
    {
        private static void Test(byte[] original, byte[] key, byte[] iv)
        {
            // Create a new instance of the Aes
            // class.  This generates a new key and initialization
            // vector (IV).
            using (Aes myAes = Aes.Create())
            {
                myAes.Key = key;
                myAes.IV = iv;

                // Encrypt the string to an array of bytes.
                byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

                // Decrypt the bytes to a string.
                byte[] roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);

                //Display the original data and the decrypted data.
                Console.WriteLine("Original:   {0}", string.Join(" ", original));
                Console.WriteLine("Cipher:   {0}", string.Join(" ", encrypted));
                Console.WriteLine("Round Trip: {0}", string.Join(" ", roundtrip));
                Console.WriteLine();
            }
        }


        public static void Main()
        {
            using (Aes myAes = Aes.Create())
            {
                Test(new byte[2] { 123, 45 }, myAes.Key, myAes.IV);
                Test(new byte[3] { 123, 45, 0 }, myAes.Key, myAes.IV);
                Test(new byte[4] { 123, 45, 0, 0 }, myAes.Key, myAes.IV);
                Test(new byte[5] { 123, 45, 0, 0, 1 }, myAes.Key, myAes.IV);
                Test(new byte[8] { 123, 45, 0, 0, 1, 2, 3, 5 }, myAes.Key, myAes.IV);
                Test(new byte[15] { 123, 45, 0, 0, 1, 2, 3, 5, 123, 45, 0, 0, 1, 2, 3 }, myAes.Key, myAes.IV);
                Test(new byte[16] { 123, 45, 0, 0, 1, 2, 3, 5, 123, 45, 0, 0, 1, 2, 3, 5 }, myAes.Key, myAes.IV);
            }
        }


        static byte[] EncryptStringToBytes_Aes(byte[] plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        csEncrypt.Write(plainText, 0, plainText.Length);
                    }

                    encrypted = msEncrypt.ToArray();
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }

        static byte[] DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            byte[] plaintext = new byte[cipherText.Length + 15];

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        int actualLength = csDecrypt.Read(plaintext, 0, plaintext.Length);
                        byte[] plain = new byte[actualLength];
                        for (int i = 0; i < actualLength; i++)
                        {
                            plain[i] = plaintext[i];
                        }
                        return plain;
                    }
                }
            }
        }
    }
}

위 코드의 실행 결과는 아래와 같다.

Original:   123 45
Cipher:   10 153 24 16 139 58 113 146 217 3 223 47 229 91 190 244
Round Trip: 123 45

Original:   123 45 0
Cipher:   235 224 243 69 21 71 112 23 114 39 92 130 227 99 37 197
Round Trip: 123 45 0

Original:   123 45 0 0
Cipher:   125 241 214 146 135 141 48 57 228 189 79 94 104 155 90 102
Round Trip: 123 45 0 0

Original:   123 45 0 0 1
Cipher:   171 220 188 90 190 172 240 180 96 32 33 222 1 90 220 222
Round Trip: 123 45 0 0 1

Original:   123 45 0 0 1 2 3 5
Cipher:   185 245 125 69 235 119 101 86 199 175 110 109 191 57 88 209
Round Trip: 123 45 0 0 1 2 3 5

Original:   123 45 0 0 1 2 3 5 123 45 0 0 1 2 3
Cipher:   27 80 206 92 164 31 40 105 77 182 204 24 39 119 83 199
Round Trip: 123 45 0 0 1 2 3 5 123 45 0 0 1 2 3

Original:   123 45 0 0 1 2 3 5 123 45 0 0 1 2 3 5
Cipher:   132 119 144 164 204 66 211 121 12 116 202 118 133 161 55 38 188 128 89 178 137 78 132 220 251 124 37 33 21 140 131 85
Round Trip: 123 45 0 0 1 2 3 5 123 45 0 0 1 2 3 5

16바이트를 암호화했더니 정말 32바이트의 cipher가 나왔다.

 

반응형

'Math' 카테고리의 다른 글

하나의 집합과 하나 이상의 이항 연산으로 정의되는 대수 구조들  (0) 2024.10.16
모노이드  (0) 2024.10.16
  (0) 2024.10.16
  (0) 2024.10.16