[python] 암호에 따라 문자열을 인코딩하는 간단한 방법?

파이썬에는 암호를 사용하여 문자열을 인코딩 / 디코딩하는 내장 된 간단한 방법이 있습니까?

이 같은:

>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'

따라서 문자열 “John Doe”는 ‘sjkl28cn2sx0’으로 암호화됩니다. 원래 문자열을 얻으려면 소스 코드의 암호 인 ‘mypass’키를 사용하여 해당 문자열을 “잠금 해제”합니다. 암호를 사용하여 Word 문서를 암호화 / 복호화 할 수있는 방법이 되었으면합니다.

이 암호화 된 문자열을 URL 매개 변수로 사용하고 싶습니다. 내 목표는 강력한 보안이 아니라 난독 화입니다. 미션 크리티컬 한 것은 인코딩되지 않습니다. 데이터베이스 테이블을 사용하여 키와 값을 저장할 수 있다는 것을 알고 있지만 최소화하려고 노력하고 있습니다.



답변

당신이 유일한 으로부터 물건을 모호하게됩니다 간단한 난독 찾고 매우 캐주얼 관찰자, 당신은 타사 라이브러리를 사용하고자되지 않습니다. Vigenere 암호와 같은 것을 추천합니다. 단순한 고대 암호 중 가장 강력한 암호 중 하나입니다.

Vigenère 암호

빠르고 쉽게 구현할 수 있습니다. 다음과 같은 것 :

import base64

