Ver- und entschlüsseln mit PyCrypto AES-256

Lesezeit: 12 Minuten

Benutzeravatar von Cyril N
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:

Dieses hier bei codekoala verwendet os.urandom, was von PyCrypto abgeraten wird.

Außerdem ist nicht garantiert, dass der Schlüssel, den ich der Funktion gebe, genau die erwartete Länge hat. Was kann ich tun, damit das passiert?

Außerdem gibt es mehrere Modi, welcher ist empfehlenswert? Ich weiß nicht was ich nehmen soll :/

Abschließend, was genau ist das IV? Kann ich eine andere IV zum Verschlüsseln und Entschlüsseln angeben oder führt dies zu einem anderen Ergebnis?

  • 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


Benutzeravatar von mnothic
mnotisch

Hier ist meine Implementierung, und sie funktioniert für mich mit einigen Korrekturen. Es 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

  • 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

  • Ich musste es ändern, um Zeichenfolgen verschlüsseln zu können, die Nicht-ASCII-Zeichen enthalten, wie z. Dazu musste ich len(s) in _pad() für ersetzen len(s.encode()) um die richtige Polsterung zu bekommen. Andernfalls würde ich die folgende Fehlermeldung erhalten: “Daten müssen im cbc-Modus auf 16-Byte-Grenze aufgefüllt werden”.

    – Samuel OD

    6. Mai 2021 um 8:08 Uhr

Benutzeravatar von Marcus
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? Du kannst den … benutzen MD5 Hash des Schlüssels, anstatt ihn direkt zu verwenden.

Darüber hinaus wird die 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 meine Umsetzung:

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

Benutzeravatar von tweaksp
optimieren

Lassen Sie mich auf Ihre Frage zu “Modi” eingehen. AES-256 ist eine Art von 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 obigen Lösungen empfehlen die Verwendung von CBC, das ist ein Beispiel. Ein anderer wird gerufen CTRund es ist etwas einfacher zu bedienen:

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 obigen Lösungen schlagen vor, zu verwenden SHA-256 zur Ableitung des Schlüssels, aber dies wird allgemein berücksichtigt schlechte kryptografische Praxis.
Schauen Sie sich Wikipedia an 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 der Geheimtext bräuchte die decodieren (‘utf-8’) um den str, bytes Roundtrip abzuschließen.

    – Stefan Scheller

    1. Dezember 2021 um 7:55 Uhr


  • Ich habe eine Fehlermeldung erhalten, als ich versuchte zu entschlüsseln. die Fehlermeldung: ‘Bytes’-Objekt hat kein Attribut ‘encode’. aber wenn ich typ iv überprüfe, heißt es . wie kann man das beheben?

    – HelpMeInDjango

    30. November 2022 um 6:47 Uhr

Benutzeravatar von cenkarioz
cenkarioz

Ich bin dankbar für die anderen Antworten, die mich inspiriert haben, aber es hat bei mir nicht funktioniert.

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 geschafft habe, es hinter dem Proxy unter Windows in einer virtuelle Umgebung… puh)

Es wird an Ihrer Implementierung gearbeitet. Denken Sie daran, die Schritte zum Auffüllen, Codieren und Verschlüsseln aufzuschreiben (und umgekehrt). Sie müssen ein- und auspacken und dabei die Reihenfolge beachten.

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'))

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)

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:]))

Benutzeravatar von nneonneo
nneonneo

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

1439780cookie-checkVer- und entschlüsseln mit PyCrypto AES-256

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy