Verschlüsseln und Entschlüsseln mit PyCrypto AES 256


171

Ich versuche, mit PyCrypto zwei Funktionen zu erstellen, die zwei Parameter akzeptieren: die Nachricht und den Schlüssel, und dann die Nachricht zu verschlüsseln / entschlüsseln.

Ich habe im Internet mehrere Links gefunden, die mir helfen sollen, aber jeder hat Fehler:

Dieser bei codekoala verwendet os.urandom, was von PyCrypto nicht empfohlen 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, um dies zu erreichen?

Es gibt auch mehrere Modi, welcher wird empfohlen? Ich weiß nicht, was ich verwenden soll: /

Was genau ist die IV? Kann ich eine andere IV zum Ver- und Entschlüsseln bereitstellen, oder führt dies zu einem anderen Ergebnis?

Bearbeiten : Der Codeteil wurde entfernt, da er nicht sicher war.


12
os.urandom wird ermutigt , auf der PyCrypto Website. Es verwendet die CryptGenRandom- Funktion von Microsoft, eine CSPRNG
Joel Vroom

5
oder /dev/urandomauf Unix
Joel Vroom

2
Nur um zu klären, in diesem Beispiel Passworts das ist Schlüssel , die 128 sein können, 192 oder 256 Bits (16, 24 oder 32 Byte)
Mark

4
Es könnte erwähnenswert sein, dass PyCrypto ein totes Projekt ist . Das letzte Commit stammt aus dem Jahr 2014. PyCryptodome scheint ein guter Ersatz zu sein
Overdrivr

1
Diese Frage ist alt, aber ich möchte darauf hinweisen (ab 2020), dass Pycrypto wahrscheinlich veraltet ist und nicht mehr unterstützt wird. Wenn man sich die Github-Seite ( github.com/pycrypto/pycrypto ) ansieht , scheint es, dass ihr letztes Commit im Jahr 2014 war. Ich würde es ablehnen, kryptografische Software zu verwenden, die sich nicht mehr in der Entwicklung befindet
irritable_phd_syndrom

Antworten:


151

Hier ist meine Implementierung und arbeitet 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:])]

14
Ich weiß, dass dies schon eine Weile her ist, aber ich denke, diese Antwort kann einige Verwirrung stiften. Diese Funktion verwendet eine Blockgröße von 32 Byte (256 Byte), um Eingabedaten aufzufüllen, aber AES verwendet eine Blockgröße von 128 Bit. In AES256 ist der Schlüssel 256 Bit, aber nicht die Blockgröße.
Tannin

13
anders ausgedrückt, "self.bs" sollte entfernt und durch "AES.block_size" ersetzt werden
Alexis

2
Warum hast 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.
Tweaksp

5
@Chris - SHA256 gibt einen 32-Byte-Hash aus - ein Schlüssel mit perfekter Größe für AES256. Die Generierung / Ableitung eines Schlüssels wird als zufällig / sicher angenommen und sollte außerhalb des Bereichs des Verschlüsselungs- / Entschlüsselungscodes liegen. Das Hashing ist nur eine Garantie dafür, dass der Schlüssel mit der ausgewählten Verschlüsselung verwendet werden kann.
Zwer

2
in _pad self.bs ist ein Zugriff erforderlich und in _unpad nicht erforderlich
mnothic

149

Möglicherweise benötigen Sie die folgenden zwei Funktionen: pad- unpadAuffüllen (beim Verschlüsseln) und - Aufheben des Auffüllens (beim Entschlüsseln), 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:])]

Sie fragen also nach der Länge des Schlüssels? Sie können die md5sum des Schlüssels verwenden, anstatt sie direkt zu verwenden.

Nach meiner geringen Erfahrung mit PyCrypto wird die IV verwendet, um die Ausgabe einer Verschlüsselung zu verwechseln, wenn die Eingabe gleich ist. Daher wird die IV als zufällige Zeichenfolge ausgewählt und als Teil der Verschlüsselungsausgabe verwendet 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:] ))