def encode(key, string):
    encoded_chars = []
    for i in xrange(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return base64.urlsafe_b64encode(encoded_string)

디코딩은 키를 빼는 것을 제외하면 거의 동일합니다.

인코딩하는 문자열이 짧거나 사용 된 암호의 길이를 추측하기 어려운 경우 깨지기가 훨씬 더 어렵습니다.

암호화 된 것을 찾고 있다면 PyCrypto가 아마도 최선의 방법 일 것입니다. 그러나 이전 답변은 몇 가지 세부 사항을 간과하고 있습니다. PyCrypto의 ECB 모드에서는 메시지 길이가 16 자의 배수 여야합니다. 그래서 패드를해야합니다. 또한 URL 매개 변수로 사용하려면base64.urlsafe_b64_encode() 하려면 표준 대신을 하십시오. 이것은 base64 알파벳의 일부 문자를 URL 안전 문자로 대체합니다 (이름에서 알 수 있듯이).

그러나 이것을 사용하기 전에이 매우 얇은 난독 화 층이 귀하의 요구에 충분 하다는 것을 절대적으로 확신해야합니다 . 내가 링크 한 Wikipedia 기사는 암호 해독에 대한 자세한 지침을 제공하므로 적당한 정도의 결정을 가진 사람은 누구나 쉽게 해독 할 수 있습니다.


답변

보안이 아닌 모호함을 원한다고 명시 적으로 말했듯이, 우리는 당신이 제안한 것의 약점에 대해 당신을 질책하지 않을 것입니다 🙂

따라서 PyCrypto를 사용합니다.

import base64
from Crypto.Cipher import AES

msg_text = b'test some plain text here'.rjust(32)
secret_key = b'1234567890123456'

cipher = AES.new(secret_key,AES.MODE_ECB) # never use ECB in strong systems obviously
encoded = base64.b64encode(cipher.encrypt(msg_text))
print(encoded)
decoded = cipher.decrypt(base64.b64decode(encoded))
print(decoded)

누군가 데이터베이스와 코드베이스를 확보하면 암호화 된 데이터를 디코딩 할 수 있습니다. secret_key안전을 지키세요 !


답변

파이썬에는 내장 된 암호화 체계가 없습니다. 또한 암호화 된 데이터 저장소를 중요하게 생각해야합니다. 한 개발자가 안전하지 않다고 이해하는 사소한 암호화 체계와 경험이 적은 개발자는 장난감 체계를 안전한 체계로 오인 할 수 있습니다. 암호화하는 경우 올바르게 암호화하십시오.

그러나 적절한 암호화 체계를 구현하기 위해 많은 작업을 할 필요는 없습니다. 우선 , 암호화 휠을 재발 명하지 말고 신뢰할 수있는 암호화 라이브러리를 사용하여이를 처리하십시오. Python 3의 경우 신뢰할 수있는 라이브러리는cryptography .

또한 암호화 및 암호 해독을 바이트에 적용하는 것이 좋습니다 . 먼저 문자 메시지를 바이트로 인코딩합니다. stringvalue.encode()UTF8로 인코딩하고 다음을 사용하여 쉽게 되돌릴 수 있습니다.bytesvalue.decode() .

마지막으로 암호화 및 복호화시 암호가 아닌 에 대해 이야기 합니다. 키는 사람이 기억할 수 없어야합니다. 암호는 사람이 읽을 수 있고 기억할 수있는 경우가 많지만 비밀 위치에 저장하지만 컴퓨터에서 읽을 수있는 것입니다. 당신 은 할있습니다약간의주의를 기울여 암호에서 키를 추출 .

그러나 웹 애플리케이션 또는 클러스터에서 실행되는 프로세스를 사람의주의없이 계속 실행하려면 키를 사용하려고합니다. 암호는 최종 사용자 만 특정 정보에 액세스해야하는 경우에 사용됩니다. 그럼에도 불구하고 일반적으로 암호로 응용 프로그램을 보호 한 다음 사용자 계정에 연결된 키를 사용하여 암호화 된 정보를 교환합니다.

대칭 키 암호화

Fernet – AES CBC + HMAC, 적극 권장

cryptography라이브러리가 포함 Fernet 조리법 , 암호화를 사용하기위한 모범 사례 조리법을. Fernet은 개방형 표준입니다. 광범위한 프로그래밍 언어로 즉시 구현 이며 메시지 변조를 방지하기 위해 버전 정보, 타임 스탬프 및 HMAC 서명과 함께 AES CBC 암호화를 패키지화합니다.

Fernet 암호화하고 해독 메시지를 매우 쉽게 그것을하게 하고 안전하게 보호 할 수 있습니다. 비밀로 데이터를 암호화하는 데 이상적인 방법입니다.

Fernet.generate_key()보안 키를 생성하는 데 사용 하는 것이 좋습니다 . 암호도 사용할 수 있지만 (다음 섹션), 전체 32 바이트 비밀 키 (암호화에 16 바이트, 서명에 16 바이트)가 생각할 수있는 대부분의 암호보다 더 안전합니다.

Fernet이 생성하는 키는 bytesURL 및 파일 안전 base64 문자 가있는 객체이므로 인쇄 할 수 있습니다.

from cryptography.fernet import Fernet

key = Fernet.generate_key()  # store in a secure location
print("Key:", key.decode())

암호화 또는 암호 해독 메시지하려면 만들 Fernet()지정된 키를 가진 인스턴스를, 및 호출 Fernet.encrypt()하거나 Fernet.decrypt(), 암호화에 일반 텍스트 메시지와 암호화 된 토큰 모두는 bytes객체.

encrypt()decrypt()기능과 같을 것이다 :

from cryptography.fernet import Fernet

def encrypt(message: bytes, key: bytes) -> bytes:
    return Fernet(key).encrypt(message)

def decrypt(token: bytes, key: bytes) -> bytes:
    return Fernet(key).decrypt(token)

데모:

>>> key = Fernet.generate_key()
>>> print(key.decode())
GZWKEhHGNopxRdOHS4H4IyKhLQ8lwnyU7vRLrM3sebY=
>>> message = 'John Doe'
>>> encrypt(message.encode(), key)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> token = _
>>> decrypt(token, key).decode()
'John Doe'

암호가있는 Fernet – 암호에서 파생 된 키로 보안이 다소 약화됩니다.

강력한 키 파생 방법사용하는 경우 비밀 키 대신 비밀번호를 사용할 수 있습니다 . 그런 다음 메시지에 salt 및 HMAC 반복 횟수를 포함해야하므로 암호화 된 값은 먼저 salt, count 및 Fernet 토큰을 분리하지 않고는 더 이상 Fernet과 호환되지 않습니다.

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

backend = default_backend()
iterations = 100_000

def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
    """Derive a secret key from a given password and salt"""
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(), length=32, salt=salt,
        iterations=iterations, backend=backend)
    return b64e(kdf.derive(password))

def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
    salt = secrets.token_bytes(16)
    key = _derive_key(password.encode(), salt, iterations)
    return b64e(
        b'%b%b%b' % (
            salt,
            iterations.to_bytes(4, 'big'),
            b64d(Fernet(key).encrypt(message)),
        )
    )

