결제 API 연동 개발을 할 때, AES/CBC/PKCS5Padding 암호화를 사용합니다. 

PHP 코드를 참고하여 파이썬으로 이용 가능한 코드를 구현했습니다.

# pip install cryptography
import base64

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend


class SEEDCBC:
    def __init__(self, iv, key):
        self.iv = bytes(iv, 'utf-8')
        self.key = base64.b64decode(key.encode('utf-8'))
        self.cipher = Cipher(algorithms.SEED(self.key), modes.CBC(self.iv), backend=default_backend())
        pass

    def pad(self, s: str):
        BLOCK_SIZE = 16
        return s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)

    def encrypt(self, plain_text):
        encryptor = self.cipher.encryptor()

        padded = self.pad(plain_text)  # Ensure plaintext is padded to 16 bytes (block size)

        result = encryptor.update(padded.encode()) + encryptor.finalize()
        return base64.b64encode(result).decode()
    
    def decrypt(self, b64_text):
        decryptor = self.cipher.decryptor()
        unpad = padding.PKCS7(128).unpadder()

        plain_text = base64.b64decode(b64_text.encode('utf-8'))

        enc = decryptor.update(plain_text) + decryptor.finalize()
        result = unpad.update(enc) + unpad.finalize()
        return result.decode()

s = SEEDCBC(iv='16자리 문자열', key='해당 암호화로 encrypt된 문자열')
enc = s.encrypt('안녕하세요')
result = s.decrypt(enc) # 안녕하세요

 

참고한 php 코드

// 문자열 Base64 복호화 후 16진수 byte 변환
function getBytesBase64($base64String) {
    $decodedData = base64_decode($base64String);
    $byteArray = [];
    for ($i = 0; $i < strlen($decodedData); $i++) {
        $byteArray[] = ord($decodedData[$i]);
        if ($byteArray[$i] > 127) {
            $byteArray[$i] -= 256;
        }
    }
    return $byteArray;
}

// 문자열 16진수 byte 변환
function getBytes($str) {
    $byteArray = unpack('C*', $str);
    $byteValues = [];
    foreach ($byteArray as $byte) {
        $byteValues[] = $byte;
    }
    return $byteValues;
}

// SEED/CBC/PKCS5Padding 암호화
function encrypt($bszIV, $bszUser_key, $str) {
    $planBytes = $this->getBytes($str);
    $keyBytes = $this->getBytesBase64($bszUser_key);
    $IVBytes = $this->getBytes($bszIV);
    $message_offset = 0;
    $message_length = count($planBytes);
    $bszChiperText = KISA_SEED_CBC::SEED_CBC_Encrypt($keyBytes, $IVBytes, $planBytes, $message_offset, $message_length);
    $string = '';
    foreach ($bszChiperText as $byte) {
        $string .= chr($byte);
    }
    $base64EncodedString = base64_encode($string);
    return $base64EncodedString;
}

// SEED/CBC/PKCS5Padding 복호화
function decrypt($bszIV, $bszUser_key, $str) {
    $planBytes = $this->getBytesBase64($str);
    $keyBytes = $this->getBytesBase64($bszUser_key);
    $IVBytes = $this->getBytes($bszIV);
    $message_offset = 0;
    $message_length = count($planBytes);
    $bszPlainText = null;
    $bszPlainText = KISA_SEED_CBC::SEED_CBC_Decrypt($keyBytes, $IVBytes, $planBytes, $message_offset, $message_length);
    return implode(array_map('chr', $bszPlainText));
}
  • getBytesBase64(), getBytes() 에서의 차이점
    # 파이썬으로 나타낸 해당 함수
    
    # getBytesBase64()
    for i in base64.b64decode(b64_text.encode('utf-8')):
        print(i)  # ord가 128부터는 ’\x80’인데 여기서는 -256을 한다.
    
    # getBytes()
    for i in bytes(bytes_text, 'utf-8'):
        print(i)
     
  • 파이썬에서는 해당 함수를 따로 구현하지 않아도 된다.

 

+ Recent posts