1
Was passiert, wenn Sie eine Eingabe haben, die genau ein Vielfaches von BLOCK_SIZE ist? Ich denke, dass die Unpad-Funktion ein wenig verwirrt werden würde ...
Kjir

2
@Kjir, dann wird eine Folge von Werten chr (BS) in der Länge BLOCK_SIZE an die Ursprungsdaten angehängt.
Marcus

1
@Marcus die padFunktion ist defekt (zumindest in Py3), ersetzen Sie sie durch, s[:-ord(s[len(s)-1:])]damit sie versionübergreifend funktioniert .
Torxed

2
@Torxed Pad-Funktion ist in CryptoUtil.Padding.pad () mit pycryptodome (pycrypto Follow-up) verfügbar
comte

2
Warum nicht einfach eine Zeichenkonstante als Füllzeichen haben?
Inaimathi

16

Lassen Sie mich Ihre Frage zu "Modi" beantworten. AES256 ist eine Art Blockchiffre . Als Eingabe werden ein 32-Byte- Schlüssel und eine 16-Byte-Zeichenfolge verwendet, die als Block bezeichnet werden, und es wird ein Block ausgegeben. Wir verwenden AES in einer Betriebsart, um zu verschlüsseln. Die obigen Lösungen schlagen die Verwendung von CBC vor, was ein Beispiel ist. Eine andere heißt CTR und 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 würde zur Vorsicht bei der Verwendung von AES-CBC mit PyCrypto raten . Der Grund dafür ist, dass Sie das Auffüllschema angeben müssen , wie in den anderen angegebenen Lösungen dargestellt. Wenn Sie beim Auffüllen nicht sehr vorsichtig sind , gibt es im Allgemeinen Angriffe , die die Verschlüsselung vollständig aufheben!

Nun ist es wichtig zu beachten, dass der Schlüssel eine zufällige 32-Byte-Zeichenfolge sein muss . Ein Passwort reicht nicht aus. 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 auch aus einem Passwort abgeleitet werden :

# 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. Dies wird jedoch im Allgemeinen als schlechte kryptografische Praxis angesehen . Weitere Informationen zu den Betriebsarten finden Sie in Wikipedia .


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) ), sonst tolle Arbeit!
Valmond

Beachten Sie, dass es besser ist, die automatisierten Verschlüsselungsmodi als AES-GCM zu verwenden. GCM verwendet intern den CTR-Modus.
Kelalaka

Diese Codeursache "TypeError: Objekttyp <Klasse 'str'> kann nicht an C-Code übergeben werden"
Da Woon Jung

7

Für jemanden, der urlsafe_b64encode und urlsafe_b64decode verwenden möchte, ist hier 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:]))

6

Sie können eine Passphrase aus einem beliebigen Kennwort abrufen, indem Sie eine kryptografische Hash-Funktion ( NICHT Pythons integriert hash) wie SHA-1 oder SHA-256 verwenden. Python bietet Unterstützung für beide 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 nur mit [:16]oder abschneiden, [:24]und seine Sicherheit bleibt bis zu der von Ihnen angegebenen Länge erhalten.


13
Sie sollten keine Hash-Funktion der SHA-Familie zum Generieren eines Schlüssels aus einem Kennwort verwenden - siehe Coda Hales Aufsatz zu diesem Thema . Verwenden Sie stattdessen eine echte Schlüsselableitungsfunktion wie scrypt . (Coda Hales Aufsatz wurde vor der Veröffentlichung von scrypt geschrieben.)
Benjamin Barenblat

7
Wenn Sie für zukünftige Leser einen Schlüssel aus einer Passphrase ableiten möchten, suchen Sie nach PBKDF2. Es ist ziemlich einfach in Python zu verwenden ( pypi.python.org/pypi/pbkdf2 ). Wenn Sie jedoch nach Hash-Passwörtern suchen, ist bcrypt eine bessere Option.
C Fairweather

