Symmetric Key - DES, AES

KyungH·2025년 1월 5일
0

Cyber-Security

목록 보기
16/27

📝 대칭키 암호 - DES, AES 블록 암호

대칭키 암호란 암호화에 사용되는 암호키와 복호화에 사용되는 암호키가 동일
암호화 기법이다. 대칭키 암호 방식으로 암호화한 정보를 누군가에게 보낼 때에는
암호키도 같이 보내야 하는데, 이때 암호키는 암호화가 되지않은 평문이다.
따라서 네트워크를 통해 암호키를 전달할 때 어떤 방법으로 안전하게 전달할 것인가가
문제가 되므로 송신과 수신하는 쪽이 상호 합의된 비밀 경로를 이용해야 한다.

대칭키 암호의 암호화 방식은 데이터를 변환하는 방법에 따라
블록암호, 스트림 암호로 구분된다.

이 글에서는 대표적인 블록암호인 DES와 AES에 대해 알아보겠다.


📌블록 암호란?

블록 암호는 고정된 크기의 블록 단위로 암호화, 복호화 연산을 수행하는
대칭키 암호화 방식이다.

블록 암호의 알고리즘

블록 암호 알고리즘은 평문 블록을 암호 블록으로 만들 때 적용되는 방식에 따라
파이스텔 블록 구조와 SPN 블록 구조로 구분된다.

▫️파이스텔 블록 구조

파이스텔 블록 구조는 평문 블록을 좌우 두 개 블록으로 분할하여 좌측 블록을
파이스텔 함수라 불리는 라운드 함수를 적용하여 출력된 결과를 우측 블록에
적용하는 과정을 반복적으로 수행한다.

  • Li+1 = Ri
  • Ri+1 = Li ⊕ F(Ri, Ki)

이후 마지막에 L과 R을 결합하여 암호화를 완료한다.

💡 파이스텔 구조는 키 순서만 반대로 적용하면
같은 구조를 사용하여 암호화와 복호화를 수행할 수 있다.

파이스텔 구조를 채택한 블록암호는 DES, Blowfish, Twofish, SEED 등이 있다.

▫️SPN 블록 구조

SPN 블록 구조는 평문 블록을 분할하지 않고 전체 블록에 암호화를 적용한다.
여러 라운드 동안 S-Box(대체)와 P-Box(치환)를 조합하여 병렬처리 하므로
암호화와 복호화가 대칭적이지 않으며 라운드 함수의 역함수를 구해야 한다.

SPN 블록 구조를 채택한 블록 암호는 AES, IDEA, SHARK, ARIA 등이 있다.

블록 암호의 운영 모드

암호화 대상 정보가 블록 크기보다 큰 경우 각 블록에 대해 어떤 방식으로
암호화를 수행하느냐에 따라 Electronic Codebook(ECB), Cipher Block Chaining(CBC),
OFB, CFB, CTR 등 다양한 운영 모드가 사용된다.

ECB 모드에서 암호화

이 모드에서는 각 블록들이 암호키를 이용하여 독립적으로 암호화 된다.
여기서의 가장 큰 약점은 동일한 내용의 블록은 동일한 암호 블록으로 암호화가
된다는 것이다.

만약 어떤 정보가 일정한 패턴을 가지고 있을 경우 ECB 모드는 이 패턴을 암호화하기
어려우며, 특히 이미지 데이터를 암호화하면 이 효과가 두드러지게 나타난다.

CBC 모드에서 암호화

CBC 모드는 블록을 암호화하기 전에 이전 블록의 암호화된 블록과
XOR 연산을 한 결과를 새로운 암호키로 해서 블록을 암호화하는 방식이다.
이러한 경우 가장 첫 번째 블록은 이전 블록이 없으므로 이 역할을 하는
초기화 백터(Initialization Vector, IV) 가 필요하다.


📌DES (Data Encryption Standard)

DES는 1970년대 IBM에서 파이스텔 블록구조에 기반하여 설계되고 개발된
56비트키 암호화 알고리즘이다. 다소 크기가 작은 암호키로 인해 현재는
보안성이 결여되어 사용하지 않고, 이를 보완한 3DES가 암호화를 위해
블록당 3번의 DES를 수행한다.

구현은 파이썬의 Pycrypto 패키지를 사용하였다.

import Crypto

from Crypto.Cipher import DES3  # 3DES 라이브러리 사용
from Crypto.Hash import SHA256 as SHA  # 암호키와 초기화 백터를 위한 SHA256 알고리즘


