it-swarm.com.de

Einfache Möglichkeit, einen String nach einem Passwort zu verschlüsseln?

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", bei dem es sich um ein Kennwort in meinem Quellcode handelt. Auf diese Weise kann ich ein Word-Dokument mit einem Kennwort verschlüsseln/entschlüsseln.

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

108
RexE

Angenommen, Sie suchen nur nach einer einfachen Verschleierung, die die sehr Gelegenheitsbeobachter, 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. So 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)

Dekodieren ist so ziemlich dasselbe, außer dass Sie den Schlüssel subtrahieren.

Es ist viel schwieriger zu brechen, wenn die Zeichenfolgen, die Sie codieren, kurz sind und/oder wenn die Länge der verwendeten Passphrase schwer zu erraten ist.

Wenn Sie nach etwas Kryptografischem suchen, ist PyCrypto wahrscheinlich die beste Wahl, obwohl die vorherigen Antworten einige Details übersehen: Im EZB-Modus in PyCrypto muss Ihre Nachricht ein Vielfaches von 16 Zeichen lang sein. Also musst du auffüllen. Wenn Sie sie auch als URL-Parameter verwenden möchten, verwenden Sie base64.urlsafe_b64_encode() und nicht den Standard. 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 Schicht der Verschleierung für Ihre Bedürfnisse ausreicht, bevor Sie diese verwenden. Der Wikipedia-Artikel, auf den ich verlinkt habe, enthält detaillierte Anweisungen zum Auflösen der Chiffre, sodass jeder mit mäßiger Entschlossenheit diese leicht auflösen kann.

64
smehmood

Da Sie ausdrücklich angeben, dass Sie Unbekanntheit und nicht Sicherheit wünschen, werden wir es vermeiden, Sie wegen der Schwäche dessen, was Sie vorschlagen, zu tadeln :)

Also, mit PyCrypto:

from Crypto.Cipher import AES
import base64

msg_text = 'test some plain text here'.rjust(32)
secret_key = '1234567890123456' # create new & store somewhere safe

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

Wenn jemand Zugriff auf Ihre Datenbank und Ihre Codebasis erhält, kann er die verschlüsselten Daten entschlüsseln. Bewahren Sie Ihren secret_key gut auf!

65
Will

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

Hier arbeiten Codier-/Decodierfunktionen.

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)
47
qneill

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 sind erforderlich, da Python 3 Zeichenfolgen/Byte-Arrays in zwei verschiedene Konzepte aufgeteilt und ihre APIs entsprechend aktualisiert hat.

46
Ryan Barrett

Python hat keine eingebauten Verschlüsselungsschemata, nein. Sie sollten verschlüsselte Datenspeicherung auch ernst nehmen. triviale Verschlüsselungsschemata, die ein Entwickler als unsicher ansieht, und ein Spielzeugschema können von einem weniger erfahrenen Entwickler für ein sicheres Schema gehalten werden. Wenn Sie verschlüsseln, verschlüsseln Sie ordnungsgemäß.

Sie müssen jedoch nicht viel arbeiten, um ein ordnungsgemäßes Verschlüsselungsschema zu implementieren. Verwenden Sie zunächst das Kryptografierad nicht neu erfinden eine vertrauenswürdige Kryptografiebibliothek, um dies für Sie zu erledigen. Für Python 3 ist diese vertrauenswürdige Bibliothek cryptography .

Ich empfehle auch, dass die Ver- und Entschlüsselung für Bytes gilt. kodiere zuerst Textnachrichten in Bytes; stringvalue.encode() codiert nach UTF8, was mit bytesvalue.decode() leicht wieder rückgängig gemacht werden kann.

Last but not least sprechen wir beim Ver- und Entschlüsseln von Schlüsseln, nicht von Passwörtern. Ein Schlüssel sollte nicht für den Menschen einprägsam sein. Er wird an einem geheimen Ort gespeichert, ist jedoch maschinenlesbar, wohingegen ein Kennwort häufig für den Menschen lesbar und auswendig zu lernen ist. Sie können can mit ein wenig Sorgfalt einen Schlüssel aus einem Passwort ableiten.

