Einfache Möglichkeit, eine Zeichenfolge anhand eines Passworts zu codieren?

122

Verfügt Python über eine integrierte, einfache Methode zum Codieren / Decodieren von Zeichenfolgen mithilfe eines Kennworts?

Etwas wie das:

>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'

Die Zeichenfolge "John Doe" wird also als "sjkl28cn2sx0" verschlüsselt. Um die ursprüngliche Zeichenfolge zu erhalten, würde ich diese Zeichenfolge mit dem Schlüssel 'mypass' "entsperren", der ein Kennwort in meinem Quellcode ist. Ich möchte, dass ich auf diese Weise ein Word-Dokument mit einem Kennwort verschlüsseln / entschlüsseln kann.

Ich möchte diese verschlüsselten Zeichenfolgen als URL-Parameter verwenden. Mein Ziel ist die Verschleierung, nicht die starke Sicherheit. Es wird nichts Missionskritisches verschlüsselt. Mir ist klar, dass ich eine Datenbanktabelle zum Speichern von Schlüsseln und Werten verwenden könnte, aber ich versuche, minimalistisch zu sein.

RexE
quelle
28
Der Begriff "Passwort" ist hier unangemessen. Sie verwenden dies als kryptografischen SCHLÜSSEL und sollten diese Terminologie verwenden, um Verwirrung in Ihren Fragen sowie in Dokumenten, Kommentaren, Spezifikationen, Testplänen usw. zu vermeiden.
Jim Dennis
2
"Ich möchte, dass ich auf diese Weise ein Word-Dokument mit einem Kennwort verschlüsseln / entschlüsseln kann." Word verfügt bereits über eine integrierte Option zum Verschlüsseln Ihrer Dokumente, wenn Sie nur Word-Dokumente verschlüsseln müssen.
Byron Filer
2
Interessanterweise nach dieser Forschungsarbeit auf Passwort - Speicher Fallen wie diese Entwickler , den Stack - Überlauf verwenden neigen dazu , weniger sicheren Code zu erzeugen. Ich frage mich warum?
Wald
Auch sollte man diese Antwort von Sicherheit
SE
Man implementiert Codierung / Decodierung nicht einfach
Glücksfall3r

Antworten:

70

Angenommen, Sie suchen nur nach einer einfachen Verschleierung, die die Dinge für den sehr zufälligen Beobachter verdeckt , und Sie möchten keine Bibliotheken von Drittanbietern verwenden. Ich würde so etwas wie die Vigenere-Chiffre empfehlen. Es ist eine der stärksten der einfachen alten Chiffren.

Vigenère-Chiffre

Es ist schnell und einfach zu implementieren. Etwas wie:

import base64

def encode(key, string):
    encoded_chars = []
    for i in xrange(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return base64.urlsafe_b64encode(encoded_string)

Die Dekodierung ist ziemlich gleich, außer dass Sie den Schlüssel subtrahieren.

Es ist viel schwieriger zu brechen, wenn die von Ihnen codierten Zeichenfolgen kurz sind und / oder wenn es schwierig ist, die Länge der verwendeten Passphrase zu erraten.

Wenn Sie nach etwas Kryptografischem suchen, ist PyCrypto wahrscheinlich die beste Wahl, obwohl frühere Antworten einige Details übersehen: Der EZB-Modus in PyCrypto erfordert, dass Ihre Nachricht ein Vielfaches von 16 Zeichen lang ist. Also musst du auffüllen. Wenn Sie sie auch als URL-Parameter verwenden möchten, verwenden Siebase64.urlsafe_b64_encode() anstelle des Standardparameters. Dies ersetzt einige der Zeichen im base64-Alphabet durch URL-sichere Zeichen (wie der Name schon sagt).

Sie sollten jedoch ABSOLUT sicher sein, dass diese sehr dünne Verschleierungsschicht für Ihre Bedürfnisse ausreicht, bevor Sie diese verwenden. Der Wikipedia-Artikel, auf den ich verlinkt habe, enthält detaillierte Anweisungen zum Brechen der Chiffre, sodass jeder mit mäßiger Entschlossenheit sie leicht brechen kann.

smehmood
quelle
6
Ich habe das Skript von smehmood repariert und die Dekodierungsfunktion gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester
3
Beachtung! Der Code von smehmood und der Fix von Adrian Mester funktionieren beide nur für Zeichenfolgen mit Zeichen aus dem unteren ASCII-Bereich! Siehe Vorrang von% Operator, Unicode-Eingabe usw. Siehe qneills Antwort für
Arbeitscode
2
@smehmood Ich erhalte den folgenden Fehler'str' object cannot be interpreted as an integer
Rohit Khatri
3
"für i in xrange (string)" muss möglicherweise in "for i in xrange (len (string))"
geändert werden
2
Encoder und Decoder für Python 2 und 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
Iman
71

Da Sie ausdrücklich angeben, dass Sie Dunkelheit und nicht Sicherheit wünschen, werden wir es vermeiden, Sie wegen der Schwäche Ihrer Vorschläge zu tadeln :)

Verwenden von PyCrypto:

import base64
from Crypto.Cipher import AES

msg_text = b'test some plain text here'.rjust(32)
secret_key = b'1234567890123456'

cipher = AES.new(secret_key,AES.MODE_ECB) # never use ECB in strong systems obviously
encoded = base64.b64encode(cipher.encrypt(msg_text))
print(encoded)
decoded = cipher.decrypt(base64.b64decode(encoded))
print(decoded)

Wenn jemand Ihre Datenbank und Ihre Codebasis in den Griff bekommt, kann er die verschlüsselten Daten entschlüsseln. Schützen Sie sich secret_key!

Wille
quelle
3
Ich denke nicht, dass dies funktionieren wird, wenn msg_text nicht ein Vielfaches von 16 Bytes lang ist, da für die AES-Verschlüsselung Blöcke mit einem Vielfachen von 16 Bytes erforderlich sind. Eine funktionierende Implementierung für msg_text beliebiger Länge müsste der Zeichenfolge eine Auffüllung hinzufügen, um ein Vielfaches von 16 zu erhalten.
Tohster
6
Ein Beispiel mit Auffüllen: paste.ubuntu.com/11024555 Es funktioniert mit einem beliebigen Passwort und einer beliebigen Nachrichtenlänge.
Iman
3
@Ethan nein, diese spezielle encryptFunktion ist Stateful dlitz.net/software/pycrypto/api/current/…, daher sollten Sie nicht versuchen, sie wiederzuverwenden.
Will
1
@ Will +1 Danke für die Info und den Link. Hat mich in Zukunft vor einer sehr teuren Fehlerbehebung bewahrt.
Ethan
3
re - "Verwenden Sie die EZB offensichtlich nie in starken Systemen": Ich experimentiere nur zu meinem eigenen Spaß damit. aber ich habe den obigen Kommentar in Ihrem Code gesehen. Für diejenigen von uns mit sehr geringem Sicherheits- / Verschlüsselungs- / informationstheoretischen Hintergrund, warum das "nie verwenden"? Vielleicht braucht eine andere Frage ... oder vielleicht gibt es einen Link darauf.
Trevor Boyd Smith
67

