他言語と相互間の暗合複合を考慮の AES 暗合複合 Python のコード

本記事よりも、2019-3-2 に書き改めた方を参照すべし。。
oboe2uran.hatenablog.com




先日書いた、
Java と Python 相互で AES 256 暗号/複合を実行する - Oboe吹きプログラマの黙示録
やはり、Python のコードを改める。
複合メソッド、Base64 受け取りとHex文字列受け取りが分かれるより引数で指定することにする。

名称も、どうせ 256 bit 長しか今後も使わないので、cryptoaes256.py でなくて、cryptoaes.py にする。
複合メソッドで渡す形式は文字列型だけにする

# -*- coding: UTF-8 -*-
# AES 256 暗合複合
#   使い方:
#     from cryptoaes import AESCipher
#     CBCモードインスタンス
#           aes = AESCipher(password, bytes.fromhex(iv_hex_string))
#     ECBモードインスタンス
#           aes = AESCipher(password)
#
#     CBCモード 暗合化 → Base64 エンコード
#            enctxt = b64encode(aes.encryptCBC(planetxt)).decode('utf-8')
#     CBCモード 複合 ← Base64エンコード済の暗号文
#            dectxt = aes.decryptCBC(encted_b64_string)
#     CBCモード 複合 ← 16進 Hex表現 の暗号文
#            dectxt = aes.decryptCBC(encted_hexstring, type='hex')
#
#     ECBモード 暗合化 → Base64 エンコード
#            enctxt = b64encode(aes.encryptECB(planetxt)).decode('utf-8')
#     ECBモード 複合 ← Base64エンコード済の暗号文
#            dectxt = aes.decryptECB(encted_b64_string)
#     CBCモード 複合 ← 16進 Hex表現 の暗号文
#            dectxt = aes.decryptECB(encted_hexstring, type='hex')
#
import re
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
from base64 import b64encode, b64decode

BLOCK_SIZE = AES.block_size
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]

class AESCipher:
    def __init__(self, password, iv=Random.new().read(AES.block_size)):
        self.key = hashlib.sha256(password.encode("utf-8")).digest()
        self.iv = iv
    # AES 256キー参照
    def getKey(self):
        return self.key
    # ベクトル値取得
    def getIV(self):
        return self.iv
    # password & ベクトル値セット
    def setPasswdAndIV(self, passandiv):
        self.key = hashlib.sha256(passandiv[0].encode("utf-8")).digest()
        self.iv = passandiv[1]

    # CBC モード暗号化  plane → byte
    def encryptCBC(self, message):
        if message is None or len(message) == 0:
            raise NameError("No value given to encrypt")
        raw = b64encode(message.encode('utf-8')).decode()
        raw = pad(raw)
        raw = raw.encode('utf-8')
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        return cipher.encrypt(raw)

    # CBC モード複合  encrypted  base64 or HEX → plane
    def decryptCBC(self, enctext, type='b64'):
        if enctext is None or len(enctext) == 0:
            raise NameError("No value given to decrypt")
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        if type=='b64':
            return b64decode(cipher.decrypt(b64decode(enctext))).decode()
        elif type=='hex':
            re.sub(b"([\x00-\x08\x0b\x0c\x0e-\x1f])*$", b'', cipher.decrypt(bytes.fromhex(enctext))).decode()
        else:
            raise TypeError("type Error must be 'b64' or 'hex'")

    # ECB モード暗号化  plane → byte
    def encryptECB(self, message):
        if message is None or len(message) == 0:
            raise NameError("No value given to encrypt")
        raw = b64encode(message.encode('utf-8')).decode()
        raw = pad(raw)
        raw = raw.encode('utf-8')
        cipher = AES.new(self.key, AES.MODE_ECB)
        return cipher.encrypt(raw)

    # ECB モード複合  encrypted  base64 or HEX → plane
    def decryptECB(self, enctxt, type='b64'):
        if enctxt is None or len(enctxt) == 0:
            raise NameError("No value given to decrypt")
        cipher = AES.new(self.key, AES.MODE_ECB)
        if type == 'b64':
            return b64decode(cipher.decrypt(b64decode(enctxt)).decode()).decode()
        elif type == 'hex':
            re.sub(b"([\x00-\x08\x0b\x0c\x0e-\x1f])*$", b'', cipher.decrypt(bytes.fromhex(enctext))).decode()
        else:
            raise TypeError("type Error must be 'b64' or 'hex'")

# password と iv を再セットするための タプルを生成
def createPasswordAndIV():
    import random
    import string
    return  (''.join(random.choices(string.ascii_letters + string.digits, k=16)), Random.new().read(AES.block_size) )

次は、Java の方の見直しだ。