6

Dankbar für die anderen Antworten, die mich inspiriert haben, aber bei mir nicht funktioniert haben.

Nach stundenlang versucht , herauszufinden, wie es funktioniert, ich kam mit der Implementierung bis unten mit der neuesten PyCryptodomex Bibliothek (es ist ein andere Geschichte , wie ich es hinter Proxy einzurichten verwaltet werden , auf Windows, in einem virtualenv .. puh)

Arbeiten an Denken Sie bei Ihrer Implementierung daran, die Schritte zum Auffüllen, Codieren und Verschlüsseln aufzuschreiben (und umgekehrt). Sie müssen unter Berücksichtigung der Reihenfolge ein- und auspacken.

base64 importieren
Hashlib importieren
aus Cryptodome.Cipher importieren AES
aus Cryptodome.Random importiere get_random_bytes

__key__ = hashlib.sha256 (b'16-Zeichenschlüssel '). Digest ()

def verschlüsseln (roh):
    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 entschlüsseln (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'))

Vielen Dank für ein funktionierendes Beispiel mit den PyCryptodomeX-Bibliotheken. Das ist sehr hilfreich!
Ygramul

5

Zum Wohle anderer ist hier meine Entschlüsselungsimplementierung, die ich durch Kombinieren der Antworten von @Cyril und @Marcus erhalten habe. Dies setzt voraus, dass dies über eine HTTP-Anforderung mit dem angegebenen und Base64-codierten encryptedText eingeht.

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

5

Eine andere Einstellung dazu (stark abgeleitet von den obigen Lösungen) aber

  • verwendet null zum Auffüllen
  • verwendet kein Lambda (war noch 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 Eingabebyte [] nachgestellte Nullen enthält, da Sie in der Funktion decrypt () Ihre Padding-Nullen sowie alle nachfolgenden Nullen essen.
Buzz Moschetti

Ja, wie oben erwähnt, wird diese Logik mit Nullen aufgefüllt. Wenn die Elemente, die Sie codieren / decodieren möchten, möglicherweise nachgestellte Nullen haben, verwenden Sie besser eine der anderen Lösungen hier
MIkee

2

Es ist etwas spät, aber ich denke, das wird sehr hilfreich sein. Niemand erwähnt ein Verwendungsschema wie das Auffüllen mit PKCS # 7. Sie können stattdessen die vorherigen Funktionen zum Auffüllen (beim Verschlüsseln) und Entpacken (beim Entschlüsseln) verwenden. I liefert den vollständigen Quellcode unten.

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


Ich weiß nicht, wer die Antwort abgelehnt hat, aber ich wäre gespannt, warum. Vielleicht ist diese Methode nicht sicher? Eine Erklärung wäre toll.
Cyril N.

1
@ CyrilN. Diese Antwort legt nahe, dass das Hashing des Kennworts mit einem einzigen Aufruf von SHA-256 ausreicht. Ist es nicht. Sie sollten wirklich PBKDF2 oder ähnliches für die Schlüsselableitung von einem Kennwort mit einer großen Anzahl von Iterationen verwenden.
Artjom B.

Vielen Dank für das Detail @ArtjomB.!
Cyril N.

Ich habe einen Schlüssel und auch iv Schlüssel mit 44 Länge. Wie kann ich deine Funktionen nutzen ?! Alle Algorithmen im Internet, die ich gefunden habe, haben Probleme mit der Länge meines Vektorschlüssels
mahshid.r


2

Ich habe beide Cryptound PyCryptodomexBibliothek benutzt und es ist blitzschnell ...

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)

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

10
Bitte geben Sie nicht nur Code an, sondern erklären Sie auch, was Sie tun und warum dies besser ist / was der Unterschied zu vorhandenen Antworten ist.
Florian Koch

Ersetzen Sie die md5.new (Schlüssel) .digest () durch md5 (Schlüssel) .digest (), und es funktioniert wie ein Zauber!
Ein STEFANI
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.