Python hat keine integrierten Verschlüsselungsschemata, nein. Sie sollten auch die verschlüsselte Datenspeicherung ernst nehmen. Triviale Verschlüsselungsschemata, die ein Entwickler als unsicher versteht, und ein Spielzeugschema können von einem weniger erfahrenen Entwickler durchaus als sicheres Schema verwechselt werden. Wenn Sie verschlüsseln, verschlüsseln Sie ordnungsgemäß.

Sie müssen jedoch nicht viel arbeiten, um ein ordnungsgemäßes Verschlüsselungsschema zu implementieren. Zunächst einmal nicht neu erfinden das Rad Kryptographie , verwenden Sie eine vertrauenswürdige Kryptographie - Bibliothek dies für Sie zu behandeln. Für Python 3 ist diese vertrauenswürdige Bibliothek cryptography.

Ich empfehle auch, dass die Ver- und Entschlüsselung für Bytes gilt . zuerst Textnachrichten in Bytes codieren; stringvalue.encode()codiert in UTF8 und kann mit einfach wieder zurückgesetzt werden bytesvalue.decode().

Last but not least sprechen wir beim Ver- und Entschlüsseln von Schlüsseln , nicht von Passwörtern. Ein Schlüssel sollte nicht menschlich einprägsam sein. Er wird an einem geheimen Ort gespeichert, ist jedoch maschinenlesbar, während ein Kennwort häufig von Menschen lesbar und gespeichert werden kann. Mit ein wenig Sorgfalt können Sie einen Schlüssel aus einem Passwort ableiten.

Für eine Webanwendung oder einen Prozess, der in einem Cluster ohne menschliche Aufmerksamkeit ausgeführt wird, um ihn weiterhin auszuführen, möchten Sie einen Schlüssel verwenden. Passwörter gelten, wenn nur ein Endbenutzer Zugriff auf die spezifischen Informationen benötigt. Selbst dann sichern Sie die Anwendung normalerweise mit einem Kennwort und tauschen dann verschlüsselte Informationen mit einem Schlüssel aus, der möglicherweise an das Benutzerkonto angehängt ist.

Symmetrische Schlüsselverschlüsselung

Fernet - AES CBC + HMAC, dringend empfohlen

Die cryptographyBibliothek enthält das Fernet-Rezept , ein Best-Practice-Rezept für die Verwendung von Kryptografie. Fernet ist ein offener Standard mit fertigen Implementierungen in einer Vielzahl von Programmiersprachen und bietet Ihnen AES CBC-Verschlüsselung mit Versionsinformationen, einem Zeitstempel und einer HMAC-Signatur, um Manipulationen an Nachrichten zu verhindern.

Fernet macht es sehr einfach, Nachrichten und zu verschlüsseln und zu entschlüsseln Sie zu schützen. Es ist die ideale Methode, um Daten mit einem Geheimnis zu verschlüsseln.

Ich empfehle Ihnen Fernet.generate_key(), einen sicheren Schlüssel zu generieren. Sie können auch ein Kennwort verwenden (nächster Abschnitt), aber ein vollständiger 32-Byte-Geheimschlüssel (16 Byte zum Verschlüsseln plus weitere 16 Byte für die Signatur) ist sicherer als die meisten Kennwörter, die Sie sich vorstellen können.

Der Schlüssel, den Fernet generiert, ist ein bytesObjekt mit URL- und dateisicheren Base64-Zeichen, also druckbar:

from cryptography.fernet import Fernet

key = Fernet.generate_key()  # store in a secure location
print("Key:", key.decode())

Um Nachrichten zu verschlüsseln oder zu entschlüsseln, erstellen Sie eine Fernet()Instanz mit dem angegebenen Schlüssel und rufen Sie das Fernet.encrypt()oder auf Fernet.decrypt(). Sowohl die zu verschlüsselnde Klartextnachricht als auch das verschlüsselte Token sind bytesObjekte.

encrypt()und decrypt()Funktionen würden aussehen wie:

from cryptography.fernet import Fernet

def encrypt(message: bytes, key: bytes) -> bytes:
    return Fernet(key).encrypt(message)

def decrypt(token: bytes, key: bytes) -> bytes:
    return Fernet(key).decrypt(token)

Demo:

>>> key = Fernet.generate_key()
>>> print(key.decode())
GZWKEhHGNopxRdOHS4H4IyKhLQ8lwnyU7vRLrM3sebY=
>>> message = 'John Doe'
>>> encrypt(message.encode(), key)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> token = _
>>> decrypt(token, key).decode()
'John Doe'

Fernet mit Passwort - Schlüssel vom Passwort abgeleitet, schwächt die Sicherheit etwas

Sie können ein Kennwort anstelle eines geheimen Schlüssels verwenden, vorausgesetzt, Sie verwenden eine Methode zur Ableitung eines starken Schlüssels . Sie müssen dann die Anzahl der Salt- und HMAC-Iterationen in die Nachricht aufnehmen, damit der verschlüsselte Wert nicht mehr mit Fernet kompatibel ist, ohne zuerst Salt, Count und Fernet-Token zu trennen:

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

backend = default_backend()
iterations = 100_000

def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
    """Derive a secret key from a given password and salt"""
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(), length=32, salt=salt,
        iterations=iterations, backend=backend)
    return b64e(kdf.derive(password))

def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
    salt = secrets.token_bytes(16)
    key = _derive_key(password.encode(), salt, iterations)
    return b64e(
        b'%b%b%b' % (
            salt,
            iterations.to_bytes(4, 'big'),
            b64d(Fernet(key).encrypt(message)),
        )
    )

def password_decrypt(token: bytes, password: str) -> bytes:
    decoded = b64d(token)
    salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
    iterations = int.from_bytes(iter, 'big')
    key = _derive_key(password.encode(), salt, iterations)
    return Fernet(key).decrypt(token)

Demo:

>>> message = 'John Doe'
>>> password = 'mypass'
>>> password_encrypt(message.encode(), password)
b'9Ljs-w8IRM3XT1NDBbSBuQABhqCAAAAAAFyJdhiCPXms2vQHO7o81xZJn5r8_PAtro8Qpw48kdKrq4vt-551BCUbcErb_GyYRz8SVsu8hxTXvvKOn9QdewRGDfwx'
>>> token = _
>>> password_decrypt(token, password).decode()
'John Doe'