Damit jedoch eine Webanwendung oder ein Prozess in einem Cluster ohne menschliche Aufmerksamkeit ausgeführt wird, um die Ausführung fortzusetzen, möchten Sie einen Schlüssel verwenden. Passwörter sind für den Fall vorgesehen, dass 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 Bibliothek cryptography enthält das Fernet-Rezept , ein Best-Practice-Rezept für die Verwendung von Kryptografie. Fernet ist ein offener Standard , das bereits in einer Vielzahl von Programmiersprachen implementiert ist und AES-CBC-Verschlüsselung mit Versionsinformationen, einem Zeitstempel und einer HMAC-Signatur zur Verhinderung von Nachrichtenmanipulationen für Sie enthält.

Fernet macht es sehr einfach, Nachrichten zu verschlüsseln und zu entschlüsseln und halten Sie sicher. Dies ist die ideale Methode zum Verschlüsseln von Daten mit einem Geheimnis.

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

Der von Fernet generierte Schlüssel ist ein bytes -Objekt mit URL und dateisicheren base64-Zeichen.

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 Fernet.encrypt() oder Fernet.decrypt() auf. , sowohl die zu verschlüsselnde Klartextnachricht als auch das verschlüsselte Token sind bytes Objekte.

Die Funktionen encrypt() und decrypt() sehen folgendermaßen aus:

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 Passwort anstelle eines geheimen Schlüssels verwenden, vorausgesetzt, Sie verwenden eine starke Schlüsselableitungsmethode . Sie müssen dann den Salt- und den HMAC-Iterationszähler 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'

Durch das Einbeziehen des Salt in die Ausgabe kann ein zufälliger Salt-Wert verwendet werden, wodurch sichergestellt wird, dass die verschlüsselte Ausgabe unabhängig von der erneuten Verwendung des Passworts oder der Wiederholung von Nachrichten vollständig zufällig ist. Durch die Einbeziehung der Iterationsanzahl wird sichergestellt, dass Sie die CPU-Leistung im Laufe der Zeit anpassen können, ohne dass ältere Nachrichten entschlüsselt werden können.

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

Wenn Sie ein kürzeres, aber dennoch einigermaßen sicheres Kennwort auswählen, wird dieses Schema nicht beeinträchtigt. Sie reduzieren lediglich die Anzahl der möglichen Werte, die ein Brute-Force-Angreifer durchsuchen müsste. Stellen Sie sicher, dass Sie ein Kennwort, das für Ihre Sicherheitsanforderungen stark genug ist auswählen.

Alternativen

Undeutlich machen

Eine Alternative ist nicht zu verschlüsseln. Versuchen Sie nicht, nur eine Verschlüsselung mit geringer Sicherheit oder eine selbst erstellte 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 warten, die Illusion von Sicherheit vermitteln, die schlimmer ist als gar keine Sicherheit.

Wenn alles, was Sie brauchen, Dunkelheit ist, nur base64 die Daten; Für URL-sichere Anforderungen ist die Funktion base64.urlsafe_b64encode() in Ordnung. Verwenden Sie hier kein Passwort, verschlüsseln Sie es einfach und Sie sind fertig. Fügen Sie höchstens etwas 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))

Dadurch wird b'Hello world!' Zu b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Nur Integrität

Wenn Sie nur sicherstellen möchten, dass die Daten vertrauenswürdig sind, unverändert nachdem sie an einen nicht vertrauenswürdigen Client gesendet und zurückerhalten wurden, können Sie die Daten signieren Verwenden Sie dazu die Bibliothek hmac mit SHA1 (immer noch gilt als sicher für HMAC-Signaturen ) 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 dies, um Daten zu signieren, fügen Sie dann die Signatur mit den Daten hinzu und senden Sie diese an den Client. Wenn Sie die Daten zurückerhalten, teilen Sie die Daten und die Unterschrift und überprüfen Sie sie. Ich habe den Standardalgorithmus auf SHA256 eingestellt, daher benötigen Sie einen 32-Byte-Schlüssel:

key = secrets.token_bytes(32)

Vielleicht möchten Sie sich die itsdangerous library ansehen, die dies alles mit Serialisierung und Deserialisierung in verschiedenen Formaten verpackt.

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 Ihre System-Unsinn-Daten nicht füttern, damit Ihr Dienst in Kreisen mit schlechten Eingaben ausgeführt wird, da der Chiffretext signiert ist.

Die Galois/Counter-Modus-Blockverschlüsselung erzeugt Chiffretext und ein Tag, um denselben Zweck zu erfüllen, kann also verwendet werden, um denselben Zwecken zu dienen. Der Nachteil ist, dass es im Gegensatz zu Fernet kein einfach zu verwendendes Standardrezept gibt, das auf anderen Plattformen wiederverwendet werden kann. AES-GCM verwendet auch kein Padding, sodass dieser Verschlüsselungs-Chiffretext der Länge der Eingabenachricht entspricht (während Fernet/AES-CBC Nachrichten zu Blöcken mit 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 hinzugefügt, um die gleichen Time-to-Live-Use-Cases 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 die Vorgehensweise, die Alles ist in Ordnung folgt, wenn auch falsch. Dies ist die cryptography -Version, 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, Wenn Sie es als globales Modul speichern, wird es beim nächsten Aufruf von Python) erneut generiert, sodass der gesamte Chiffretext nicht mehr entschlüsselt werden kann.

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 inkorrekte Behandlung des IV-Werts durch All'S Vаиітy kann zu einem Datenverlust führen oder dazu, dass alle verschlüsselten Nachrichten nicht mehr gelesen werden können, da die IV verloren geht. Die Verwendung von Fernet schützt Sie vor solchen Fehlern.

AES EZB - nicht sicher

Wenn Sie zuvor AES ECB-Verschlüsselung implementiert haben und dies in Python 3 weiterhin unterstützen müssen, können Sie dies auch mit cryptography tun. Die gleichen Einschränkungen gelten, EZB ist nicht sicher genug für reale Anwendungen. Neuimplementierung dieser Antwort für Python 3, automatisches Auffüllen hinzufügen:

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 soll lediglich veranschaulichen, dass cryptography die gängigen kryptografischen Bausteine ​​verarbeiten kann, auch die, die Sie eigentlich nicht verwenden sollten.

40
Martijn Pieters

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

Was ist los mit XOR Verschlüsselung?

https://crypto.stackexchange.com/questions/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 schmutzige Arbeit 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 aufzufüllen:

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

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

Gutschrift an https://stackoverflow.com/a/2490376/241294 für die Base64-Codierungs-/Decodierungsfunktionen (Ich bin ein python Neuling).

24
poida

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 = '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 https://bugs.python.org/issue4329 (TypeError: Die Zeichenzuordnung muss eine Ganzzahl, None oder Unicode zurückgeben), verwenden Sie str (cipher), während Sie wie folgt decodieren

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

In [13]: encode("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
10

Arbeitende Codierungs-/Decodierungsfunktionen in python3 (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)
8
Christian

Vielen Dank für einige tolle Antworten. Es gibt nichts Originelles zum Hinzufügen, aber hier sind einige fortschreitende Überarbeitungen von qneills Antwort unter Verwendung einiger nützlicher Python=) - Funktionen. Ich hoffe, Sie stimmen zu, dass sie den Code vereinfachen und verdeutlichen.

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()-- Verknüpfen 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)

listen mit Hilfe eines Listenverständnisses erstellen

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)

Häufig sind in Python überhaupt keine Listenindizes erforderlich. 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 ein paar 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
6
Nick

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 speichern möchten - Sie verlieren nur das Wörterbuch und die Verhinderung von 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 simplecrypt vorgeschlagen

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
4
HCLivess

