J'essaie de créer deux fonctions à l'aide de PyCrypto qui acceptent deux paramètres: le message et la clé, puis chiffrer/déchiffrer le message.
J'ai trouvé plusieurs liens sur le Web pour m'aider, mais chacun d'entre eux a des défauts:
Celui-ci chez codekoala utilise os.urandom, ce qui est découragé par PyCrypto.
De plus, la clé que je donne à la fonction n’est pas garantie pour avoir la longueur exacte attendue. Que puis-je faire pour que cela se produise?
En outre, il existe plusieurs modes, lequel est recommandé? Je ne sais pas quoi utiliser: /
Enfin, quel est exactement le IV? Puis-je fournir un IV différent pour le cryptage et le décryptage, ou est-ce que cela renverra un résultat différent?
Voici ce que j'ai fait jusqu'à présent:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BLOCK_SIZE=32
def encrypt(message, passphrase):
# passphrase MUST be 16, 24 or 32 bytes long, how can I do that ?
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return base64.b64encode(aes.encrypt(message))
def decrypt(encrypted, passphrase):
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return aes.decrypt(base64.b64decode(encrypted))
Voici mon implémentation et fonctionne pour moi avec quelques corrections et améliore l'alignement de la phrase clé et de la phrase secrète avec 32 octets et iv à 16 octets:
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.bs = 32
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))
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:])]
Vous aurez peut-être besoin des deux fonctions suivantes pour remplir (lorsque le chiffrement) et unpad (pour le déchiffrement) lorsque la longueur de l'entrée n'est pas un multiple de 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:])]
Donc vous demandez la longueur de la clé? Vous pouvez utiliser le md5sum de la clé plutôt que de l’utiliser directement.
De plus, selon ma petite expérience d'utilisation de PyCrypto, l'IV est utilisé pour mélanger la sortie d'un chiffrement lorsque l'entrée est identique, de sorte que l'IV est choisi comme chaîne aléatoire et utilisé utilisez-le pour déchiffrer le message.
Et voici ma mise en œuvre, espérons que cela vous sera utile:
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:] ))
Permettez-moi de répondre à votre question sur les "modes". AES256 est une sorte de bloc chiffré. Il prend en entrée un clé de 32 octets et une chaîne de 16 octets, appelée le bloc et génère un bloc. Nous utilisons AES dans un mode de fonctionnement afin de chiffrer. Les solutions ci-dessus suggèrent d'utiliser CBC, qui est un exemple. Un autre s'appelle CTR, et il est un peu plus facile à utiliser:
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)
Ceci est souvent appelé AES-CTR. Je vous conseillerais de ne pas utiliser AES-CBC avec PyCrypto. La raison en est qu'il vous oblige à spécifier le remplissage, comme illustré par les autres solutions données. En général, si vous n'êtes pas très prudent sur le remplissage, il y a attaques qui cassent complètement le cryptage!
A présent, il est important de noter que la clé doit être une chaîne aléatoire de 32 octets}; un mot de passe non suffit. Normalement, la clé est générée comme suit:
# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
Une clé peut être dérivée d'un mot de passe, aussi:
# 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)
Certaines solutions ci-dessus suggèrent d'utiliser SHA256 pour dériver la clé, mais ceci est généralement considéré comme mauvaise pratique cryptographique . Consultez wikipedia pour plus d'informations sur les modes de fonctionnement.
Pour ceux qui souhaitent utiliser urlsafe_b64encode et urlsafe_b64decode, voici la version qui fonctionne pour moi (après avoir passé un peu de temps avec le problème unicode)
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:]))
Vous pouvez obtenir une phrase secrète d'un mot de passe arbitraire en utilisant une fonction de hachage cryptographique (NOThash
de Python) comme SHA-1 ou SHA-256. Python inclut un support pour les deux dans sa bibliothèque standard:
import hashlib
hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string
Vous pouvez tronquer une valeur de hachage cryptographique en utilisant simplement [:16]
ou [:24]
et il conservera sa sécurité jusqu'à la longueur spécifiée.
Pour le bénéfice des autres, voici mon implémentation de décryptage à laquelle je suis arrivé en combinant les réponses de @Cyril et @Marcus. Cela suppose que cela vienne via une requête HTTP avec encryptedText cité et codé en 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()
Une autre prise en compte (fortement inspirée des solutions précédentes) mais
testé avec Python 2.7 et 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')
Il est un peu tard, mais je pense que cela sera très utile. Personne ne mentionne un schéma d'utilisation tel que le rembourrage PKCS # 7. Vous pouvez l'utiliser à la place des fonctions précédentes pour pad (lorsque le chiffrement) et unpad (pour le déchiffrement) .i fournira le code source complet ci-dessous.
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())
Reconnaissant pour les autres réponses qui ont inspiré mais qui n'ont pas fonctionné pour moi.
Après avoir passé des heures à comprendre comment cela fonctionne, j’ai proposé l’implémentation ci-dessous avec la dernière bibliothèque PyCryptodomex (c’est une autre histoire de savoir comment j’ai réussi à l’installer derrière un proxy, sous Windows, dans un environnement virtuel. phew)
Lorsque vous travaillez sur votre implémentation, n'oubliez pas de noter les étapes de remplissage, d'encodage et de cryptage (et inversement). Vous devez emballer et décompresser en tenant compte de la commande.
import base64 import hashlib depuis Cryptodome.Cipher import AES depuis Cryptodome.Random import get_random_bytes __ clé__ = hashlib.sha256 (clé de 16 caractères '). () 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 (clé = __key__, mode = AES.MODE_CFB, iv = iv) retourne 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) renvoyer unpad (base64.b64decode (cipher.decrypt (enc [AES.block_size:])). decode ('utf8'))
J'ai utilisé à la fois la bibliothèque Crypto
et PyCryptodomex
et elle flambe vite ...
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=16
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)
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:])
encodage compatible 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