222 votes

Cryptage et décryptage avec PyCrypto AES 256

J'essaie de construire deux fonctions utilisant PyCrypto qui acceptent deux paramètres : le message et la clé, puis chiffrent/déchiffrent le message.

J'ai trouvé plusieurs liens sur le web pour m'aider, mais chacun d'entre eux a des défauts :

Celui-ci à codekoala utilise os.urandom, ce qui est déconseillé par PyCrypto.

De plus, la clé que je donne à la fonction n'est pas garantie d'avoir la longueur exacte attendue. Que puis-je faire pour que cela se produise ?

De plus, il existe plusieurs modes, lequel est recommandé ? Je ne sais pas quoi utiliser :/

Enfin, qu'est-ce que l'IV exactement ? Puis-je fournir un IV différent pour le cryptage et le décryptage, ou cela donnera-t-il un résultat différent ?

Modifier : Suppression de la partie code car elle n'était pas sécurisée.

17 votes

os.urandom est encouragé sur le PyCrypto site web. Il utilise la technologie de Microsoft CryptGenRandom qui est une fonction CSPRNG

6 votes

Ou /dev/urandom sur Unix

2 votes

Juste pour clarifier, dans cet exemple phrase d'authentification est le clé qui peut être de 128, 192 ou 256 bits (16, 24 ou 32 octets).

227voto

mnothic Points 141

Voici ma mise en œuvre et fonctionne pour moi avec quelques corrections et améliore l'alignement de la clé et 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 = 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:])]

17 votes

Je sais que ce sujet a été abordé depuis un certain temps, mais je pense que cette réponse peut semer la confusion. Cette fonction utilise une taille de bloc de 32 octets (256 octets) pour rembourrer les données d'entrée mais AES utilise une taille de bloc de 128 bits. Dans AES256, le clé est de 256 bits, mais pas la taille du bloc.

14 votes

Pour le dire autrement, "self.bs" devrait être supprimé et remplacé par "AES.block_size".

7 votes

Pourquoi hacher la clé ? Si vous pensez qu'il s'agit de quelque chose comme un mot de passe, alors vous ne devriez pas utiliser SHA256 ; mieux vaut utiliser une fonction de dérivation de clé, comme PBKDF2, que PyCrypto fournit.

159voto

Marcus Points 3170

Vous pouvez avoir besoin des deux fonctions suivantes : pad - pour le remplissage (lors du cryptage) et unpad - pour décompacter (lors du décryptage) 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 de l'utilisation de PyCrypto, l'IV est utilisé pour mélanger la sortie d'un cryptage lorsque l'entrée est la même, donc l'IV est choisi comme une chaîne aléatoire, et l'utiliser comme une partie de la sortie de cryptage, et ensuite l'utiliser pour décrypter le message.

Et voici ma mise en œuvre, j'espère qu'elle 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:] ))

1 votes

Que se passe-t-il si vous avez une entrée qui est exactement un multiple de BLOCK_SIZE ? Je pense que la fonction unpad serait un peu confuse...

2 votes

@Kjir, alors une séquence de valeur chr(BS) de longueur BLOCK_SIZE sera ajoutée aux données d'origine.

0 votes

Vous avez raison, le pad et unpad Les fonctions sont maintenant plus claires pour moi, merci !

6voto

nneonneo Points 56821

Vous pouvez obtenir une phrase de passe à partir d'un mot de passe arbitraire en utilisant une fonction de hachage cryptographique ( PAS La fonction intégrée de Python hash ) comme SHA-1 ou SHA-256. Python inclut le support des 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 que vous indiquez.

15 votes

Vous ne devriez pas utiliser une fonction de hachage de type SHA pour générer une clé à partir d'un mot de passe - voir L'essai de Coda Hale sur le sujet . Pensez à utiliser un véritable fonction de dérivation des clés comme scrypt à la place. (L'essai de Coda Hale a été écrit avant la publication de scrypt).

7 votes

Pour les futurs lecteurs, si vous cherchez à dériver une clé à partir d'une phrase de passe, cherchez PBKDF2. Il est assez facile à utiliser en python ( pypi.python.org/pypi/pbkdf2 ). Si vous cherchez à hacher des mots de passe, cependant, bcrypt est une meilleure option.

5voto

scottmrogowski Points 461

Pour le bénéfice des autres, voici mon implémentation de décryptage à laquelle j'ai abouti en combinant les réponses de @Cyril et @Marcus. Cela suppose que le message arrive via une requête HTTP avec le texte crypté cité et encodé 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()

-1voto

espadon Points 1

Les fonctions pad et unpad, telles que décrites ci-dessus, ne fonctionnent que pour les entrées de type octet. Si l'entrée est une chaîne unicode, elle doit d'abord être convertie en type octet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X