
대칭키 암호화 방식이란
대칭키 암호화는 말그대로 암호화 키와 복호화 키가 대칭이라는 말로, 즉 암호화 복호화 키가 같은 암호화 방식을 말합니다. 따라서 대칭키 암호에서는 암호화를 하는 측과 복호화를 하는 측이 같은 암호키를 공유해야 합니다.
대칭키 암호화 특징
대칭키 암호화 방식은 키 크기가 상대적으로 작고 암호화 알고리즘 내부 구조가 단순하여 시스템 개발에 용이하다는 장점이 있습니다. 그러나 교환 당사자간 동일한 키를 공유해야 하기 때문에 키 관리에 어려움이 있으며, 잦은 키 변경이 있는 경우 불편함을 초래한다는 장점이 있습니다.
대칭키 암호는 혼돈(Confuse)과 확산(Diffusion) 의 성질을 이용하여 평문을 암호화 하는데, 혼돈 효과는 치환(Substitution) 을 통해 형성되고, 확산 효과는 순열(Permutation)을 통해 얻게 됩니다.
대칭키 암호를 설계할 때, 치환과 순열 연산을 포함하여 하나의 라운드(Round)를 구성하고 이 라운드를 반복적으로 사용하는 구조로 대칭키 암호를 구현합니다.
이러한 대칭키 암호는 데이터를 변환하는 방법에 따라 블록암호화 방식과 스트림 암호로 구별합니다. 이번 포스트에서는 스트림 암호에 대한 설명은 제외하고, 블록암호에 대해서만 설명합니다.
블록 암호(Block cipher)
먼저 블록 암호에 대해 살펴보겠습니다.
블록 암호란 기밀성(Confidentiality) 있는 정보를 블록 단위로 암호화 하는 방식을 의미합니다. 여기서 기밀성 있는 정보란 암호화 할 대상을 의미합니다. 암호화하려는 정보가 블록의 길이보다 길 경우 특정한 운용 방식이 사용됩니다. 특정 운용 방식의 종류로 ECB, CBC, OFB, CFB, CTR 등이 있습니다. 이에 대한 내용은 아래에서 서술하겠습니다.
블록 암호의 장점은 블록 단위 암호화로 인해 기호를 삽입하거나 제거가 불가능하고, 다양한 운영 방법에 의해 설계될 수 있다는 점을 들 수 있습니다. 그러나 블록 단위로 암호화가 이루어지기 때문에 암호화 과정이 블록의 크기에 따라 지연되므로, 암호화 속도가 느리다는 단점이 있습니다.
패딩(PADDING)
주어진 평문을 블록 암호로 암호화 하기 위해서는 평문을 블록 크기의 배수로 만들어야 합니다. 블록의 사이즈는 암호화방식마다 다릅니다.
예를 들어 AES 128 암호화 알고리즘의 경우 블록의 크기가 128bit 라는 의미로 암호화 할 데이터를 16byte 씩 쪼개어 블록을 생성한다고 생각하면 블록 암호를 처음 접한 분들이 이해하기 편할 것입니다.
즉 암호화 할 데이터를 블록에 크기에 맞개 쪼개야 하는데, 암호화 할 데이터의 크기가 블록의 배수가 아닌 경우, 블록의 배수가 되도록 데이터의 길이를 맞춰줄 필요가 있습니다. 이때 블록사이즈와 암호화 할 데이터의 길이가 맞지 않거나, 혹은 초과 하는 경우, 블록의 배수가 되도록 데이터를 추가해야 합니다. 이 때, 추가되는 데이터를 패딩(Padding)이라고 합니다. 이러한 패딩을 추가하는 알고리즘의 종류로 PKCS#7, PKCS#5, ANSIX923 등이 있습니다. 패딩알고리즘은 크게 중요하지는 않지만, 패딩 길이 결정, 패딩바이트 생성, 패딩 적용, 패딩 해제 시 제거 등의 기능을 수행합니다.
패딩을 추가하는데 있어 가장 중요한 원칙은 P(M) 으로부터 M 을 복원할 수 있어야 합니다. 이 원칙이 지켜지지 않는다면, 패딩이 추가된 메세지로부터 원본 메세지를 찾을 수 없습니다. 예를 들어 평문의 길이를 32bit의 배수로 만들고 싶을 때, 모자란만큼 0으로 채워 32의 배수로 만드는 패딩을 생각해봅시다. 어떤 평문이 있고, 패딩을 추가하여 생성된 메세지가 00010011 01000111 11001000 00000000 일때, 실제 평문이 어디까지 인지 알 수 없습니다. 따라서 비트단위의 올 제로(All Zero) 패딩은 패딩된 메세지로부터 원본 메세지를 복구할 수 없으므로 사용할 수 없습니다.
운용방식
블록 암호 운용 방식의 종류로 ECB, CBC, OFB, CFB, CTR 등이 있다고 앞서 서술하였습니다. 운용방식이란 하나의 키에서 블록 암호를 반복적으로 안전하게 이용하는 절차를 의미합니다. 블록 암호는 특정한 길이의 블록 단위로 동작하기 때문에, 가변적인 길이의 데이터를 암호화하기 위해 먼저 이들을 블록 단위로 나누어야 하고, 그 블록들을 어떻게 암호화 할지 정해야 합니다. 이때 블록들의 암호화 방식을 운용 방식이라고 합니다.
먼저 ECB 운용 방식을 살펴보겠습니다. ECB는 전자 코드북(Electronic CodeBook; ECB)의 약자로 운용 방식 중 가장 간단한 구조를 갖고 있습니다.
암호화하려는 메세지를 여러 블록으로 나누어 각각 암호화를 수행합니다. 이때 모든 블록이 같은 암호화 키를 사용하기 때문에 보안에 취약합니다. 또한 암호화 메세지를 여러 부분으로 나누었을 때, 두 블록이 같은 값을 가진다면, 암호화 한 결과 역시 같습니다. 이는 공격자가 비슷한 메세지를 반복적으로 암호화하는 반복 공격에도 취약합니다.
다음으로 CBC 운용 방식을 살펴보겠습니다. CBC는 암호 블록체인(Cipher Block Chaining; CBC)의 약자로, 1976년 IBM에 의해 개발된 운용 방식이며, 현재 대중적으로 가장 많이 사용되고 있습니다.
CBC 운용방식에서 각 블록은 암호화 되기 전 이전 블록의 암호화 결과와 베타적 논리합 (XOR) 연산이 수행되며, 첫 블록의 경우 초기화 벡터(Initialize Vector; IV)가 사용됩니다. 초기화 벡터가 같은 경우 출력 결과가 항상 같기 때문에, 매 암호화 마다 다른 IV 값을 사용해야 합니다. CBC 운용 방식은 암호화 입력 값이 이전 결과에 의존하기 때문에 병렬화가 불가능하지만, 복호화의 경우 각 블록을 복호화 한 다음 이전 암호화 블록과 XOR 연산을 수행하여 복구할 수 있기 때문에 병렬화가 가능하다는 특징을 갖고 있습니다.
Python을 이용한 대칭키 블록 암호 예제
해당 포스트에서는 암호화 알고리즘을 구현하지는 않습니다. 대칭키 암호에는 여러 종류가 있으며, 각 암호화 방법마다 알고리즘도 조금 씩 다르기 때문입니다. 그러나 블록 단위로 라운드 연산을 통해 암호화를 수행한다는 점은 동일하므로, 대칭키 암호화의 종류 별로 알고리즘의 원리를 알고 싶은 분들은, 개별적으로 검색해보시기 바랍니다.
본 문단에서는 python을 이용하여 대칭키 암호화를 구현해 보도록 하겠습니다. 코드 구현을 위해 pycryptodome, 라이브러리를 사용하며, 운용 방식은 암호화 종류와 상관 없이 CBC 만 살펴보도록 하겠습니다.
[DES]
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
def encrypt(plaintext, key):
# 8바이트 랜덤한 IV(Initialization Vector) 생성
iv = get_random_bytes(8)
# DES CBC 모드로 암호화
cipher = DES.new(key, DES.MODE_CBC, iv)
ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), 8))
return iv + ciphertext
def decrypt(ciphertext, key):
# IV 추출
iv = ciphertext[:8]
# DES CBC 모드로 복호화
cipher = DES.new(key, DES.MODE_CBC, iv)
decrypted_text = unpad(cipher.decrypt(ciphertext[8:]), 8)
return decrypted_text.decode('utf-8')
if __name__ == "__main__":
# 8바이트의 키 생성
key = get_random_bytes(8)
# 암호화할 평문
plaintext = "Hello, DES CBC!"
# 암호화
encrypted_data = encrypt(plaintext, key)
print("Encrypted:", encrypted_data.hex())
# 복호화
decrypted_data = decrypt(encrypted_data, key)
print("Decrypted:", decrypted_data)
[3DES]
from Crypto.Cipher import DES3
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
def encrypt_3des(plaintext, key):
# 8바이트 랜덤한 IV(Initialization Vector) 생성
iv = get_random_bytes(8)
# 3DES CBC 모드로 암호화
cipher = DES3.new(key, DES3.MODE_CBC, iv)
ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), 8))
return iv + ciphertext
def decrypt_3des(ciphertext, key):
# IV 추출
iv = ciphertext[:8]
# 3DES CBC 모드로 복호화
cipher = DES3.new(key, DES3.MODE_CBC, iv)
decrypted_text = unpad(cipher.decrypt(ciphertext[8:]), 8)
return decrypted_text.decode('utf-8')
if __name__ == "__main__":
# 24바이트의 키 생성 (3개의 8바이트 키를 사용)
key = get_random_bytes(24)
# 암호화할 평문
plaintext = "Hello, 3DES CBC!"
# 암호화
encrypted_data = encrypt_3des(plaintext, key)
print("Encrypted:", encrypted_data.hex())
# 복호화
decrypted_data = decrypt_3des(encrypted_data, key)
print("Decrypted:", decrypted_data)
그 외에도 SEED, ARIA 등 다양한 대칭키 암호화 알고리즘이 존재합니다.