파이썬 AES암호화 알고리즘 코드

암호화 알고리즘에는 다양한 종류가 있습니다. 미국 국립표준기술연구소(NIST)에서 표준화한 대칭 블록 방식의 암호인 AES (Advanced Encryption Standard)에 대해서 알아보겠습니다. AES는 미 국가안보국에 의해 1급비밀(Top Secret)에 사용할 수 있도록 승인된 알고리즘 중 최초로 공개되어 있는 알고리즘입니다.



 

모듈 설치

파이썬에서 사용할 수 있는 PyCryptodome 암호화 라이브러리를 사용해보겠습니다.
 

pip install pycryptodome

터미널에 다음 코드를 붙여 넣어 설치하실 수 있습니다.
 
 

필요 모듈 불러오기

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes

AES암호화를 하기 위해  PyCryptoDome 라이브러리에서 필요한 모듈을 불러옵니다.
 

AES.py (AES모듈)

PyCryptoDome 에는 AES알고리즘을 제공합니다.
 
 

AES.py (AES모듈)

pad, unpad는 AES알고리즘의 고정된 데이터 블록 크기는 16바이트(1byte = 8bit)로 구성되어 있으며 AES암호화를 진행하기 위해 원본 데이터의 크기가 16바이트의 배수가 아닌 경우에는 남는 공간을 패딩작업을 통해 채워줘야 합니다. 때문에 이 작업을 위해 사용할 패딩과 복호화 시에 언패딩 함수를 사용합니다.
 

get_random_bytes함수는 랜덤 한 바이트를 생성하는데 사용이되며 이를 활용하여 랜덤한 키를 생성합니다.
 
 

AES 키 생성

key = get_random_bytes(16)

key 변수를 생성하고 get_random_bytes()로 암호화에 사용될 랜덤한 바이트의 key값을 생성해 줍니다.
이때 인자값으로 16이 들어간 건 16바이트의 키를 생성한다는 의미입니다. (컴퓨터에서 1바이트는 8비트로 구성되므로, 16바이트는 16*8= 128비트가 됩니다. 16 bytes = 128bit)
영문 한 개에 1바이트로 인식되기 때문에 16자리의 암호키값을 직접 작성하여도 됩니다. 다만 보안 측에는 위험합니다.
생성된 128비트의 랜덤 한 키는 AES-128 암호화에 사용됩니다.
 
AES키의 종류16바이트(AES-128), 24바이트(AES-192), 32바이트(AES-256)중 하나여야 합니다.
키의 길이가 이 세 가지 중 하나가 아니라면, AES 알고리즘이 올바르게 작동하지 않습니다.
만약 AES-192를 사용하려면 get_random_bytes(24)를 사용하여 192비트 키를 생성할 수 있습니다.
 
 

AES데이터 준비

AES암호화에 사용될 데이터를 준비해줘야 합니다.
암호화할 데이터는 이미지나 파일이나 문자열등 다양하게 사용할 수 있지만 지금은 문자열을 사용해 보겠습니다.
다음 코드를 참고하여 data 변수를 생성한 뒤 "Helo, world!"라는 문자열을 넣은 뒤 인코딩해주면 됩니다.
 

data = "Hello, World!".encode('utf-8')

data변수에 문자열을 넣은 뒤에 따로 utf-8로 인코딩하는 이유는 python에서 문자열은 유니코드 문자들로 구성되지만 컴퓨터에서 처리할 수 있는 형태인 바이트 단위로 바꿔주기 위해서. endcode('utf-8') 메서드를 통해 유니코드 문자열을 utf-8 인코딩 방식으로 바이트 배열로 변환합니다.
 
 

바이트배열 출력

여기서 재밌는 사실이 있는데, data 변수에 담긴 영문 문자열을 print문으로 출력하면 아래와 같이 나옵니다.

data = "Hello, World!".encode('utf-8')
print(data)  # b'Hello, World!'

우리가 생각하는 바이트 배열로 문자가 나오는 게 아니라 b'Hello, world!'라고 나오는 이유는 UTF-8로 인코딩한 후 각 글자가 해당하는 ASCII값으로 변환되어 출력해 주기 때문에 사용하였던 문자열과 동일하게 됩니다.
 

data = "서울".encode('utf-8')
print(data)  # b'\xec\x84\x9c\xec\x9a\xb8'

반면에 한글 문자열을 UTF-8로 인코딩하면 두 개 이상의 바이트로 변환되고 Ascii범위 밖에 있어서 16진수 숫자를 사용하여 ASCII코드를 표현하는 \Xhh 형식으로 출력됩니다.
 
 

AES 객체 생성 및 암호화

cipher = AES.new(key, AES.MODE_CBC)

cipher

cipher 변수에는 AES.new() 함수를 사용하여 새로운 암호화 객체를 생성합니다. 이 함수는 암호화키와 블록 암호 운영모드에 대한 두 가지의 매개 변수를 받습니다.
 