Das Einbeziehen des Salt in die Ausgabe ermöglicht die Verwendung eines zufälligen Salt-Werts, wodurch sichergestellt wird, dass die verschlüsselte Ausgabe unabhängig von der Wiederverwendung des Kennworts oder der Wiederholung von Nachrichten garantiert vollständig zufällig ist. Durch das Einbeziehen der Iterationszahl wird sichergestellt, dass Sie die CPU-Leistung im Laufe der Zeit anpassen können, ohne die Fähigkeit zu verlieren, ältere Nachrichten zu entschlüsseln.

Ein Passwort allein kann so sicher sein wie ein Fernet 32-Byte-Zufallsschlüssel, vorausgesetzt, Sie generieren ein richtig zufälliges Passwort aus einem Pool ähnlicher Größe. 32 Bytes ergeben 256 ^ 32 Schlüssel. Wenn Sie also ein Alphabet mit 74 Zeichen (26 obere, 26 untere, 10 Ziffern und 12 mögliche Symbole) verwenden, sollte Ihr Passwort mindestens math.ceil(math.log(256 ** 32, 74))== 42 Zeichen lang sein. Eine gut ausgewählte größere Anzahl von HMAC-Iterationen kann jedoch den Mangel an Entropie etwas mildern, da dies es für einen Angreifer viel teurer macht, sich brutal hineinzuzwingen.

Wissen Sie nur, dass die Auswahl eines kürzeren, aber dennoch einigermaßen sicheren Passworts dieses Schema nicht lähmt, sondern nur die Anzahl der möglichen Werte verringert, die ein Brute-Force-Angreifer durchsuchen müsste. Stellen Sie sicher, dass Sie ein Kennwort auswählen, das Ihren Sicherheitsanforderungen entspricht .

Alternativen

Verschleierung

Eine Alternative ist nicht zu verschlüsseln . Seien Sie nicht versucht, nur eine Verschlüsselung mit geringer Sicherheit oder eine hausgemachte Implementierung von beispielsweise Vignere zu verwenden. Diese Ansätze bieten keine Sicherheit, können jedoch einem unerfahrenen Entwickler, der die Aufgabe hat, Ihren Code in Zukunft zu verwalten, die Illusion von Sicherheit geben, die schlimmer ist als gar keine Sicherheit.

Wenn Sie nur Dunkelheit benötigen, geben Sie einfach die Daten ein. Für URL-sichere Anforderungen ist die base64.urlsafe_b64encode()Funktion in Ordnung. Verwenden Sie hier kein Passwort, codieren Sie einfach und Sie sind fertig. Fügen Sie höchstens eine Komprimierung hinzu (wie zlib):

import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

def obscure(data: bytes) -> bytes:
    return b64e(zlib.compress(data, 9))

def unobscure(obscured: bytes) -> bytes:
    return zlib.decompress(b64d(obscured))

Dies wird b'Hello world!'zu b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Nur Integrität

Wenn Sie nur sicherstellen müssen, dass die Daten unverändert bleiben, nachdem sie an einen nicht vertrauenswürdigen Client gesendet und zurück empfangen wurden, möchten Sie die Daten signieren. Sie können die hmacBibliothek hierfür mit SHA1 verwenden (noch) als sicher für die HMAC-Signatur angesehen ) oder besser:

import hmac
import hashlib

def sign(data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    assert len(key) >= algorithm().digest_size, (
        "Key must be at least as long as the digest size of the "
        "hashing algorithm"
    )
    return hmac.new(key, data, algorithm).digest()

def verify(signature: bytes, data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    expected = sign(data, key, algorithm)
    return hmac.compare_digest(expected, signature)

Verwenden Sie diese Option, um Daten zu signieren, fügen Sie dann die Signatur den Daten hinzu und senden Sie diese an den Client. Wenn Sie die Daten zurückerhalten, teilen Sie Daten und Signatur auf und überprüfen Sie sie. Ich habe den Standardalgorithmus auf SHA256 gesetzt, daher benötigen Sie einen 32-Byte-Schlüssel:

key = secrets.token_bytes(32)

Vielleicht möchten Sie sich die itsdangerousBibliothek ansehen , die dies alles mit Serialisierung und De-Serialisierung in verschiedenen Formaten zusammenfasst.

Verwendung der AES-GCM-Verschlüsselung zur Bereitstellung von Verschlüsselung und Integrität

Fernet baut auf AEC-CBC mit einer HMAC-Signatur auf, um die Integrität der verschlüsselten Daten sicherzustellen. Ein böswilliger Angreifer kann die Unsinndaten Ihres Systems nicht füttern, um Ihren Dienst in Kreisen mit schlechten Eingaben zu betreiben, da der Chiffretext signiert ist.

Die Blockverschlüsselung im Galois / Counter-Modus erzeugt Chiffretext und ein Tag für denselben Zweck und kann daher für dieselben Zwecke verwendet werden. Der Nachteil ist, dass es im Gegensatz zu Fernet kein einfach zu verwendendes Einheitsrezept gibt, das auf anderen Plattformen wiederverwendet werden kann. AES-GCM verwendet auch kein Auffüllen, daher entspricht dieser Verschlüsselungs-Chiffretext der Länge der Eingabenachricht (während Fernet / AES-CBC Nachrichten in Blöcke fester Länge verschlüsselt, wodurch die Nachrichtenlänge etwas verdeckt wird).

AES256-GCM verwendet das übliche 32-Byte-Geheimnis als Schlüssel:

key = secrets.token_bytes(32)

dann benutze

import binascii, time
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidTag

backend = default_backend()

def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes:
    current_time = int(time.time()).to_bytes(8, 'big')
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.GCM(iv), backend=backend)
    encryptor = cipher.encryptor()
    encryptor.authenticate_additional_data(current_time)
    ciphertext = encryptor.update(message) + encryptor.finalize()        
    return b64e(current_time + iv + ciphertext + encryptor.tag)

def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes:
    algorithm = algorithms.AES(key)
    try:
        data = b64d(token)
    except (TypeError, binascii.Error):
        raise InvalidToken
    timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:]
    if ttl is not None:
        current_time = int(time.time())
        time_encrypted, = int.from_bytes(data[:8], 'big')
        if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted:
            # too old or created well before our current time + 1 h to account for clock skew
            raise InvalidToken
    cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend)
    decryptor = cipher.decryptor()
    decryptor.authenticate_additional_data(timestamp)
    ciphertext = data[8 + len(iv):-16]
    return decryptor.update(ciphertext) + decryptor.finalize()

Ich habe einen Zeitstempel eingefügt, um dieselben Time-to-Live-Anwendungsfälle zu unterstützen, die Fernet unterstützt.

Andere Ansätze auf dieser Seite in Python 3

AES CFB - wie CBC, jedoch ohne Pad