def password_decrypt(token: bytes, password: str) -> bytes:
    decoded = b64d(token)
    salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
    iterations = int.from_bytes(iter, 'big')
    key = _derive_key(password.encode(), salt, iterations)
    return Fernet(key).decrypt(token)

데모:

>>> message = 'John Doe'
>>> password = 'mypass'
>>> password_encrypt(message.encode(), password)
b'9Ljs-w8IRM3XT1NDBbSBuQABhqCAAAAAAFyJdhiCPXms2vQHO7o81xZJn5r8_PAtro8Qpw48kdKrq4vt-551BCUbcErb_GyYRz8SVsu8hxTXvvKOn9QdewRGDfwx'
>>> token = _
>>> password_decrypt(token, password).decode()
'John Doe'

출력에 솔트를 포함하면 임의의 솔트 값을 사용할 수 있으므로 암호 재사용이나 메시지 반복에 관계없이 암호화 된 출력이 완전히 무작위로 보장됩니다. 반복 횟수를 포함하면 오래된 메시지를 해독하는 기능을 잃지 않고 시간이 지남에 따라 CPU 성능 향상을 조정할 수 있습니다.

비슷한 크기의 풀에서 적절하게 임의의 암호를 생성한다면 암호만으로도 Fernet 32 ​​바이트 임의 키만큼 안전 할 수 있습니다. 32 바이트는 256 ^ 32 개의 키를 제공하므로 74 자 (대문자 26 개, 하위 26 개, 10 자리 및 12 개의 가능한 기호)의 알파벳을 사용하는 경우 암호는 최소 math.ceil(math.log(256 ** 32, 74))== 42 자 여야합니다 . 그러나 잘 선택된 더 많은 수의 HMAC 반복 은 공격자가 무차별 대입하는 데 훨씬 더 많은 비용이 들기 때문에 엔트로피 부족을 다소 완화 할 수 있습니다.

더 짧지 만 여전히 합리적으로 안전한 암호를 선택한다고해서이 체계가 무력화되는 것은 아니며 무차별 대입 공격자가 검색해야하는 가능한 값의 수를 줄입니다. 보안 요구 사항에 대해 충분히 강력한 암호 를 선택하십시오. .

대안

모호함

대안은 암호화하지 않는 것 입니다. 보안 수준이 낮은 암호 나 집에서 만든 구현을 사용하려는 유혹을받지 마십시오. 이러한 접근 방식에는 보안이 없지만, 미래에 코드를 유지 관리하는 작업을 맡은 경험이없는 개발자에게 보안이 전혀없는 것보다 더 나쁜 보안의 환상을 줄 수 있습니다.

모호함 만 있으면 데이터를 base64로 지정하십시오. URL 안전 요구 사항의 경우 base64.urlsafe_b64encode()기능 이 좋습니다. 여기에서 비밀번호를 사용하지 말고 인코딩 만하면됩니다. 기껏해야 압축을 추가하십시오 (예 zlib:).

import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

def obscure(data: bytes) -> bytes:
    return b64e(zlib.compress(data, 9))

def unobscure(obscured: bytes) -> bytes:
    return zlib.decompress(b64d(obscured))

이 회전 b'Hello world!'b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

무결성 만

신뢰할 수없는 클라이언트로 전송되고 다시 수신 된 후 데이터가 변경되지 않도록 신뢰할 수 있는지 확인하는 방법 만 있으면 데이터 에 서명하고이를 위해 hmac라이브러리 를 SHA1과 함께 사용할 수 있습니다 (여전히 HMAC 서명에 대해 안전한 것으로 간주 됨 ) 이상 :

import hmac
import hashlib

