Verschlüsseln und Entschlüsseln mit PyCrypto AES 256
Lesezeit: 11 Minuten
Kyrill N.
Ich versuche, zwei Funktionen mit PyCrypto zu erstellen, die zwei Parameter akzeptieren: die Nachricht und den Schlüssel, und dann die Nachricht verschlüsseln/entschlüsseln.
Ich habe mehrere Links im Internet gefunden, die mir helfen, aber jeder von ihnen hat Fehler:
Nur zur Verdeutlichung, in diesem Beispiel Passphrase ist der Schlüssel die 128, 192 oder 256 Bit (16, 24 oder 32 Byte) sein können
– Markieren
21. April 2016 um 21:37 Uhr
Es könnte erwähnenswert sein, dass PyCrypto eine ist totes Projekt. Der letzte Commit ist von 2014. PyCryptodome sieht nach einem guten Drop-in-Ersatz aus
– Overdriver
7. August 2018 um 7:58 Uhr
Diese Frage ist alt, aber ich möchte (ab 2020) darauf hinweisen, dass Pycrypto wahrscheinlich veraltet ist und nicht mehr unterstützt wird. Ein Blick auf ihre Github-Seite (github.com/pycrypto/pycrypto), scheint ihr letzter Commit im Jahr 2014 gewesen zu sein. Ich wäre misstrauisch gegenüber der Verwendung von kryptografischer Software, die nicht mehr in der Entwicklung ist
– irritable_phd_syndrome
6. Juni 2020 um 3:40 Uhr
mnotisch
Hier ist meine Implementierung und funktioniert für mich mit einigen Korrekturen und verbessert die Ausrichtung des Schlüssels und der geheimen Phrase mit 32 Bytes und iv auf 16 Bytes:
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:])]
Warum hashst du den Schlüssel? Wenn Sie erwarten, dass dies so etwas wie ein Passwort ist, sollten Sie SHA256 nicht verwenden. Verwenden Sie besser eine Schlüsselableitungsfunktion wie PBKDF2, die PyCrypto bereitstellt.
– zwicksp
20. Juni 2017 um 18:43 Uhr
@Chris – SHA256 gibt einen 32-Byte-Hash aus – ein Schlüssel mit perfekter Größe für AES256. Es wird davon ausgegangen, dass die Generierung/Ableitung eines Schlüssels zufällig/sicher ist und außerhalb des Bereichs des Verschlüsselungs-/Entschlüsselungscodes liegen sollte – Hashing ist nur eine Garantie dafür, dass der Schlüssel mit der ausgewählten Chiffre verwendbar ist.
– zwer
20. Juni 2017 um 20:35 Uhr
@mnothic Was ist der Grund für _pad eine normale Klassenmethode und _unpad sein staticmethod? Was ist der Unterschied?
– Boffin
4. Juli 2017 um 16:11 Uhr
in _pad ist self.bs Zugriff erforderlich und in _unpad nicht erforderlich
– mnotisch
5. Juli 2017 um 13:56 Uhr
@mnothic – dumme Frage: warum s[:-ord(s[len(s)-1:])] statt nur s[:-ord(s[-1])]?
– Ryugie
30. Oktober 2017 um 19:15 Uhr
Markus
Möglicherweise benötigen Sie die folgenden zwei Funktionen: pad– zum Auffüllen (bei der Verschlüsselung) und unpad– zum Auffüllen (bei der Entschlüsselung), wenn die Länge der Eingabe kein Vielfaches von BLOCK_SIZE ist.
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]
Du fragst also nach der Schlüssellänge? Sie können die MD5-Summe des Schlüssels verwenden, anstatt sie direkt zu verwenden.
Darüber hinaus wird der IV nach meiner kleinen Erfahrung mit PyCrypto verwendet, um die Ausgabe einer Verschlüsselung zu verwechseln, wenn die Eingabe gleich ist, sodass die IV als zufällige Zeichenfolge ausgewählt und als Teil der Verschlüsselungsausgabe verwendet wird, und dann Verwenden Sie es, um die Nachricht zu entschlüsseln.
Und hier ist meine Implementierung, ich hoffe, sie wird für Sie nützlich sein:
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:] ))
Was passiert, wenn Sie eine Eingabe haben, die genau ein Vielfaches von BLOCK_SIZE ist? Ich denke, dass die Unpad-Funktion etwas verwirrt werden würde …
– Kjir
21. Oktober 2013 um 10:43 Uhr
@Kjir, dann wird eine Sequenz des Wertes chr(BS) in der Länge BLOCK_SIZE an die Ursprungsdaten angehängt.
– Markus
22. Oktober 2013 um 4:14 Uhr
@Marcus der pad Funktion ist defekt (zumindest in Py3), ersetzen Sie durch s[:-ord(s[len(s)-1:])] damit es versionsübergreifend funktioniert.
– Torx
24. Februar 2014 um 11:06 Uhr
@Torxed-Pad-Funktion ist verfügbar in CryptoUtil.Padding.pad() mit pycryptodome (pycrypto followup)
– comte
11. April 2017 um 15:46 Uhr
Warum nicht einfach eine Zeichenkonstante als Füllzeichen verwenden?
– Inaimathi
25. April 2017 um 14:48 Uhr
optimieren
Lassen Sie mich auf Ihre Frage zu “Modi” eingehen. AES256 ist eine Art Blockchiffre. Es nimmt als Eingabe ein 32-Byte Schlüssel und eine 16-Byte-Zeichenfolge namens the Block und gibt einen Block aus. Wir verwenden AES in a Arbeitsweise um zu verschlüsseln. Die oben genannten Lösungen schlagen die Verwendung von CBC vor, was ein Beispiel ist. Eine andere heißt CTR und ist etwas einfacher zu verwenden:
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)
Dies wird oft als AES-CTR bezeichnet. Ich rate zur Vorsicht bei der Verwendung von AES-CBC mit PyCrypto. Der Grund dafür ist, dass Sie die angeben müssen Polsterschema, wie durch die anderen angegebenen Lösungen veranschaulicht. Im Allgemeinen, wenn Sie nicht sind sehr Achte auf die Polsterung, es gibt Anschläge die die Verschlüsselung vollständig brechen!
Nun ist es wichtig zu beachten, dass der Schlüssel a sein muss zufälliger 32-Byte-String; ein Passwort nicht genügen. Normalerweise wird der Schlüssel wie folgt generiert:
# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
Ein Schlüssel kann sein von einem Passwort abgeleitetzu:
# 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)
Einige der oben genannten Lösungen schlagen vor, SHA256 zum Ableiten des Schlüssels zu verwenden, aber dies wird im Allgemeinen in Betracht gezogen schlechte kryptografische Praxis. Kasse Wikipedia für mehr zu den Betriebsarten.
iv_int = int(binascii.hexlify(iv), 16) funktioniert nicht, ersetzen Sie es durch iv_int = int(binascii.hexlify(iv), 16) plus ‘import binascii’ und es sollte funktionieren (unter Python 3.x ), ansonsten tolle Arbeit!
– Walmond
20. Mai 2019 um 15:04 Uhr
Beachten Sie, dass es besser ist, Autehnticated Encryption-Modi als AES-GCM zu verwenden. GCM verwendet intern den CTR-Modus.
– kelalaka
15. September 2019 um 10:02 Uhr
Dieser Code verursacht “TypeError: Objekttyp kann nicht an C-Code übergeben werden”
– Da Woon Jung
21. November 2019 um 2:49 Uhr
@DaWoonJung Ich habe dasselbe mit Python 3.9.7 beobachtet. ciphertext = aes.encrypt(plaintext.encode(‘utf-8’)) und die Geheimtext bräuchte die decodieren (‘utf-8’) um den str, bytes Roundtrip abzuschließen.
– Stefan Scheller
1. Dezember 2021 um 7:55 Uhr
cenkarioz
Dankbar für die anderen Antworten, die mich inspirierten, aber nicht funktionierten.
Nachdem ich Stunden damit verbracht hatte, herauszufinden, wie es funktioniert, kam ich auf die Implementierung unten mit der neuesten PyCryptodomex Bibliothek (es ist eine andere Geschichte, wie ich es hinter Proxy unter Windows in einer virtuellen Umgebung eingerichtet habe. Puh)
Denken Sie bei der Arbeit an Ihrer Implementierung daran, die Schritte zum Auffüllen, Codieren und Verschlüsseln aufzuschreiben (und umgekehrt). Beim Ein- und Auspacken muss die Reihenfolge beachtet werden.
Für jemanden, der urlsafe_b64encode und urlsafe_b64decode verwenden möchte, hier ist die Version, die für mich funktioniert (nachdem ich einige Zeit mit dem Unicode-Problem verbracht habe)
Sie können aus einem beliebigen Passwort eine Passphrase erhalten, indem Sie eine kryptografische Hash-Funktion verwenden (NICHT Python ist eingebaut hash) wie SHA-1 oder SHA-256. Python unterstützt beides in seiner Standardbibliothek:
import hashlib
hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string
Sie können einen kryptografischen Hashwert einfach mit abschneiden [:16] oder [:24] und es behält seine Sicherheit bis zu der von Ihnen angegebenen Länge.
Eine andere Einstellung dazu (stark von den obigen Lösungen abgeleitet), aber
verwendet null zum Auffüllen
verwendet kein Lambda (war nie ein Fan)
getestet mit Python 2.7 und 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')
Dies funktioniert nicht, wenn das Eingangsbyte[] hat nachgestellte Nullen, weil Sie in der decrypt () -Funktion Ihre Füllnullen PLUS alle nachgestellten Nullen essen.
– Buzz Moschetti
31. Dezember 2017 um 18:48 Uhr
Ja, wie ich oben gesagt habe, diese Logik füllt mit Nullen. Wenn die Elemente, die Sie codieren/decodieren möchten, nachgestellte Nullen haben können, verwenden Sie besser eine der anderen Lösungen hier
– Mikee
1. Januar 2018 um 22:12 Uhr
** Warnung ** Diese Funktion funktioniert nicht mit lateinischen und Sonderzeichen.
– Elias Prado
18. Oktober 2021 um 13:11 Uhr
10333300cookie-checkVerschlüsseln und Entschlüsseln mit PyCrypto AES 256yes
os.urandom ist ermutigte auf der PyKrypto Webseite. Es verwendet Microsofts CryptGenRandom Funktion, die a ist CSPRNG
– Joel Vroom
28. November 2013 um 15:43 Uhr
oder
/dev/urandom
auf Unix– Joel Vroom
28. November 2013 um 16:19 Uhr
Nur zur Verdeutlichung, in diesem Beispiel Passphrase ist der Schlüssel die 128, 192 oder 256 Bit (16, 24 oder 32 Byte) sein können
– Markieren
21. April 2016 um 21:37 Uhr
Es könnte erwähnenswert sein, dass PyCrypto eine ist totes Projekt. Der letzte Commit ist von 2014. PyCryptodome sieht nach einem guten Drop-in-Ersatz aus
– Overdriver
7. August 2018 um 7:58 Uhr
Diese Frage ist alt, aber ich möchte (ab 2020) darauf hinweisen, dass Pycrypto wahrscheinlich veraltet ist und nicht mehr unterstützt wird. Ein Blick auf ihre Github-Seite (github.com/pycrypto/pycrypto), scheint ihr letzter Commit im Jahr 2014 gewesen zu sein. Ich wäre misstrauisch gegenüber der Verwendung von kryptografischer Software, die nicht mehr in der Entwicklung ist
– irritable_phd_syndrome
6. Juni 2020 um 3:40 Uhr