Dies ist der Ansatz, dem All І s Vаиітy folgt, wenn auch falsch. Dies ist die cryptographyVersion, aber beachten Sie, dass ich die IV in den Chiffretext einbeziehe . Sie sollte nicht als global gespeichert werden (die Wiederverwendung einer IV schwächt die Sicherheit des Schlüssels, und das Speichern als globales Modul bedeutet, dass sie neu generiert wird der nächste Python-Aufruf, der den gesamten Chiffretext unverschlüsselbar macht):

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_cfb_encrypt(message, key):
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return b64e(iv + ciphertext)

def aes_cfb_decrypt(ciphertext, key):
    iv_ciphertext = b64d(ciphertext)
    algorithm = algorithms.AES(key)
    size = algorithm.block_size // 8
    iv, encrypted = iv_ciphertext[:size], iv_ciphertext[size:]
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted) + decryptor.finalize()

Diesem fehlt die zusätzliche Panzerung einer HMAC-Signatur, und es gibt keinen Zeitstempel. Sie müssten diese selbst hinzufügen.

Das Obige zeigt auch, wie einfach es ist, grundlegende Kryptographie-Bausteine ​​falsch zu kombinieren. Die falsche Behandlung des IV-Werts durch Vаиітy kann zu einer Datenverletzung führen oder dazu, dass alle verschlüsselten Nachrichten nicht lesbar sind, weil die IV verloren geht. Die Verwendung von Fernet schützt Sie stattdessen vor solchen Fehlern.

AES EZB - nicht sicher

Wenn Sie zuvor die AES-EZB-Verschlüsselung implementiert haben und diese in Python 3 weiterhin unterstützen müssen, können Sie dies auch weiterhin tun cryptography. Die gleichen Einschränkungen gelten, EZB ist nicht sicher genug für reale Anwendungen . Implementieren Sie diese Antwort für Python 3 erneut und fügen Sie die automatische Behandlung des Auffüllens hinzu:

from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_ecb_encrypt(message, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(cipher.algorithm.block_size).padder()
    padded = padder.update(msg_text.encode()) + padder.finalize()
    return b64e(encryptor.update(padded) + encryptor.finalize())

def aes_ecb_decrypt(ciphertext, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder()
    padded = decryptor.update(b64d(ciphertext)) + decryptor.finalize()
    return unpadder.update(padded) + unpadder.finalize()

Auch hier fehlt die HMAC-Signatur, und Sie sollten die EZB sowieso nicht verwenden. Das Obige dient lediglich der Veranschaulichung, dass cryptographydie gängigen kryptografischen Bausteine ​​verarbeitet werden können, auch diejenigen, die Sie eigentlich nicht verwenden sollten.

Martijn Pieters
quelle
51

Das in der Vigenere- Verschlüsselungsantwort von @ smehmood erwähnte "encoded_c" sollte "key_c" sein.

Hier arbeiten Codierungs- / Decodierungsfunktionen.

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

Haftungsausschluss: Wie aus den Kommentaren hervorgeht, sollte dies nicht zum Schutz von Daten in einer realen Anwendung verwendet werden, es sei denn, Sie lesen dies und haben nichts dagegen, mit Anwälten zu sprechen:

Was ist falsch an der XOR-Verschlüsselung?

qneill
quelle
2
Sehr nützlich, danke. Ich habe eine Python 3-Version unten gepostet (sie sah in den Kommentaren hässlich aus)
Ryan Barrett
1
"Schneiers Gesetz" : Jeder, vom ahnungslosesten Amateur bis zum besten Kryptographen, kann einen Algorithmus erstellen, den er selbst nicht brechen kann. Verwenden Sie dies nicht, es ist nicht einmal in der Nähe zu sichern.
Zaph
3
Toll! Diese Version funktioniert auch für Zeichenfolgen mit Akzenten, während die Version von @ smehmood dies nicht tut. Anwendungsbeispiel: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)Es funktioniert gut.
Basj
2
Hier ist ein Sublime-Text-Plugin, das auf diesem Code basiert und das einfache Codieren / Decodieren von Text mit STRG + UMSCHALT + P und dann "Eeencode" oder "Dddecode" ermöglicht.
Basj
2
@basj Danke für das Beispiel
sk03
49

Hier ist eine Python 3-Version der Funktionen aus @qneills Antwort :

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc).encode()).decode()

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc).decode()
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

Die zusätzlichen Codierungen / Decodierungen werden benötigt, da Python 3 Zeichenfolgen / Byte-Arrays in zwei verschiedene Konzepte aufgeteilt und ihre APIs aktualisiert hat, um dies widerzuspiegeln.

Ryan Barrett
quelle
4
Vielen Dank Ryan, fwiw Sie Tippfehler @qniell
qneill
3
Für diejenigen, die sich fragen, sind die Unterschiede die .encode()).decode(). in der Rückkehr von encode()und .decode()in der zweiten Zeile in decode().
RolfBly
2
Hmm, der codierte Code ist nicht wirklich eindeutig. Ich habe einen Test durchgeführt und er zeigt, dass jeder Codeschlüssel von 11,22,33,44, ..., 88,99,111,222, ... immer einen anderen Code wie zuvor hat. Aber ich schätze es
Hzzkygcs
1
@ Ryan Barrett, ist es möglich, die Richtigkeit des Passworts beim Dekodieren zu überprüfen. Angenommen, ich sende einem eine codierte Zeichenfolge, die den Schlüssel kennt. Was passiert, wenn er den Schlüssel mit einem Tippfehler eingibt? Die Dekodierung gibt ihm immer noch eine "dekodierte" Zeichenfolge, aber es ist nicht die richtige, wie er es beurteilen kann?
Heinz
26

Haftungsausschluss: Wie in den Kommentaren erwähnt, sollte dies nicht zum Schutz von Daten in einer realen Anwendung verwendet werden.

Was ist falsch an der XOR-Verschlüsselung?

/crypto/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


Wie bereits erwähnt, enthält die PyCrypto-Bibliothek eine Reihe von Chiffren. Die XOR "Chiffre" kann verwendet werden, um die Drecksarbeit zu erledigen, wenn Sie es nicht selbst tun möchten:

from Crypto.Cipher import XOR
import base64

def encrypt(key, plaintext):
  cipher = XOR.new(key)
  return base64.b64encode(cipher.encrypt(plaintext))

def decrypt(key, ciphertext):
  cipher = XOR.new(key)
  return cipher.decrypt(base64.b64decode(ciphertext))

Die Chiffre funktioniert wie folgt, ohne den Klartext auffüllen zu müssen:

>>> encrypt('notsosecretkey', 'Attack at dawn!')
'LxsAEgwYRQIGRRAKEhdP'

>>> decrypt('notsosecretkey', encrypt('notsosecretkey', 'Attack at dawn!'))
'Attack at dawn!'