def sign(data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    assert len(key) >= algorithm().digest_size, (
        "Key must be at least as long as the digest size of the "
        "hashing algorithm"
    )
    return hmac.new(key, data, algorithm).digest()

def verify(signature: bytes, data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    expected = sign(data, key, algorithm)
    return hmac.compare_digest(expected, signature)

이를 사용하여 데이터에 서명 한 다음 데이터와 함께 서명을 첨부하여 클라이언트에 보냅니다. 데이터를 다시 받으면 데이터와 서명을 분할하고 확인하십시오. 기본 알고리즘을 SHA256으로 설정 했으므로 32 바이트 키가 필요합니다.

key = secrets.token_bytes(32)

다양한 형식의 직렬화 및 역 직렬화로이 모든 것을 패키징 하는 itsdangerous라이브러리 를 살펴볼 수 있습니다 .

AES-GCM 암호화를 사용하여 암호화 및 무결성 제공

Fernet은 암호화 된 데이터의 무결성을 보장하기 위해 HMAC 서명으로 AEC-CBC를 기반으로합니다. 악의적 인 공격자는 암호문이 서명되어 있기 때문에 잘못된 입력으로 서클에서 서비스를 계속 실행하기 위해 시스템에 말도 안되는 데이터를 제공 할 수 없습니다.

갈루아 / 카운터 모드의 블록 암호는 암호문을 생성하고 태그 때문에 동일한 목적을 제공하기 위해 사용될 수 있고, 동일한 목적을 제공 할 수있다. 단점은 Fernet과 달리 다른 플랫폼에서 재사용 할 수있는 사용하기 쉬운 한 가지 방법이 없다는 것입니다. AES-GCM은 또한 패딩을 사용하지 않으므로이 암호화 암호문은 입력 메시지의 길이와 일치합니다 (Fernet / AES-CBC는 메시지를 고정 길이의 블록으로 암호화하여 메시지 길이를 다소가립니다).

AES256-GCM은 일반적인 32 바이트 비밀을 키로 사용합니다.

key = secrets.token_bytes(32)

그런 다음 사용

import binascii, time
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

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

backend = default_backend()

def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes:
    current_time = int(time.time()).to_bytes(8, 'big')
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.GCM(iv), backend=backend)
    encryptor = cipher.encryptor()
    encryptor.authenticate_additional_data(current_time)
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return b64e(current_time + iv + ciphertext + encryptor.tag)

def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes:
    algorithm = algorithms.AES(key)
    try:
        data = b64d(token)
    except (TypeError, binascii.Error):
        raise InvalidToken
    timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:]
    if ttl is not None:
        current_time = int(time.time())
        time_encrypted, = int.from_bytes(data[:8], 'big')
        if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted:
            # too old or created well before our current time + 1 h to account for clock skew
            raise InvalidToken
    cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend)
    decryptor = cipher.decryptor()
    decryptor.authenticate_additional_data(timestamp)
    ciphertext = data[8 + len(iv):-16]
    return decryptor.update(ciphertext) + decryptor.finalize()

Fernet이 지원하는 동일한 TTL (Time-to-Live) 사용 사례를 지원하기 위해 타임 스탬프를 포함했습니다.

이 페이지의 다른 접근 방식, Python 3

AES CFB- CBC와 비슷하지만 패딩 할 필요 없음

이것은 잘못되었지만 All Іѕ Vаиітy가 따르는 접근 방식입니다 . 이것은 cryptography버전이지만 암호문에 IV를 포함하므로 전역으로 저장해서는 안됩니다 (IV를 재사용하면 키의 보안이 약화되고 모듈 전역으로 저장하면 다시 생성됨을 의미 함). 다음 Python 호출, 모든 암호문을 해독 할 수 없도록 렌더링) :

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

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

backend = default_backend()

def aes_cfb_encrypt(message, key):
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return b64e(iv + ciphertext)

def aes_cfb_decrypt(ciphertext, key):
    iv_ciphertext = b64d(ciphertext)
    algorithm = algorithms.AES(key)
    size = algorithm.block_size // 8
    iv, encrypted = iv_ciphertext[:size], iv_ciphertext[size:]
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted) + decryptor.finalize()

여기에는 HMAC 서명의 추가 아머 링이 없으며 타임 스탬프가 없습니다. 직접 추가해야합니다.

위의 내용은 기본 암호화 빌딩 블록을 잘못 결합하는 것이 얼마나 쉬운 지 보여줍니다. 모든 Іѕ Vаиітy의 IV 값을 잘못 처리하면 IV가 손실되어 데이터 유출 또는 모든 암호화 된 메시지를 읽을 수 없게 될 수 있습니다. 대신 Fernet을 사용하면 이러한 실수로부터 보호됩니다.

AES ECB – 안전하지 않음

이전에 AES ECB 암호화를 구현 했고 Python 3에서이를 계속 지원해야하는 경우에도 여전히 그렇게 할 수 cryptography있습니다. 동일한 경고가 적용되며 ECB는 실제 애플리케이션에 대해 충분히 안전하지 않습니다 . Python 3에 대한 답변을 다시 구현하여 패딩 자동 처리를 추가합니다.