class myDES:
    # 클래스 생성자, 3DES 암호키를 생성을 위한 keytext, 초기화 백터를 위한 ivtext
    def __init__(self, keytext, ivtext):
        hash = SHA.new()
        hash.update(
            keytext.encode("utf-8")
        )  # SHA256.update()는 유니코드 문자열을 받지 않음
        key = hash.digest()  # 추출된 해시값은 32바이트
        self.key = key[:24]  # Pycryptodome에서 제공하는 3DES의 크기는 16,24 바이트

        hash.update(ivtext.encode("utf-8"))
        iv = hash.digest()
        self.iv = iv[:8]

    def enc(self, plaintext):
        # 3DES 객체 생성, 운영모드에 따라 IV가 필요할수도, 없을 수도 있다
        des3 = DES3.new(self.key, DES3.MODE_CBC, self.iv)
        encmsg = des3.encrypt(plaintext.encode())
        return encmsg

    def dec(self, ciphertext):
        # 암호화와 복호화를 위한 DES객체는 각각 생성해야 한다
        # 전역으로 생성하여 사용할 경우 제대로된 복호화 결과가 나오지 않음
        des3 = DES3.new(self.key, DES3.MODE_CBC, self.iv)
        decmsg = des3.decrypt(ciphertext)
        return decmsg


def main():
    keytext = "samsjang"
    ivtext = "1234"
    msg = "python3x"

    myCipher = myDES(keytext, ivtext)
    ciphered = myCipher.enc(msg)
    deciphered = myCipher.dec(ciphered)
    print("ORIGINAL:\t%s" % msg)
    print("CIPHERED:\t%s" % ciphered)
    print("DECIPHERED:\t%s" % deciphered)


main()

위 코드에서 keytext는 우리가 기억하는 암호키, 즉 이를 해시함수로 변환한 값에서
24바이트를 사용하여 암호화의 키로 사용한다. ivtext는 같은 맥락으로
초기 블록의 암호화에 사용하기 전 XOR 연산에 사용할 IV를 ivtext의 해시값에서
8바이트를 사용하여 생성한다.

예시에서는 ivtext 에 하나의 고정된 문자열을 대입하였으나,
실제로 이렇게 동작시킬경우 해시함수의 특성상 항상 동일한 IV가 생성된다.
그렇게되면 동일한 평문에 대해 동일한 암호문이 생성되는 문제가 발생한다.

따라서 실제로는 IV를 랜덤하게 생성하여 동일한 평문에도 다른 암호문이 생성되도록
해야 보안을 강화할 수 있다.

💡데이터 무결성 보장하기

위 코드를 실행시키면 다음과 같은 결과를 얻을 수 있다

ORIGINAL:       python3x
CIPHERED:       b'S\x9d\xf2\xfe#\xf7\xfa\x06'
DECIPHERED:     b'python3x'

결과에서 b'로 표시된 것은 문자열이 아닌 바이트 객체라는 것을 나타내며,
이것은 유니코드가 아니라 이진데이터임을 뜻한다.

위의 코드에서 msg을 10문자로 구성된 문자열로 바꾸어 실행할 경우
다음 오류가 발생한다.

ValueError: Data must be padded to 8 byte boundary in CBC mode

즉, 암호화하려는 메시지 길이는 8바이트의 배수이어야 한다. 이에 따르면
우리가 사용하는 대다수의 평문을 암호화하기엔 조금 번거롭다.

강제로 문자 '0'을 뒤에 붙여 8배수로 만들면 본래의 내용을 유지하면서
조건을 만족시킬 순 있으나, 이는 원문이 텍스트가 아닌 바이너리로 되어있을 경우
복호화 한 정보의 길이가 원래 정보와 다르다면 이는 복호화된 파일이 변조되었을
수 있으므로 옳지 않은 해결방법이다.

그렇다면 원래 정보에 추가된 문자 '0'의 개수에 대한 정보를 포함시킨 후
암호화를 진행하면 된다. 즉, 추가된 '0'의 개수를 나타내는 헤더를 앞에 포함시켜
헤더 + 원래 정보 + 추가된 '0' 문자열 형태를 한번에 암호화 하면된다.
헤더는 8바이트로 구성하며 추가된 '0' 개수 만큼 자릿수에 숫자를 기록하고
나머지는 #으로 채운다. (EX. 0이 2개 추가된 경우, 숫자/숫자/######)

이후 복호화 과정에서 첫 8바이트를 잘라내고 '#' 문자 앞까지 읽어
추가된 '0'의 개수를 확인하여 남아있는 데이터에서 '0' 개수를 제외한 정보를 읽는다.

또한 이러한 데이터의 무결성을 보장하기 위해선 해시를 이용하여 해결할 수 있다.
해시에 대해서는 다른 글에서 설명하도록 하겠다.


📌AES (Advanced Encryption Standard)

레인달 암호화 알고리즘으로 불리는 AES 암호 표준은 DES와 달리 SPN 블록 구조를
채용하였고, 키는 128비트, 192비트, 256비트를 지원하며 블록 크기는 128비트이다.

AES 알고리즘에 대한 공격 방법은 다양하게 알려져 있으나 아직까지 완전히
해독된 사례가 없어 안전성이 보장된 알고리즘으로 알려져 있다.

코드는 키 크기(128, 192, 256비트), 블록 크기(128비트)가 다른 것 외에
3DES와 동일하다.

0개의 댓글

관련 채용 정보