Dank an https://stackoverflow.com/a/2490376/241294 für die Base64-Codierungs- / Decodierungsfunktionen (ich bin ein Python-Neuling).

Poida
quelle
Hinweis: Das Crypto-Modul wird in Python3 von installiertem Pycrptop und nicht von Crypto installiert. sudo pip3 install pycrypto.
Nikhil VJ
2
Hinweis: Pycrypto konnte an meinem Ende nicht auf Herokuapp installiert werden. Ich habe diesen Beitrag gefunden. Es scheint zu sagen, dass das Pycrypto-Paket durch ein anderes namens Pycryptodome Insteal ersetzt wurde und dass die XOR-Methode veraltet ist: github.com/digitalocean/netbox/issues/1527
Nikhil VJ
2
Verwenden Sie diese Methode niemals . Beachten Sie die Beschreibung dieser 'Chiffre' in der Dokumentation : XOR-Spielzeugchiffre, XOR ist eine der einfachsten Stream-Chiffren. Die Ver- und Entschlüsselung erfolgt durch XOR-Daten mit einem Schlüsselstrom, der durch Verketten des Schlüssels erstellt wird. Verwenden Sie es nicht für echte Anwendungen! .
Martijn Pieters
@MartijnPieters du hast recht. Hoffentlich hat meine Bearbeitung diesen Punkt klar gemacht.
Poida
12

Hier ist eine Implementierung der URL-sicheren Ver- und Entschlüsselung mit AES (PyCrypto) und base64.

import base64
from Crypto import Random
from Crypto.Cipher import AES

AKEY = b'mysixteenbytekey' # AES key must be either 16, 24, or 32 bytes long

iv = Random.new().read(AES.block_size)

def encode(message):
    obj = AES.new(AKEY, AES.MODE_CFB, iv)
    return base64.urlsafe_b64encode(obj.encrypt(message))

def decode(cipher):
    obj2 = AES.new(AKEY, AES.MODE_CFB, iv)
    return obj2.decrypt(base64.urlsafe_b64decode(cipher))

Wenn Sie auf ein Problem wie dieses stoßen,TypeError: character mapping must return integer, None or unicode verwenden Sie https://bugs.python.org/issue4329 ( ) str(cipher)beim Dekodieren Folgendes:

return obj2.decrypt(base64.urlsafe_b64decode(str(cipher)))

Prüfung:

In [13]: encode(b"Hello World")
Out[13]: b'67jjg-8_RyaJ-28='

In [14]: %timeit encode("Hello World")
100000 loops, best of 3: 13.9 µs per loop

In [15]: decode(b'67jjg-8_RyaJ-28=')
Out[15]: b'Hello World'

In [16]: %timeit decode(b'67jjg-8_RyaJ-28=')
100000 loops, best of 3: 15.2 µs per loop
Alles ist gut
quelle
Fehler mit Windows x64 + Python 3.6 + PyCryptodome (da Pycrypto veraltet ist) : TypeError: Object type <class 'str'> cannot be passed to C code.
Basj
@Basj aww sorry .. Ich benutze kein Windows, daher kann ich keine Korrektur vornehmen.
Alles ist gut 13.
Sie nicht die IV erzeugen und speichern sie auf Modulebene, müssen Sie umfassen die IV in der Chiffretext - Nachricht zurück! Sie haben jetzt zwei Probleme eingeführt: Wenn Sie den Python-Prozess neu starten, erhalten Sie eine neue IV, die es unmöglich macht, zuvor verschlüsselte Nachrichten zu entschlüsseln. In der Zwischenzeit verwenden Sie die IV für mehrere Nachrichten erneut, wodurch die Sicherheit effektiv wieder auf EZB-Ebene reduziert wird.
Martijn Pieters
1
@ AllІSVаиітy Gelöst mit b'...', habe ich die Antwort für zukünftige Referenz bearbeitet!
Basj
8

Funktionierende Codierungs- / Decodierungsfunktionen in Python3 (nur sehr wenig von qneills Antwort angepasst):

def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = (ord(clear[i]) + ord(key_c)) % 256
        enc.append(enc_c)
    return base64.urlsafe_b64encode(bytes(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + enc[i] - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)
Christian
quelle
8

Vielen Dank für einige tolle Antworten. Nichts Originelles zum Hinzufügen, aber hier sind einige progressive Änderungen der Antwort von qneill unter Verwendung einiger nützlicher Python-Funktionen. Ich hoffe, Sie stimmen zu, dass sie den Code vereinfachen und klarstellen.

import base64


def qneill_encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def qneill_decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

enumerate()- Koppeln Sie die Elemente in einer Liste mit ihrem Index

Durchlaufen Sie die Zeichen in einer Zeichenfolge

def encode_enumerate(key, clear):
    enc = []
    for i, ch in enumerate(clear):
        key_c = key[i % len(key)]
        enc_c = chr((ord(ch) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def decode_enumerate(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i, ch in enumerate(enc):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(ch) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

Erstellen Sie Listen mit einem Listenverständnis

def encode_comprehension(key, clear):
    enc = [chr((ord(clear_char) + ord(key[i % len(key)])) % 256)
                for i, clear_char in enumerate(clear)]
    return base64.urlsafe_b64encode("".join(enc))


def decode_comprehension(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(ch) - ord(key[i % len(key)])) % 256)
           for i, ch in enumerate(enc)]
    return "".join(dec)

In Python werden Listenindizes häufig überhaupt nicht benötigt. Beseitigen Sie Schleifenindexvariablen vollständig mit zip und cycle:

from itertools import cycle


def encode_zip_cycle(key, clear):
    enc = [chr((ord(clear_char) + ord(key_char)) % 256)
                for clear_char, key_char in zip(clear, cycle(key))]
    return base64.urlsafe_b64encode("".join(enc))


def decode_zip_cycle(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(enc_char) - ord(key_char)) % 256)
                for enc_char, key_char in zip(enc, cycle(key))]
    return "".join(dec)

und einige Tests ...

msg = 'The quick brown fox jumps over the lazy dog.'
key = 'jMG6JV3QdtRh3EhCHWUi'
print('cleartext: {0}'.format(msg))
print('ciphertext: {0}'.format(encode_zip_cycle(key, msg)))

encoders = [qneill_encode, encode_enumerate, encode_comprehension, encode_zip_cycle]
decoders = [qneill_decode, decode_enumerate, decode_comprehension, decode_zip_cycle]

# round-trip check for each pair of implementations
matched_pairs = zip(encoders, decoders)
assert all([decode(key, encode(key, msg)) == msg for encode, decode in matched_pairs])
print('Round-trips for encoder-decoder pairs: all tests passed')

