PyCrypto AES-256을 사용한 암호화 및 암호 해독
저는 PyCrypto를 사용하여 메시지와 키의 두 가지 매개 변수를 수락한 다음 메시지를 암호화/암호 해독하는 두 가지 기능을 구축하려고 합니다.
웹에서 도움이 되는 링크를 여러 개 찾았지만 각각의 링크에 결함이 있습니다.
코데코알라에 있는 이것은 PyCrypto에 의해 차단된 os.urandom을 사용합니다.
게다가, 내가 함수에 주는 키는 정확한 길이가 예상되는 것을 보장하지 않습니다.어떻게 하면 그렇게 할 수 있을까요?
또한 여러 가지 모드가 있는데, 어떤 것이 추천되나요?무엇을 사용해야 할지 모르겠어요 :/
마지막으로 링거는 정확히 무엇입니까?암호화 및 암호 해독을 위해 다른 IV를 제공할 수 있습니까? 아니면 다른 결과로 반환됩니까?
다음은 제가 구현한 것이며, 몇 가지 수정 사항을 참조하십시오.키 및 암호 구문의 정렬이 32바이트, IV가 16바이트로 향상됩니다.
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.bs = AES.block_size
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw.encode()))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
@staticmethod
def _unpad(s):
return s[:-ord(s[len(s)-1:])]
두 이 필요할 수 .pad
할 때) 및 패암 (암호화를 할 때) 및unpad
입력 길이가 BLOCK_SIZE의 배수가 아닐 때(복호화를 수행할 때) 패딩을 해제합니다.
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]
그럼 열쇠의 길이를 묻는 겁니까?키의 MD5 해시를 직접 사용하지 않고 사용할 수 있습니다.
또한, PyCrypto를 사용한 저의 작은 경험에 따르면, IV는 입력이 동일할 때 암호화 출력을 혼합하는 데 사용되므로 IV는 무작위 문자열로 선택되고 암호화 출력의 일부로 사용된 다음 메시지를 해독하는 데 사용됩니다.
다음은 제 구현입니다.
import base64
from Crypto.Cipher import AES
from Crypto import Random
class AESCipher:
def __init__( self, key ):
self.key = key
def encrypt( self, raw ):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( self, enc ):
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] ))
"모드"에 대한 질문을 드리겠습니다. AES-256은 일종의 블록 암호입니다.블록이라고 하는 32바이트 키와 16바이트 문자열을 입력하고 블록을 출력합니다.암호화하기 위해 운영 모드에서 AES를 사용합니다.위의 솔루션은 하나의 예인 CBC를 사용할 것을 제안합니다.또 다른 이름은 CTR이며 사용하기가 다소 쉽습니다.
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random
# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
key_bytes = 32
# Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
# pair (iv, ciphtertext). "iv" stands for initialization vector.
def encrypt(key, plaintext):
assert len(key) == key_bytes
# Choose a random, 16-byte IV.
iv = Random.new().read(AES.block_size)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(plaintext)
return (iv, ciphertext)
# Takes as input a 32-byte key, a 16-byte IV, and a ciphertext, and outputs the
# corresponding plaintext.
def decrypt(key, iv, ciphertext):
assert len(key) == key_bytes
# Initialize counter for decryption. iv should be the same as the output of
# encrypt().
iv_int = int(iv.encode('hex'), 16)
ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Decrypt and return the plaintext.
plaintext = aes.decrypt(ciphertext)
return plaintext
(iv, ciphertext) = encrypt(key, 'hella')
print decrypt(key, iv, ciphertext)
이를 흔히 AES-CTR이라고 합니다.AES-CBC를 PyCrypto와 함께 사용할 때 주의할 것을 조언합니다.그 이유는 주어진 다른 솔루션에 예시된 것처럼 패딩 방식을 지정해야 하기 때문입니다.일반적으로 패딩에 대해 매우 조심하지 않으면 암호화를 완전히 깨는 공격이 있습니다!
키는 임의의 32바이트 문자열이어야 합니다. 암호로는 충분하지 않습니다.일반적으로 키는 다음과 같이 생성됩니다.
# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
키는 암호에서 파생될 수도 있습니다.
# It's also possible to derive a key from a password, but it's important that
# the password have high entropy, meaning difficult to predict.
password = "This is a rather weak password."
# For added # security, we add a "salt", which increases the entropy.
#
# In this example, we use the same RNG to produce the salt that we used to
# produce key1.
salt_bytes = 8
salt = Random.new().read(salt_bytes)
# Stands for "Password-based key derivation function 2"
key2 = PBKDF2(password, salt, key_bytes)
위의 일부 솔루션은 키를 유도하기 위해 SHA-256을 사용할 것을 제안하지만 이는 일반적으로 잘못된 암호화 관행으로 간주됩니다.작동 모드에 대한 자세한 내용은 위키피디아를 참조하십시오.
저는 저에게 영감을 준 다른 답변들에 감사하지만, 그것은 저에게 효과가 없었습니다.
몇 시간을 들여 작동 방식을 파악한 후, 최신 PyCryptodomex 라이브러리를 사용하여 아래와 같은 구현 방법을 생각해 냈습니다(프록시 뒤에서 Windows에서 가상 환경에서 어떻게 설정했는지는 별개의 이야기입니다).휴)
구현 작업을 수행하고 있습니다.패딩, 인코딩 및 암호화 단계(또는 그 반대)를 기록해야 합니다.당신은 순서를 염두에 두고 짐을 싸고 짐을 풀어야 합니다.
import base64
import hashlib
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes
__key__ = hashlib.sha256(b'16-character key').digest()
def encrypt(raw):
BS = AES.block_size
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
raw = base64.b64encode(pad(raw).encode('utf8'))
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key= __key__, mode= AES.MODE_CFB,iv= iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(enc):
unpad = lambda s: s[:-ord(s[-1:])]
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(__key__, AES.MODE_CFB, iv)
return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8'))
urlsafe_b64encode와 urlsafe_b64decode를 사용하고 싶은 사람을 위해, 여기 제가 사용할 수 있는 버전이 있습니다(유니코드 문제로 시간을 보낸 후).
BS = 16
key = hashlib.md5(settings.SECRET_KEY).hexdigest()[:BS]
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, raw):
raw = pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.urlsafe_b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.urlsafe_b64decode(enc.encode('utf-8'))
iv = enc[:BS]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[BS:]))
암호화 해시 함수를 사용하여 임의 암호에서 암호 구문을 가져올 수 있습니다(Python의 내장형이 아님).hash
SHA-1은 SHA-256을 합니다. 라이브러리에서 두 합니다.Python은 표준 라이브러리에서 두 가지를 모두 지원합니다.
import hashlib
hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string
은 다을사암해값시수있다습니자를을음호화여용을 하여 잘라낼 수 .[:16]
또는[:24]
지정된 길이까지 보안을 유지합니다.
이에 대한 또 다른 견해(위의 솔루션에서 크게 파생됨)는 있지만,
- 패딩에 null을 사용
- 람다를 사용하지 않음(팬이 아님)
Python 2.7 및 3.6.5로 테스트됨
#!/usr/bin/python2.7 # you'll have to adjust for your setup, e.g., #!/usr/bin/python3 import base64, re from Crypto.Cipher import AES from Crypto import Random from django.conf import settings class AESCipher: """ Usage: aes = AESCipher( settings.SECRET_KEY[:16], 32) encryp_msg = aes.encrypt( 'ppppppppppppppppppppppppppppppppppppppppppppppppppppppp' ) msg = aes.decrypt( encryp_msg ) print("'{}'".format(msg)) """ def __init__(self, key, blk_sz): self.key = key self.blk_sz = blk_sz def encrypt( self, raw ): if raw is None or len(raw) == 0: raise NameError("No value given to encrypt") raw = raw + '\0' * (self.blk_sz - len(raw) % self.blk_sz) raw = raw.encode('utf-8') iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key.encode('utf-8'), AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf-8') def decrypt( self, enc ): if enc is None or len(enc) == 0: raise NameError("No value given to decrypt") enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new(self.key.encode('utf-8'), AES.MODE_CBC, iv ) return re.sub(b'\x00*$', b'', cipher.decrypt( enc[16:])).decode('utf-8')
다른 사람들의 이익을 위해, 여기 @Cyril과 @Marcus의 답변을 결합하여 얻은 저의 암호 해독 구현이 있습니다.이것은 이것이 암호화된 HTTP 요청을 통해 들어오는 것을 가정합니다.텍스트를 따옴표로 묶고 base64를 인코딩했습니다.
import base64
import urllib2
from Crypto.Cipher import AES
def decrypt(quotedEncodedEncrypted):
key = 'SecretKey'
encodedEncrypted = urllib2.unquote(quotedEncodedEncrypted)
cipher = AES.new(key)
decrypted = cipher.decrypt(base64.b64decode(encodedEncrypted))[:16]
for i in range(1, len(base64.b64decode(encodedEncrypted))/16):
cipher = AES.new(key, AES.MODE_CBC, base64.b64decode(encodedEncrypted)[(i-1)*16:i*16])
decrypted += cipher.decrypt(base64.b64decode(encodedEncrypted)[i*16:])[:16]
return decrypted.strip()
둘 다 사용했습니다.Crypto
그리고.PyCryptodomex
도서관과 도서관은 빠르게 불타오르고 있습니다.
import base64
import hashlib
from Cryptodome.Cipher import AES as domeAES
from Cryptodome.Random import get_random_bytes
from Crypto import Random
from Crypto.Cipher import AES as cryptoAES
BLOCK_SIZE = AES.block_size
key = "my_secret_key".encode()
__key__ = hashlib.sha256(key).digest()
print(__key__)
def encrypt(raw):
BS = cryptoAES.block_size
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
raw = base64.b64encode(pad(raw).encode('utf8'))
iv = get_random_bytes(cryptoAES.block_size)
cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
a= base64.b64encode(iv + cipher.encrypt(raw))
IV = Random.new().read(BLOCK_SIZE)
aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
b = base64.b64encode(IV + aes.encrypt(a))
return b
def decrypt(enc):
passphrase = __key__
encrypted = base64.b64decode(enc)
IV = encrypted[:BLOCK_SIZE]
aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
enc = aes.decrypt(encrypted[BLOCK_SIZE:])
unpad = lambda s: s[:-ord(s[-1:])]
enc = base64.b64decode(enc)
iv = enc[:cryptoAES.block_size]
cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
b= unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
return b
encrypted_data =encrypt("Hi Steven!!!!!")
print(encrypted_data)
print("=======")
decrypted_data = decrypt(encrypted_data)
print(decrypted_data)
PKCS#7 패딩과 같은 방식을 사용할 수 있습니다.대신 이전 기능을 사용하여 암호화를 수행할 때 패딩(Pad)하고 암호 해독을 수행할 때 패딩(Pad)을 해제할 수 있습니다.아래에 전체 소스 코드를 제공하겠습니다.
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
import pkcs7
class Encryption:
def __init__(self):
pass
def Encrypt(self, PlainText, SecurePassword):
pw_encode = SecurePassword.encode('utf-8')
text_encode = PlainText.encode('utf-8')
key = hashlib.sha256(pw_encode).digest()
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
pad_text = pkcs7.encode(text_encode)
msg = iv + cipher.encrypt(pad_text)
EncodeMsg = base64.b64encode(msg)
return EncodeMsg
def Decrypt(self, Encrypted, SecurePassword):
decodbase64 = base64.b64decode(Encrypted.decode("utf-8"))
pw_encode = SecurePassword.decode('utf-8')
iv = decodbase64[:AES.block_size]
key = hashlib.sha256(pw_encode).digest()
cipher = AES.new(key, AES.MODE_CBC, iv)
msg = cipher.decrypt(decodbase64[AES.block_size:])
pad_text = pkcs7.decode(msg)
decryptedString = pad_text.decode('utf-8')
return decryptedString
import StringIO
import binascii
def decode(text, k=16):
nl = len(text)
val = int(binascii.hexlify(text[-1]), 16)
if val > k:
raise ValueError('Input is not padded or padding is corrupt')
l = nl - val
return text[:l]
def encode(text, k=16):
l = len(text)
output = StringIO.StringIO()
val = k - (l % k)
for _ in xrange(val):
output.write('%02x' % val)
return text + binascii.unhexlify(output.getvalue())
Mnothic의 대답을 보세요.
호환되는 UTF-8 인코딩:
def _pad(self, s):
s = s.encode()
res = s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs).encode()
return res
파이크립토는 늙고 망가졌습니다.
암호화 기술은 요즘 더 나은 지원을 제공합니다.
여기 또 다른 구현이 있습니다.이것은 바이트를 반환합니다. 전송을 위해 문자열로 변환하려면 base64를 사용해야 합니다.
import os
import hashlib
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
_BLOCK_SIZE = 16
class AesStringCipher:
def __init__(self, key):
self._key = hashlib.sha256(key.encode()).digest()
def encrypt_str(self, raw:str) -> bytes:
iv = os.urandom(_BLOCK_SIZE)
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
encryptor = cipher.encryptor()
raw = _pad(raw)
return iv + encryptor.update(raw.encode('utf-8')) + encryptor.finalize()
def decrypt_str(self, enc:bytes) -> str:
iv = enc[:_BLOCK_SIZE]
enc = enc[_BLOCK_SIZE:]
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
decryptor = cipher.decryptor()
raw = decryptor.update(enc) + decryptor.finalize()
raw = raw.decode('utf-8')
return _unpad(raw)
def _pad(s:str) -> str:
padding = (_BLOCK_SIZE - (len(s) % _BLOCK_SIZE))
return s + padding * chr(padding)
def _unpad(s:str) -> str:
return s[:-ord(s[len(s)-1:])]
if __name__ == '__main__':
cipher = AesStringCipher('my secret password')
secret_msg = 'this is a super secret msg ...'
enc_msg = cipher.encrypt_str(secret_msg)
dec_msg = cipher.decrypt_str(enc_msg)
assert secret_msg == dec_msg
AES-256과 utf8mb4를 사용한 라틴어 및 특수 문자(중국어)의 암호화 및 암호 해독:
라틴어와 중국어와 같은 특수 값을 암호화 및 해독해야 하는 사용자를 위해 이 작업을 수행하기 위해 @MIKEE 코드를 수정했습니다.
UTF-8만으로는 이러한 유형의 인코딩을 처리할 수 없습니다.
import base64, re
from Crypto.Cipher import AES
from Crypto import Random
from django.conf import settings
import codecs
# Make utf8mb4 recognizable.
codecs.register(lambda name: codecs.lookup('utf8') if name == 'utf8mb4' else None)
class AESCipher:
def __init__(self, key, blk_sz):
self.key = key
self.blk_sz = blk_sz
def encrypt( self, raw ):
# raw is the main value
if raw is None or len(raw) == 0:
raise NameError("No value given to encrypt")
raw = raw + '\0' * (self.blk_sz - len(raw) % self.blk_sz)
raw = raw.encode('utf8mb4')
# Initialization vector to avoid same encrypt for same strings.
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf8mb4')
def decrypt( self, enc ):
# enc is the encrypted value
if enc is None or len(enc) == 0:
raise NameError("No value given to decrypt")
enc = base64.b64decode(enc)
iv = enc[:16]
# AES.MODE_CFB that allows bigger length or Latin values
cipher = AES.new(self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
return re.sub(b'\x00*$', b'', cipher.decrypt( enc[16:])).decode('utf8mb4')
용도:
>>> from django.conf import settings
>>> from aesencryption import AESCipher
>>>
>>> aes = AESCipher(settings.SECRET_KEY[:16], 32)
>>>
>>> value = aes.encrypt('漢字')
>>>
>>> value
'hnuRwBjwAHDp5X0DmMF3lWzbjR0r81WlW9MRrWukgQwTL0ZI88oQaWvMfBM+W87w9JtSTw=='
>>> dec_value = aes.decrypt(value)
>>> dec_value
'漢字'
>>>
다음과 같은 라틴 문자도 동일합니다.ã, á, à, â, ã, ç
,기타.
주의점
라틴 값을 데이터베이스에 저장하려면 이러한 유형의 데이터를 허용하도록 설정해야 합니다.따라서 데이터베이스가 utf-8로 설정된 경우 이러한 유형의 데이터는 허용되지 않습니다.당신은 그곳에서도 옷을 갈아입어야 할 것입니다.
from Crypto import Random
from Crypto.Cipher import AES
import base64
BLOCK_SIZE=16
def trans(key):
return md5.new(key).digest()
def encrypt(message, passphrase):
passphrase = trans(passphrase)
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return base64.b64encode(IV + aes.encrypt(message))
def decrypt(encrypted, passphrase):
passphrase = trans(passphrase)
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return aes.decrypt(encrypted[BLOCK_SIZE:])
새로운 장고 미리지 필드 패키지를 사용할 수 있습니다.
언급URL : https://stackoverflow.com/questions/12524994/encrypt-and-decrypt-using-pycrypto-aes-256
'itsource' 카테고리의 다른 글
Android - Firebase - TaskSnapshot - Method는 개인 범위 내에서만 액세스해야 합니까? (0) | 2023.06.21 |
---|---|
오류: C 스택 사용량이 제한에 너무 가깝습니다. (0) | 2023.06.21 |
동적 vuex 모듈과 새 클래스를 등록하는 중 (0) | 2023.06.21 |
PATINDEX 패턴 인수에서 밑줄 문자를 이스케이프하는 방법은 무엇입니까? (0) | 2023.06.21 |
Oracle SQL - 하나의 문이 있는 테이블에 여러 행을 삽입하시겠습니까? (0) | 2023.06.21 |