본문 바로가기
Python tips

KISA 128bit SEED CBC 암호화 복호화

by 스티브 십잡스 2024. 4. 16.

결제 관련 외부 API를 연동할 때, 해당 암호화 방식을 사용해야 하는 경우가 있습니다.

파이썬 코드를 제공하는 곳을 아직 찾지 못해 PHP 코드를 참고하여 구현했습니다.

# pip install cryptography


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

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()에 있다.
  • PHP 해당 함수를 파이썬으로 나타내보면, 아래와 같다.
    # 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)
    • 위 함수를 파이썬에서는 구현하지 않아도 된다.