본문 바로가기
ETC/Python

CryptoJS AES Encrypt Python Version

by Guardy 2024. 10. 16.
728x90

CryptoJS를 이용하여 AES 암호화하는 코드는 다음과 같다.

https://www.npmjs.com/package/crypto-js#plain-text-encryption

 

crypto-js

JavaScript library of crypto standards.. Latest version: 4.2.0, last published: a year ago. Start using crypto-js in your project by running `npm i crypto-js`. There are 12944 other projects in the npm registry using crypto-js.

www.npmjs.com

var encrypted = CryptoJS.AES.encrypt(
      'test', // 암호화하고싶은 text
      '12345678901234567890123456789012', // key
      {
        iv: CryptoJS.enc.Utf8.parse('1234567890123456'), //iv
        padding: CryptoJS.pad.Pkcs7,
        mode: CryptoJS.mode.CBC,
      }
    );
console.log(encrypted.toString());

다음의 결과는 로그를 찍어보면

다음과 같은 값이 나온다.

이제 이를 PyCryptodome 라이브러리를 이용한 파이썬 코드로 바꿔주면 다음과 같이코드를 작성할수 있다.

https://pycryptodome.readthedocs.io/en/latest/src/util/util.html#module-Crypto.Util.Padding

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

def encrypt_aes_key(data, key):
    key = key.encode('utf-8')
    iv = b'1234567890123456'

    padded_data = pad(data.encode('utf-8'), AES.block_size)

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

    encrypted = cipher.encrypt(padded_data)

    encrypted_base64 = base64.b64encode(encrypted).decode('utf-8')

    return encrypted_base64

AESKEY = '12345678901234567890123456789012'  # 32바이트 키
str_to_encrypt = "test"  # 암호화할 문자열

# 암호화 수행
enc_str = encrypt_aes_key(str_to_encrypt, AESKEY)
print(enc_str)

같은 AES Key와 Text 그리고 IV까지 맞췄는데도 결과가 다르다.

왜그럴까

바로 Salt라는 녀석때문이다. 

CryptoJS 라이브러리의 경우 Salt라는 값을 추가하는데

Salt는 암호화나 해시 과정에서 데이터의 보안을 강화하기 위해 추가되는 임의의 값이다.

Salt를 사용하면 동일한 비밀번호나 데이터도 매번 다른 암호화 결과가 나오게 되어, 암호화된 데이터로부터 원본 값을 추측하기 어려워지기때문에 사용한다.

그래서 우리는 CryptoJS와 같은 방식으로 파이썬 코드를 다시 다음과 같이 작성해줘야한다.

import base64
from Crypto.Hash import MD5
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import secrets

def encrypt(plaintext, password):
    salt = secrets.token_bytes(8)

    derived = b""
    while len(derived) < 48: 
        hasher = MD5.new()
        hasher.update(derived[-16:] + password.encode('utf-8') + salt)
        derived += hasher.digest()

    key = derived[0:32]
    iv = derived[32:48]

    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(plaintext.encode('utf-8'), 16))

    encrypted_bytes = base64.b64encode(b'Salted__' + salt + encrypted)
    return encrypted_bytes.decode('utf-8')

plaintext = "test"
password = "12345678901234567890123456789012"
encrypted = encrypt(plaintext, password)
print("Encrypted ciphertext (base64):", encrypted)

다음 코드를 실행하면 다음과 같은 결과가 나온다.

Salt가 추가되었기 때문에 값이 다르긴하지만 복호화했을때는 같은 값으로 나온다!

백엔드와 프론트엔드에서 Python과 JS 다른 두 언어로 AES 암호화/복호화를 했어야했는데

계속 다른 형식으로 나와 오류가 났는데, 삽질을 하다가 결국 해결 완료했다.

 

728x90