Hinweis: Ich hatte Probleme mit Windows + Python 3.6 + alle Antworten mit pycrypto (nicht in der Lage pip install pycrypto Unter Windows) oder pycryptodome (Die Antworten hier mit from Crypto.Cipher import XOR sind fehlgeschlagen, weil XOR von dieser pycrypto-Verzweigung nicht unterstützt wird; und die Lösungen mit ... AES sind auch mit TypeError: Object type <class 'str'> cannot be passed to C code fehlgeschlagen. ]). Außerdem hat die Bibliothek simple-cryptpycrypto als Abhängigkeit, daher ist dies keine Option.


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

import base64
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

def encrypt(plaintext, password):
    f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode())))
    return f.encrypt(plaintext.encode()).decode()

def decrypt(ciphertext, password):
    f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode())))
    return f.decrypt(ciphertext.encode()).decode()

Verwendung:

>>> encrypt('John Doe', password='mypass')
'gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g=='

>>> decrypt('gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g==', password='mypass')
'John Doe'

Hinweis:

3
Basj

Bei einer anderen Implementierung von @ qneill-Code, die eine 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
2
ahmed

Dies funktioniert, aber die Passwortlänge sollte genau 8 Sein. 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
2
Pratik Deoghare

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

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

2
Alan

Externe Bibliotheken bieten Verschlüsselungsalgorithmen mit geheimen Schlüsseln.

Beispielsweise bietet das Cypher -Modul in PyCrypto 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 Python Wrapper für OpenSSL und bietet (unter anderem) eine umfassende Kryptografie-Bibliothek für allgemeine Zwecke. Enthalten sind symmetrische Chiffren (wie AES).

1
gimel

Wer auch immer hierher kam (und der Kopfgeldjäger) schien nach Einleimern mit wenig Setup zu suchen, die andere Antworten nicht liefern. Also stelle ich base64 vor.

Denken Sie jetzt daran, dass dies nur eine grundlegende Verschleierung ist und in ** KEIN WEG ZUR SICHERHEIT ** angegeben 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:

  • sie möchten mehr/weniger Byte-zu-String-Codierung/-Decodierung selbst durchführen, abhängig von Ihrer E/A. Schauen Sie sich bytes() und bytes::decode() an
  • base64 ist leicht an den verwendeten Zeichentypen zu erkennen und endet häufig mit =. Leute wie ich dekodieren sie in der Javascript-Konsole, 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 (wegen 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 Elemente identisch. Das Plus ist, dass die Daten mehr verschleiert werden. Wenn Sie es anders herum machen, ist der Datenteil für alle genau gleich, unabhängig vom Schlüssel.

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

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'
1
towc

wenn Sie eine sichere Verschlüsselung wünschen:

für python 2 solltest du keyczar verwenden http://www.keyczar.org/

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

in beiden Fällen wird eine Schlüsselverstärkung verwendet, die sie sicherer macht als die meisten anderen Antworten. und da sie so einfach zu bedienen sind, möchten Sie sie vielleicht auch dann verwenden, wenn die Sicherheit nicht kritisch ist ...

0
andrew cooke

Also, als es wird nichts geschäftskritisches verschlüsselt, und Sie möchten nur obsfuscation verschlüsseln.

Lassen Sie mich Caesers Chiffre präsentieren

enter image description here

Caesars Chiffre oder Caesar Shift ist eine der einfachsten und bekanntesten Verschlüsselungstechniken. Es ist eine Art von Substitutions-Chiffre, bei der jeder Buchstabe im Klartext durch einen Buchstaben mit einer festgelegten Anzahl von Stellen im Alphabet ersetzt wird. Beispielsweise würde bei einer Linksverschiebung von 3 D durch A ersetzt, E würde zu B und so weiter.

Beispielcode für Ihre 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 entspricht Ihren Anforderungen und ist einfach und macht die Kodierung Ding'y '.

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

0
Yash Mishra