# round-trip applying each kind of decode to each kind of encode to prove equivalent
from itertools import product
all_combinations = product(encoders, decoders)
assert all(decode(key, encode(key, msg)) == msg for encode, decode in all_combinations)
print('Each encoder and decoder can be swapped with any other: all tests passed')

>>> python crypt.py
cleartext: The quick brown fox jumps over the lazy dog.
ciphertext: vrWsVrvLnLTPlLTaorzWY67GzYnUwrSmvXaix8nmctybqoivqdHOic68rmQ=
Round-trips for encoder-decoder pairs: all tests passed
Each encoder and decoder can be swapped with any other: all tests passed
Nick
quelle
Sehr schönes @Nick, gute Progression von Pythonismen und Tests auch booten. Um die richtige Anerkennung zu geben, habe ich gerade einen Fehler in Smehmoods ursprünglicher Antwort stackoverflow.com/a/2490718/468252 behoben .
11.
4

Wenn Sie sicher sein möchten, können Sie Fernet verwenden, das kryptografisch einwandfrei ist. Sie können ein statisches "Salz" verwenden, wenn Sie es nicht separat aufbewahren möchten - Sie verlieren nur die Verhinderung von Wörterbuch- und Regenbogenangriffen. Ich habe es gewählt, weil ich lange oder kurze Passwörter auswählen kann, was mit AES nicht so einfach ist.

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64

#set password
password = "mysecretpassword"
#set message
message = "secretmessage"

kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt="staticsalt", iterations=100000, backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)

#encrypt
encrypted = f.encrypt(message)
print encrypted

#decrypt
decrypted = f.decrypt(encrypted)
print decrypted

Wenn das zu kompliziert ist, hat jemand eine einfache Verschlüsselung vorgeschlagen

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
HCLivess
quelle
Generieren Sie einfach ein Salt und fügen Sie es in das Verschlüsselungsergebnis ein, damit wiederholte Passwörter und Nachrichten immer noch zu einer zufälligen Ausgabe führen. Schließen Sie auch den Iterationswert ein, um den Algorithmus zukunftssicher zu machen, können Sie jedoch weiterhin Nachrichten mit einer anderen Iterationszahl entschlüsseln.
Martijn Pieters
3

Wer auch immer hierher kam (und der Bountier), schien nach Einzeilern mit wenig Setup zu suchen, die andere Antworten nicht liefern. Also schlage ich base64 vor.

Denken Sie jetzt daran, dass dies nur eine grundlegende Verschleierung ist und in ** KEINER WEISE FÜR SICHERHEIT ** in Ordnung ist , aber hier sind einige Einzeiler:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data, key):
    return urlsafe_b64encode(bytes(key+data, 'utf-8'))

def decode(enc, key):
    return urlsafe_b64decode(enc)[len(key):].decode('utf-8')

print(encode('hi', 'there')) # b'dGhlcmVoaQ=='
print(decode(encode('hi', 'there'), 'there')) # 'hi'

Ein paar Dinge zu beachten:

  • Abhängig von Ihrer E / A sollten Sie sich mehr oder weniger mit der Codierung / Decodierung von Byte zu String befassen. Schauen Sie in bytes()undbytes::decode()
  • base64 ist leicht an den verwendeten Zeichentypen zu erkennen und endet häufig mit =Zeichen. Leute wie ich gehen absolut darum herum, sie in der Javascript-Konsole zu dekodieren, wenn wir sie auf Websites sehen. Es ist so einfach wie btoa(string)(js)
  • Die Reihenfolge ist Schlüssel + Daten, wie in b64, welche Zeichen am Ende erscheinen, hängt davon ab, welche Zeichen am Anfang stehen (aufgrund von Byte-Offsets. Wikipedia hat einige nette Erklärungen). In diesem Szenario ist der Anfang der codierten Zeichenfolge für alle mit diesem Schlüssel codierten Zeichenfolgen gleich. Das Plus ist, dass die Daten stärker verschleiert werden. Wenn Sie es umgekehrt machen, ist der Datenteil für alle gleich, unabhängig vom Schlüssel.

Wenn das, was Sie wollten, nicht einmal einen Schlüssel irgendeiner Art, sondern nur eine Verschleierung benötigte, können Sie noch einmal base64 ohne Schlüssel verwenden:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data):
    return urlsafe_b64encode(bytes(data, 'utf-8'))

def decode(enc):
    return urlsafe_b64decode(enc).decode()

print(encode('hi')) # b'aGk='
print(decode(encode('hi'))) # 'hi'
Schlepptau
quelle
2
Ja, wenn Sie sich nicht um Sicherheit kümmern, ist base64 viel besser als zu verschlüsseln.
Martijn Pieters
Über die anderen Antworten, die keine Einzeiler sind: Das ist nicht der Punkt der Frage. Sie fordern zwei Funktionen zum Aufrufen an. Und Fernet(key).encrypt(message)ist nur ein Ausdruck, genau wie Ihr base64-Aufruf.
Martijn Pieters
Und Sie sollten das insgesamt entfernenkey . Viele Entwickler werden aus Stack Overflow kopieren und einfügen, ohne darauf zu achten, und davon ausgehen, dass der Schlüssel geheim ist. Wenn Sie es einschließen müssen, verwenden Sie es zumindest nicht und warnen oder lösen Sie eine Ausnahme aus, wenn Sie es trotzdem verwenden. Unterschätzen Sie nicht die Dummheit der Copy-and-Paste-Kultur und Ihre Verantwortung, vernünftige Funktionen bereitzustellen.
Martijn Pieters
3

Ich werde 4 Lösungen geben:

1) Verwenden der Fernet-Verschlüsselung mit cryptographyBibliothek

Hier ist eine Lösung mit dem Paket cryptography, die Sie wie gewohnt installieren können pip install cryptography:

import base64
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

def cipherFernet(password):
    key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password)
    return Fernet(base64.urlsafe_b64encode(key))

def encrypt1(plaintext, password):
    return cipherFernet(password).encrypt(plaintext)

def decrypt1(ciphertext, password):
    return cipherFernet(password).decrypt(ciphertext)

# Example:

print(encrypt1(b'John Doe', b'mypass'))  
# b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg=='
print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'mypass')) 
# b'John Doe'
try:  # test with a wrong password
    print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'wrongpass')) 
except InvalidToken:
    print('Wrong password')

Sie können sich mit Ihrem eigenen Salz, Ihrer Iterationszahl usw. anpassen. Dieser Code ist nicht weit von der Antwort von @ HCLivess entfernt, aber das Ziel ist es, einsatzbereit zu sein encryptund decryptFunktionen zu haben. Quelle: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Hinweis: Verwenden Sie .encode()und .decode()überall, wenn Sie Zeichenfolgen 'John Doe'anstelle von Bytes wie möchten b'John Doe'.


2) Einfache AES-Verschlüsselung mit CryptoBibliothek

