본문 바로가기
Programming/Python

파이썬 AES 암호화 파일 예제

by whitele 2021. 7. 23.
반응형

 

파이썬 AES 파일 암호화

AES 암호는 대칭키 암호화로 현재 대중적으로 사용되는 암호화 방식 중 하나이다. 여러 암호화 모드를 적용할 수 있다. Python에서는 AES를 사용하기 위해서 pycrypto 모듈을 설치해야 한다.

 리눅스를 기준으로 'pip install pycryptodomex'를 명령하면 설치가 진행된다. 사용 전 SHA 해시를 사용해 초기화 벡터와 키를 sha로 해싱 후 키로 사용할 것이다. 해시는 파이썬 자체로 hashlib 있으나 pycrypto를 이용해 사용할 것이다.

 

 

해시 설명

hash는 일종의 대응 함수이다. 해시는 빠른 검색을 위해 사용되었고 이후 역상 저항성을 이용해 암호 및 보안에도 이용된다. 주로 MD, SHA 계열이 있고 SHA는 버전에 따라서 알고리즘이 다른 경우가 있다. 암호로 사용되지 않는 해시도 있다.

 MD5의 경우 이미 해시가 풀려 암호학적으로 이용하기에는 안전하지 않은 해시이다. 대부분 느려도 안전한 SHA를 사용하며 이외 다양한 해시 알고리즘이 사용된다. 예제에서는 SHA256 코드를 사용하여 암호화를 진행한다.

 pycrypto를 이용하여 SHA 함수를 사용하고 crypto->hash 모듈에 있다.

SHA 256 

from Crypto.Hash import SHA256
hash=SHA256.new()

 새로운 sha256 해시 객체 생성

hash.update(data)

새로운 해시 데이터 적용

hv=hash.digest()

해시값 (hash value) 반환

 

AES 클래스

AES.new()
AES.new(key,mode, iv)

다음을 일반적을 사용

 

key : key 값 AES는 128 192 256의 키 길이를 사용할 수 있다.

mode : 암호 mode를 입력하며 기본 값으로 ECB 모드이다.

IV : 초기화 벡터를 입력하는 인수 ECB모드와 CTR모드는 사용되지 않음

 

 

from Crypto.Cipher import AES
from Crypto.Hash import SHA256 as SHA
import struct
from os import path

class fileAES():
    def __init__(self, keytext, out_filename):
        hash = SHA.new()
        hash.update(keytext.encode('utf-8'))
        key = hash.digest()
        self.key = key[:16]

        iv_text = 'iv data'
        hash.update(iv_text.encode('utf-8'))
        iv = hash.digest()
        del iv_text
        self.iv = iv[:16]
        self.out_filename = out_filename

    def encrypt_file(self, filename, blocksize=65536):
        aes = AES.new(self.key, AES.MODE_CBC, self.iv)
        filesize = path.getsize(filename)
        if self.out_filename == None:
            out_filename = filename[0:len(filename)-4]+'.aef'
        else:
            out_filename = self.out_filename
        with open(filename, 'rb') as origin:
            with open(out_filename, 'wb') as ret:
                ret.write(struct.pack('<Q', filesize))
                while True:
                    block = origin.read(blocksize)
                    # print(block)
                    if len(block) == 0:
                        break
                    elif len(block) % 16 != 0:
                        block += b'0'*(16 - len(block) % 16)
                    ret.write(aes.encrypt(block))

    def decrypt_file(self, filename, file_exp, blocksize = 1024):
        with open(filename, 'rb') as origin:
            filesize = struct.unpack('<Q', origin.read(struct.calcsize('<Q')))[0]
            aes = AES.new(self.key, AES.MODE_CBC, self.iv)
            filename = filename[0:len(filename)-3] + file_exp
            with open(filename, 'wb') as ret:
                ret.write(aes.decrypt(origin.read(16)))
                while True:
                    block = origin.read(blocksize)
                    if len(block) == 0:
                        break
                    ret.write(aes.decrypt(block))
                ret.truncate(filesize)

 

encrypt_file 메서드

 encrypt_file 메서드는 AES 암호화를 하기 위한 메서드이다. AES 기능을 제공한다.

aes=AES.new(slef.key,AES.MODE_CBC,self.iv)로 CBC 모드로 암호화하는 객체를 생성한다.

 

with open(filename,'rb') as origin:
	with open(out_filename,'wb') as ret:

 바이너리 읽기로 원본 파일을 부르고 별칭을 origin으로 한다. out_filename으로 지정된 파일을 바이너리 쓰기로 새 파일을 생성하며 별칭은 ret으로 함.

 

ret.write(struct.pack('<Q',filesize))

filesize를 c의 구조체 형식으로 저장하는 것이다. <는 리틀 엔디안을 뜻하며 Q는 long long 자료형으로 구조체를 정의한 것이다. 리틀 엔디안인지 빅 엔디안인지는 직접 확인해봐야 한다. unpack 할 때 역시 같은 엔디안 방식으로 unpack 하므로 여기서는 크게 상관없다.

 

 

while 문 내부

 블록 단위로 원본 파일을 읽고 만약 없다면 종료한다. 16의 배수가 아니라면 0으로 채운다. 이것을 패딩(padding)이라고 한다. 반복문 마지막에 블록을 암호화한다.

 완성된 암호화 file 구조는 다음과 같다.

ex.aef 구조

decrypt_file 메서드

 암호화된 파일을 복호화하는 메서드이다. encrypt_file 메서드와 달리 암호화 파일이 origin이고 복호화 파일이 ret이다.

 

filesize = struct.unpack('<Q', origin.read(struct.calcsize('<Q')))[0]

이 코드에 처음 파일 크기를 구조체로 저장했던 것을 푸는 것이다. 그 후 1024 블록 단위로 복호화를 진행한다. 블록단위는 16의 배수라면 상관없다. 마지막 trucate 함수는 filesize 만큼 패딩을 지우기 위해 자르는 함수이다.

 

 

다음 예제를 위해 참조한 코드

 

728x90
반응형

댓글