AES.py (AES모듈)

key는 아까 위에서 생성하였고, 사용가능한 블록 암호 운영모드를 선택하면 됩니다.
AES블록 암호에서는 데이터를 고정된 크기의 블록으로 나누어서 각각 암호화하는데 각 블록을 어떻게 처리할지 결정하는 게 바로 운영 모드입니다. 블록 암호 운영모드 선택은 여러 사용용도에 따라서 다르지만 이번에는 CBC를 사용하겠습니다.  ECB 모드에서 같은 평문 블록은 항상 같은 알고리즘 결과를 생성하는데 CBC는 다르기 때문에 패턴 인식 공격등 보안성이 상대적으로 좋습니다. 
 
CBC 모드의 경우 원래 초기 iv값이 필요하지만 위에 AES.new() 코드를 실행할 때 CBC 모드의 AES 암호화 객체가 생성되고 이 객체는 자동으로 IV를 생성하기 때문에 별도의 IV를 지정하지 않아도 됩니다.

iv는 초기 벡터(Initialization Vector)를 말하는데 블록암호의 운영모드 중 일부(CBC, CFB, OFB) 등 에서 사용되는 값으로 첫 번째 블록을 암호화할 때 필요합니다. 이런 운영 모드들은 각 블록의 암호화가 이전 블록의 결과에 의존하는데 첫 번째 블록은 이전에 암호화된 블록이 없으므로 iv를 사용해 줍니다.
 

ct_bytes

ct_bytes = cipher.encrypt(pad(data, AES.block_size))

cipher.encrypt(pad(data, AES.block_size)) 코드를 사용하여 패딩 된 데이터를 알고리즘으로 암호화합니다.
(pad(data, AES.block_size) 코드는 원본 데이터에 패딩을 추가하는 작업을 합니다. 원본 데이터가 AES 블록 크기인 16배 수로 맞추어지도록 합니다. 원본 데이터 블록의 크기가 16 배수가 아닌 경우 AES알고리즘은 정상적으로 작동하지 않습니다.
 
cipher.encrypt() 해당 함수를 사용하면 cipher 변수에서 입력받은 암호화 키와 블록암호 운영모드를 사용하여 pad 함수로 패딩 된 데이터를 실제로 암호화하는 작업을 수행합니다.
 

초기화 벡터(iv), 암호문 저장

iv = cipher.iv
ct = ct_bytes

iv변수에 iv값을 저장해 줍니다.
 
pycryptodome 라이브러리를 사용해서 암호화 시에 AES.new(key, AES.MODE_CBC)를 호출할 때 자동으로 초기화 벡터(IV) 값을 생성해 주기 때문에 따로 지정해주지 않아도 자동으로 생성해 주고, 해당 IV값은 cipher.iv 속성을 통해 접근할 수 있습니다. 복호화 시에도 이 iv값이 필요합니다.
 
ct변수에는 암호화한 암호문(ct_bytes)이 담긴 변수를 다른 변수(ct)로 재할당합니다.
 
 

복호화

cipher2 = AES.new(key, AES.MODE_CBC, iv=iv)
pt = unpad(cipher2.decrypt(ct), AES.block_size)

cipher2는 복호화를 위한 AES 객체를 새로 생성하며 암호화 시에 사용한 key값과 동일한 블록 암호화 모드를 사용합니다. 복호화에 사용할 iv값은 암호화시에 자동으로 생성된 iv를 iv변수에 재할당 하였으므로 iv값을 입력하면 됩니다.
 
pt변수에는 복호화된 문자열이 담길 변수입니다. unpad를 통하여 복호화를 진행합니다.
새로 생성한 복호화 AES객체를 통하여 암호문(ct)을 복호화합니다. AES.block_size는 고정된 데이터 블록 크기인 16 바이트입니다.
 
 

복호문 출력

print(f"decrypted message is:{pt.decode('utf-8')}")

이후에 print문으로 복호화된 복호문을 출력합니다. 이때 인코딩되어있는 복호문을 다시 디코딩하여 출력합니다.
 
 

전체코드

#필요한 모듈 호출
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes

#키 생성
key = get_random_bytes(16)

#암호화 데이터 인코딩
data = "Hello, World!".encode('utf-8')

#암호화 객체 생성 및 암호화
cipher = AES.new(key, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(data, AES.block_size))

#초기화 벡터 iv, 암호문 ct
iv = cipher.iv
ct = ct_bytes

#복호화 객체 생성 및 복호화
cipher2 = AES.new(key, AES.MODE_CBC, iv=iv)
pt = unpad(cipher2.decrypt(ct), AES.block_size)

#복호문 디코딩후 출력
print(f"decrypted message is:{pt.decode('utf-8')}")

 

전체 코드는 깃허브에서도 확인할 수 있습니다.

 

GitHub - Blue-B/AES-Algorithm

Contribute to Blue-B/AES-Algorithm development by creating an account on GitHub.

github.com

 

Top