Dies funktioniert mit Python 3:

import base64
from Crypto import Random
from Crypto.Hash import SHA256
from Crypto.Cipher import AES

def cipherAES(password, iv):
    key = SHA256.new(password).digest()
    return AES.new(key, AES.MODE_CFB, iv)

def encrypt2(plaintext, password):
    iv = Random.new().read(AES.block_size)
    return base64.b64encode(iv + cipherAES(password, iv).encrypt(plaintext))

def decrypt2(ciphertext, password):
    d = base64.b64decode(ciphertext)
    iv, ciphertext = d[:AES.block_size], d[AES.block_size:]
    return cipherAES(password, iv).decrypt(ciphertext)

# Example:    

print(encrypt2(b'John Doe', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'wrongpass'))  # wrong password: no error, but garbled output

Hinweis: Sie können entfernen base64.b64encodeund .b64decode/ oder wenn Sie keine textlesbare Ausgabe wünschen und / oder wenn Sie den Chiffretext trotzdem als Binärdatei auf der Festplatte speichern möchten.


3) AES mit einer besseren Funktion zur Ableitung von Passwortschlüsseln und der Möglichkeit zu testen, ob "falsches Passwort eingegeben" wurde, mit CryptoBibliothek

Die Lösung 2) mit AES "CFB-Modus" ist in Ordnung, hat jedoch zwei Nachteile: Die Tatsache, dass SHA256(password)dies mit einer Nachschlagetabelle leicht brutal erzwungen werden kann und dass es keine Möglichkeit gibt, zu testen, ob ein falsches Passwort eingegeben wurde. Dies wird hier durch die Verwendung von AES im "GCM-Modus" gelöst, wie in AES erläutert : Wie erkennt man, dass ein falsches Passwort eingegeben wurde? und Ist diese Methode, um zu sagen, dass das eingegebene Passwort falsch ist, sicher? ::

import Crypto.Random, Crypto.Protocol.KDF, Crypto.Cipher.AES

def cipherAES_GCM(pwd, nonce):
    key = Crypto.Protocol.KDF.PBKDF2(pwd, nonce, count=100000)
    return Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_GCM, nonce=nonce, mac_len=16)

def encrypt3(plaintext, password):
    nonce = Crypto.Random.new().read(16)
    return nonce + b''.join(cipherAES_GCM(password, nonce).encrypt_and_digest(plaintext))  # you case base64.b64encode it if needed

def decrypt3(ciphertext, password):
    nonce, ciphertext, tag = ciphertext[:16], ciphertext[16:len(ciphertext)-16], ciphertext[-16:]
    return cipherAES_GCM(password, nonce).decrypt_and_verify(ciphertext, tag)

# Example:

print(encrypt3(b'John Doe', b'mypass'))
print(decrypt3(b'\xbaN_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\xa50', b'mypass'))
try:
    print(decrypt3(b'\xbaN_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\xa50', b'wrongpass'))
except ValueError:
    print("Wrong password")

4) Verwenden von RC4 (keine Bibliothek erforderlich)

Angepasst von https://github.com/bozhu/RC4-Python/blob/master/rc4.py .

def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        yield S[(S[i] + S[j]) % 256]

def encryptRC4(plaintext, key, hexformat=False):
    key, plaintext = bytearray(key), bytearray(plaintext)  # necessary for py2, not for py3
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    keystream = PRGA(S)
    return b''.join(b"%02X" % (c ^ next(keystream)) for c in plaintext) if hexformat else bytearray(c ^ next(keystream) for c in plaintext)