from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

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

backend = default_backend()

def aes_ecb_encrypt(message, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(cipher.algorithm.block_size).padder()
    padded = padder.update(msg_text.encode()) + padder.finalize()
    return b64e(encryptor.update(padded) + encryptor.finalize())

def aes_ecb_decrypt(ciphertext, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder()
    padded = decryptor.update(b64d(ciphertext)) + decryptor.finalize()
    return unpadder.update(padded) + unpadder.finalize()

다시 말하지만 여기에는 HMAC 서명이 없기 때문에 ECB를 사용해서는 안됩니다. 위의 내용은 cryptography실제로 사용해서는 안되는 공통 암호화 빌딩 블록을 처리 할 수있는 것을 설명하기 위한 것입니다.


답변

@smehmood의 Vigenere 암호 답변에 언급 된 “encoded_c”는 “key_c” 여야합니다.

다음은 인코딩 / 디코딩 기능입니다.

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

면책 조항 : 의견에서 알 수 있듯이,이 내용 을 읽고 변호사와 이야기하는 것을 꺼리지 않는 한 실제 애플리케이션에서 데이터를 보호하는 데 사용해서는 안됩니다 .

XOR 암호화의 문제점은 무엇입니까?


답변

@qneill의 답변 에서 가져온 함수의 Python 3 버전은 다음과 같습니다 .

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc).encode()).decode()

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc).decode()
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

Python 3은 문자열 / 바이트 배열을 두 개의 다른 개념으로 분할하고이를 반영하도록 API를 업데이트했기 때문에 추가 인코딩 / 디코딩이 필요합니다.


답변

면책 조항 : 주석에서 언급했듯이 실제 애플리케이션에서 데이터를 보호하기 위해 사용해서는 안됩니다 .

XOR 암호화의 문제점은 무엇입니까?

/crypto/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


앞서 언급했듯이 PyCrypto 라이브러리에는 암호 모음이 포함되어 있습니다. XOR “암호화”를 사용하여 직접 수행하고 싶지 않은 경우 더러운 작업을 수행 할 수 있습니다.

from Crypto.Cipher import XOR
import base64

def encrypt(key, plaintext):
  cipher = XOR.new(key)
  return base64.b64encode(cipher.encrypt(plaintext))

def decrypt(key, ciphertext):
  cipher = XOR.new(key)
  return cipher.decrypt(base64.b64decode(ciphertext))

암호는 일반 텍스트를 채울 필요없이 다음과 같이 작동합니다.

>>> encrypt('notsosecretkey', 'Attack at dawn!')
'LxsAEgwYRQIGRRAKEhdP'

>>> decrypt('notsosecretkey', encrypt('notsosecretkey', 'Attack at dawn!'))
'Attack at dawn!'

신용 https://stackoverflow.com/a/2490376/241294에 대한 은 base64 인코딩 / 디코딩 기능에 대한 것입니다 (저는 파이썬 초보자입니다).


답변

다음은 AES (PyCrypto) 및 base64를 사용한 URL Safe 암호화 및 복호화 구현입니다.

import base64
from Crypto import Random
from Crypto.Cipher import AES

AKEY = b'mysixteenbytekey' # AES key must be either 16, 24, or 32 bytes long

iv = Random.new().read(AES.block_size)

def encode(message):
    obj = AES.new(AKEY, AES.MODE_CFB, iv)
    return base64.urlsafe_b64encode(obj.encrypt(message))

def decode(cipher):
    obj2 = AES.new(AKEY, AES.MODE_CFB, iv)
    return obj2.decrypt(base64.urlsafe_b64decode(cipher))

https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) 와 같은 문제가 발생 str(cipher)하면 다음과 같이 디코딩 하는 동안 사용 하십시오 .

return obj2.decrypt(base64.urlsafe_b64decode(str(cipher)))

테스트:

In [13]: encode(b"Hello World")
Out[13]: b'67jjg-8_RyaJ-28='

In [14]: %timeit encode("Hello World")
100000 loops, best of 3: 13.9 µs per loop

In [15]: decode(b'67jjg-8_RyaJ-28=')
Out[15]: b'Hello World'

In [16]: %timeit decode(b'67jjg-8_RyaJ-28=')
100000 loops, best of 3: 15.2 µs per loop