print(encryptRC4(b'John Doe', b'mypass'))                           # b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a'
print(encryptRC4(b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a', b'mypass'))   # b'John Doe'

(Veraltet seit den letzten Änderungen, aber zur späteren Bezugnahme aufbewahrt): Ich hatte Probleme mit Windows + Python 3.6 + allen Antworten, die pycrypto( unter Windows nicht möglich pip install pycrypto) oder pycryptodome(die Antworten hier mit from Crypto.Cipher import XORfehlgeschlagen, weil XORdiese pycryptoAbzweigung nicht unterstützt wird ; und die lösungen mit auch ... AESfehlgeschlagen mit TypeError: Object type <class 'str'> cannot be passed to C code). Außerdem hat die Bibliothek simple-crypteine pycryptoAbhängigkeit, sodass dies keine Option ist.

Basj
quelle
1
Sie möchten die Anzahl der Salz- und Iterationen nicht fest codieren. Generieren Sie ein zufälliges Salt und machen Sie die Iterationszahl bei der Verschlüsselung konfigurierbar, nehmen Sie diese Informationen in das Verschlüsselungsergebnis auf und extrahieren und verwenden Sie die Werte bei der Entschlüsselung. Das Salt schützt Sie vor der trivialen Erkennung wiederverwendeter Passwörter für eine bestimmte Nachricht. Die Anzahl der Iterationen ist zukunftssicher für den Algorithmus.
Martijn Pieters
Ja, natürlich @MartijnPieters, aber das Ziel ist hier, einen einfachen Code für einfache Zwecke zu haben, wie von OP angefordert, mit zwei Parametern : Klartext + Passwort. Natürlich verwenden Sie für komplexere Szenarien (z. B. eine Datenbank) alle diese zusätzlichen Parameter.
Basj
Zusätzliche Parameter sind nicht erforderlich! Ich füge diese Informationen hinzu, die im undurchsichtigen Rückgabewert von codiert sind password_encrypt().
Martijn Pieters
@MartijnPieters Gute Lösung in der Tat.
Basj
2

Dies funktioniert, aber die Passwortlänge sollte genau sein 8. Dies ist einfach und erfordert pyDes .

from pyDes import *

def encode(data,password):
    k = des(password, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
    d = k.encrypt(data)
    return d

def decode(data,password):
    k = des(password, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
    d = k.decrypt(data)
    return d

x = encode('John Doe', 'mypass12')
y = decode(x,'mypass12')

print x
print y

AUSGABE:

³.\Þ\åS¾+æÅ`;Ê
John Doe
Pratik Deoghare
quelle
Verwenden Sie keine feste IV! Randomisieren Sie die IV und fügen Sie sie stattdessen dem Chiffretext hinzu. Andernfalls können Sie auch den EZB-Modus verwenden. wiederholte Klartextnachrichten sind ansonsten trivial zu erkennen.
Martijn Pieters
Außerdem scheint das pyDes-Projekt tot zu sein. Die Homepage ist weg und die letzte Veröffentlichung auf PyPI ist jetzt 9 Jahre alt.
Martijn Pieters
2

Bei einer anderen Implementierung von @ qneill-Code, die die CRC-Prüfsumme der ursprünglichen Nachricht enthält, wird eine Ausnahme ausgelöst, wenn die Prüfung fehlschlägt:

import hashlib
import struct
import zlib

def vigenere_encode(text, key):
    text = '{}{}'.format(text, struct.pack('i', zlib.crc32(text)))

    enc = []
    for i in range(len(text)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(text[i]) + ord(key_c)) % 256)
        enc.append(enc_c)

    return base64.urlsafe_b64encode("".join(enc))


def vigenere_decode(encoded_text, key):
    dec = []
    encoded_text = base64.urlsafe_b64decode(encoded_text)
    for i in range(len(encoded_text)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(encoded_text[i]) - ord(key_c)) % 256)
        dec.append(dec_c)

    dec = "".join(dec)
    checksum = dec[-4:]
    dec = dec[:-4]

    assert zlib.crc32(dec) == struct.unpack('i', checksum)[0], 'Decode Checksum Error'

    return dec
ahmed
quelle
2

Sie können AES verwenden, um Ihre Zeichenfolge mit einem Kennwort zu verschlüsseln. Sie sollten jedoch ein Kennwort wählen, das stark genug ist, damit die Leute nicht leicht erraten können, was es ist (Entschuldigung, ich kann nichts dagegen tun. Ich bin ein Möchtegern-Sicherheitsbeauftragter).

AES ist stark mit einer guten Schlüsselgröße, aber es ist auch einfach mit PyCrypto zu verwenden.

Alan
quelle
3
Danke Alan. Zur Verdeutlichung verschlüssele ich die Passwörter jedoch nicht selbst. Im obigen Beispiel verschlüssele ich die Zeichenfolge "John Doe" mit dem Kennwort "mypass", einem einfachen Kennwort, das ich in meinem Quellcode verwende. Benutzerkennwörter sind nicht beteiligt, ebenso wenig wie andere sehr vertrauliche Informationen. Ich habe meine Frage bearbeitet, um dies zu klären.
RexE
1
AES ist großartig, wenn es richtig verwendet wird. Es ist jedoch leicht, es falsch zu verwenden; Hier gibt es mindestens eine Antwort, die einen unsicheren Blockverschlüsselungsmodus verwendet, zwei weitere, die mit dem IV-Wert umgehen. Verwenden Sie besser eine gute Bibliothek mit einem genau definierten Rezept wie Fernet!
Martijn Pieters
Eigentlich ist das eine sehr kluge Beobachtung. Ich habe die IV einmal gefummelt.
Alan
0

Externe Bibliotheken bieten Verschlüsselungsalgorithmen für geheime Schlüssel.

Das CypherModul in PyCrypto bietet beispielsweise eine Auswahl vieler Verschlüsselungsalgorithmen:

  • Crypto.Cipher.AES
  • Crypto.Cipher.ARC2
  • Crypto.Cipher.ARC4
  • Crypto.Cipher.Blowfish
  • Crypto.Cipher.CAST
  • Crypto.Cipher.DES
  • Crypto.Cipher.DES3
  • Crypto.Cipher.IDEA
  • Crypto.Cipher.RC5
  • Crypto.Cipher.XOR

MeTooCrypto ist ein PythonWrapper für OpenSSL und bietet (unter anderem) eine umfassende Allzweck-Kryptografiebibliothek. Enthalten sind symmetrische Chiffren (wie AES).

Gimel
quelle
0

Wenn Sie eine sichere Verschlüsselung wünschen:

Für Python 2 sollten Sie keyczar verwenden. http://www.keyczar.org/

Für Python 3 habe ich, bis keyczar verfügbar ist, simple-crypt http://pypi.python.org/pypi/simple-crypt geschrieben

Beide verwenden eine Schlüsselverstärkung, wodurch sie sicherer sind als die meisten anderen Antworten hier. und da sie so einfach zu bedienen sind, möchten Sie sie vielleicht auch dann verwenden, wenn die Sicherheit nicht kritisch ist ...

Andrew Cooke
quelle
Aus dem Keyczar-Repository : Wichtiger Hinweis: Keyczar ist veraltet. Die Keyczar-Entwickler empfehlen Tink , aber es gibt keine Python-Version von Tink.
Martijn Pieters
0

Da also nichts Missionskritisches verschlüsselt wird und Sie nur zur Verschleierung verschlüsseln möchten .

Lassen Sie mich Caesers Chiffre präsentieren

Geben Sie hier die Bildbeschreibung ein

Caesars Chiffre oder Caesar-Verschiebung ist eine der einfachsten und bekanntesten Verschlüsselungstechniken. Es handelt sich um eine Art Substitutions-Chiffre, bei der jeder Buchstabe im Klartext durch einen Buchstaben mit einer festen Anzahl von Positionen im Alphabet ersetzt wird. Zum Beispiel würde bei einer Linksverschiebung von 3 D durch A ersetzt, E würde B werden und so weiter.

Beispielcode als Referenz:

def encrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) + s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) + s - 97) % 26 + 97) 

        return result 

    def decrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) - s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) - s - 97) % 26 + 97) 

        return result 

    #check the above function 
    text = "ATTACKATONCE"
    s = 4
    print("Text  : " + text) 
    print("Shift : " + str(s)) 
    print("Cipher: " + encrypt(text,s))
    print("Original text: " + decrypt(encrypt(text,s),s))

Vorteile: Es erfüllt Ihre Anforderungen und ist einfach und erledigt die Codierung "y".

Nachteil: Kann durch einfache Brute-Force-Algorithmen geknackt werden (höchst unwahrscheinlich, dass jemand versuchen würde, alle zusätzlichen Ergebnisse durchzugehen).

Yash Mishra
quelle
0

Hinzufügen eines weiteren Codes mit Decodierung und Codierung als Referenz

import base64

def encode(key, string):
    encoded_chars = []
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    arr2 = bytes(encoded_string, 'utf-8')
    return base64.urlsafe_b64encode(arr2)

def decode(key, string):
    encoded_chars = []
    string = base64.urlsafe_b64decode(string)
    string = string.decode('utf-8')
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) - ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return encoded_string

def main():
    answer = str(input("EorD"))
    if(answer in ['E']):
        #ENCODE
        file = open("D:\enc.txt")
        line = file.read().replace("\n", " NEWLINEHERE ")
        file.close()
        text = encode("4114458",line)
        fnew = open("D:\\new.txt","w+")
        fnew.write(text.decode('utf-8'))
        fnew.close()
    else:
        #DECODE
        file = open("D:\\new.txt",'r+')
        eline = file.read().replace("NEWLINEHERE","\n")
        file.close()
        print(eline)
        eline = eline.encode('utf-8')
        dtext=decode("4114458",eline)
        print(dtext)
        fnew = open("D:\\newde.txt","w+")
        fnew.write(dtext)
        fnew.close

if __name__ == '__main__':
    main()
Sriram Suruliandi
quelle