Un modo semplice per codificare una stringa in base a una password?


123

Python ha un modo semplice e integrato di codificare / decodificare le stringhe usando una password?

Qualcosa come questo:

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

Quindi la stringa "John Doe" viene crittografata come "sjkl28cn2sx0". Per ottenere la stringa originale, dovrei "sbloccare" quella stringa con la chiave "mypass", che è una password nel mio codice sorgente. Mi piacerebbe che questo fosse il modo in cui posso crittografare / decrittografare un documento di Word con una password.

Vorrei utilizzare queste stringhe crittografate come parametri URL. Il mio obiettivo è offuscare, non una forte sicurezza; non viene codificato nulla di mission critical. Mi rendo conto che potrei usare una tabella di database per memorizzare chiavi e valori, ma sto cercando di essere minimalista.


28
Il termine "password" qui non è appropriato. La stai usando come CHIAVE crittografica e dovresti usare quella terminologia per evitare confusione nelle tue domande, nonché in qualsiasi documento, commento, specifica, piano di test, ecc.
Jim Dennis

2
"Mi piacerebbe che questo fosse il modo in cui posso crittografare / decrittografare un documento di Word con una password.", Word ha già un'opzione incorporata per crittografare i tuoi documenti se hai solo bisogno di crittografare i documenti di Word.
Byron Filer

2
È interessante notare che, secondo questo documento di ricerca sulle insidie ​​dell'archiviazione delle password come questo , gli sviluppatori che utilizzano Stack Overflow tendono a produrre codice meno sicuro. Caspita, mi chiedo perché?
foresta

Inoltre, si dovrebbe leggere questa risposta dalla sicurezza.SE
kelalaka

Non si implementa semplicemente la codifica / decodifica
luckyging3r

Risposte:


70

Supponendo che tu stia cercando solo un semplice offuscamento che oscuri le cose all'osservatore molto casuale e non stai cercando di utilizzare librerie di terze parti. Consiglierei qualcosa come il cifrario Vigenere. È uno dei più forti dei semplici codici antichi.

Cifrario di Vigenère

È veloce e facile da implementare. Qualcosa di simile a:

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)

La decodifica è più o meno la stessa, tranne per la sottrazione della chiave.

È molto più difficile da rompere se le stringhe che stai codificando sono brevi e / o se è difficile indovinare la lunghezza della passphrase utilizzata.

Se stai cercando qualcosa di crittografico, PyCrypto è probabilmente la soluzione migliore, anche se le risposte precedenti trascurano alcuni dettagli: la modalità ECB in PyCrypto richiede che il tuo messaggio sia un multiplo di 16 caratteri di lunghezza. Quindi, devi tamponare. Inoltre, se desideri utilizzarli come parametri URL, utilizzabase64.urlsafe_b64_encode() , piuttosto che quello standard. Questo sostituisce alcuni dei caratteri dell'alfabeto base64 con caratteri sicuri per l'URL (come suggerisce il nome).

Tuttavia, si dovrebbe essere assolutamente certi che questo molto sottile strato di offuscamento è sufficiente per le vostre esigenze prima di utilizzare questo. L'articolo di Wikipedia a cui ho collegato fornisce istruzioni dettagliate per rompere il codice, quindi chiunque abbia una moderata quantità di determinazione potrebbe facilmente romperlo.


6
Ho corretto lo script di smehmood e aggiunto la funzione di decodifica gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester

3
Attenzione! Il codice di smehmood e la correzione di Adrian Mester funzionano entrambi solo per stringhe con caratteri dell'intervallo ascii inferiore! Vedi la precedenza dell'operatore%, l'input Unicode ecc. Vedi la risposta di qneill per il codice funzionante
le_m

2
@smehmood Ricevo il seguente errore 'str' object cannot be interpreted as an integer
Rohit Khatri

3
"for i in xrange (string)" potrebbe dover cambiare in "for i in xrange (len (string))"
user3113626

2
codificatore e decodificatore per python 2 e 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman

71

Poiché dichiari esplicitamente di volere l'oscurità e non la sicurezza, eviteremo di rimproverarti per la debolezza di ciò che suggerisci :)

Quindi, usando 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)

Se qualcuno si impossessa del tuo database e della tua base di codice, sarà in grado di decodificare i dati crittografati. Mantieni la tua secret_keycassaforte!


3
Non penso che funzionerà a meno che msg_text non sia un multiplo di 16 byte di lunghezza, poiché la crittografia AES richiede blocchi di lunghezza multipla di 16. Un'implementazione funzionante per msg_text di lunghezza arbitraria dovrebbe aggiungere il riempimento alla stringa per ottenere un multiplo di 16 di lunghezza.
tohster

6
Un esempio con riempimento: paste.ubuntu.com/11024555 Funziona con password e lunghezza del messaggio arbitrarie.
iman

3
@Ethan no, questa particolare encryptfunzione è stateful dlitz.net/software/pycrypto/api/current/… quindi non dovresti provare a riutilizzarla.
Sarà il

1
@ Will +1 Grazie per le informazioni e il collegamento. Mi ha salvato da una correzione di bug molto costosa in futuro.
Ethan

3
ri - "non usare mai ECB in sistemi forti ovviamente": sto solo sperimentando questo per il mio divertimento. ma ho visto il commento sopra nel tuo codice. per quelli di noi con un background minimo di sicurezza / crittografia / teoria dell'informazione, perché "non usare mai"? forse ha bisogno di un'altra domanda ... o forse c'è un link su di esso.
Trevor Boyd Smith

69

Python non ha schemi di crittografia incorporati, no. Dovresti anche prendere sul serio l'archiviazione dei dati crittografati; schemi di crittografia banali che uno sviluppatore considera insicuri e uno schema giocattolo potrebbe essere scambiato per uno schema sicuro da uno sviluppatore meno esperto. Se crittografa, crittografa correttamente.

Tuttavia, non è necessario lavorare molto per implementare uno schema di crittografia adeguato. Prima di tutto, non reinventare la ruota crittografica , usa una libreria di crittografia affidabile per gestirlo per te. Per Python 3, quella libreria affidabile ècryptography .

Raccomando anche che la crittografia e la decrittografia si applichino ai byte ; codificare prima i messaggi di testo in byte; stringvalue.encode()codifica in UTF8, facilmente ripristinato di nuovo utilizzando bytesvalue.decode().

Ultimo ma non meno importante, quando si crittografa e decrittografa, si parla di chiavi , non di password. Una chiave non dovrebbe essere memorabile dall'uomo, è qualcosa che si memorizza in un luogo segreto ma leggibile dalla macchina, mentre una password spesso può essere leggibile e memorizzata dall'uomo. È possibile ricavare una chiave da una password, con un po 'di attenzione.

Ma per un'applicazione Web o un processo in esecuzione in un cluster senza l'attenzione umana per continuare a eseguirlo, è necessario utilizzare una chiave. Le password servono quando solo un utente finale ha bisogno di accedere alle informazioni specifiche. Anche in questo caso, di solito proteggi l'applicazione con una password, quindi scambi le informazioni crittografate utilizzando una chiave, forse una allegata all'account utente.

Crittografia a chiave simmetrica

Fernet - AES CBC + HMAC, fortemente raccomandato

La cryptographylibreria include la ricetta Fernet , una ricetta delle migliori pratiche per l'utilizzo della crittografia. Fernet è uno standard aperto , con implementazioni pronte in un'ampia gamma di linguaggi di programmazione e racchiude la crittografia AES CBC per te con informazioni sulla versione, un timestamp e una firma HMAC per prevenire la manomissione dei messaggi.

Fernet rende molto facile crittografare e decrittografare i messaggi e tenerti al sicuro. È il metodo ideale per crittografare i dati con un segreto.

Ti consiglio di usare Fernet.generate_key() per generare una chiave sicura. Puoi anche usare una password (sezione successiva), ma una chiave segreta a 32 byte completa (16 byte con cui crittografare, più altri 16 per la firma) sarà più sicura della maggior parte delle password a cui potresti pensare.

La chiave che Fernet genera è un bytesoggetto con caratteri base64 URL e file safe, quindi stampabile:

from cryptography.fernet import Fernet

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

Per crittografare o decrittografare i messaggi, creare Fernet()un'istanza con la chiave fornita e chiamare Fernet.encrypt()oFernet.decrypt() , sia il messaggio di testo normale da crittografare che il token crittografato sono bytesoggetti.

encrypt()e le decrypt()funzioni sarebbero simili:

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 con password - chiave derivata dalla password, indebolisce un po 'la sicurezza

È possibile utilizzare una password invece di una chiave segreta, a condizione di utilizzare un metodo di derivazione della chiave efficace . È quindi necessario includere il sale e il conteggio delle iterazioni HMAC nel messaggio, quindi il valore crittografato non è più compatibile con Fernet senza prima separare salt, count e Fernet token:

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'

Includere il salt nell'output rende possibile utilizzare un valore salt casuale, che a sua volta garantisce che l'output crittografato sia completamente casuale indipendentemente dal riutilizzo della password o dalla ripetizione del messaggio. L'inclusione del conteggio delle iterazioni garantisce la possibilità di regolare gli aumenti delle prestazioni della CPU nel tempo senza perdere la capacità di decrittografare i messaggi meno recenti.

Una password da sola può essere sicura come una chiave casuale Fernet a 32 byte, a patto di generare una password adeguatamente casuale da un pool di dimensioni simili. 32 byte fornisce un numero di chiavi di 256 ^ 32, quindi se utilizzi un alfabeto di 74 caratteri (26 in alto, 26 in basso, 10 cifre e 12 possibili simboli), la tua password dovrebbe essere math.ceil(math.log(256 ** 32, 74))lunga almeno == 42 caratteri. Tuttavia, a ben selezionato di iterazioni HMAC può mitigare in qualche modo la mancanza di entropia poiché ciò rende molto più costoso per un attaccante penetrare con la forza bruta.

Sappi solo che la scelta di una password più corta ma comunque ragionevolmente sicura non paralizzerà questo schema, ma ridurrà semplicemente il numero di possibili valori che un aggressore di forza bruta dovrebbe cercare; assicurati di scegliere una password sufficientemente complessa per i tuoi requisiti di sicurezza .

alternative

oscuramento

Un'alternativa è non crittografare . Non essere tentato di utilizzare solo un codice a bassa sicurezza o un'implementazione casalinga di, ad esempio Vignere. Non c'è sicurezza in questi approcci, ma può dare a uno sviluppatore inesperto a cui è affidato il compito di mantenere il codice in futuro l'illusione della sicurezza, che è peggio di nessuna sicurezza.

Se tutto ciò di cui hai bisogno è l'oscurità, basare solo i dati; per requisiti sicuri per l'URL, la base64.urlsafe_b64encode()funzione va bene. Non utilizzare una password qui, codifica e il gioco è fatto. Al massimo, aggiungi un po 'di compressione (come 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))

Questo si trasforma b'Hello world!'in b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Solo integrità

Se tutto ciò di cui hai bisogno è un modo per assicurarti che i dati possano essere considerati attendibili per essere inalterati dopo essere stati inviati a un client non attendibile e ricevuti di nuovo, allora vuoi firmare i dati, puoi usare la hmaclibreria per questo con SHA1 (ancora considerato sicuro per la firma HMAC ) o meglio:

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)

Usalo per firmare i dati, quindi allega la firma con i dati e inviala al client. Quando ricevi indietro i dati, dividi i dati e la firma e verifica. Ho impostato l'algoritmo predefinito su SHA256, quindi avrai bisogno di una chiave a 32 byte:

key = secrets.token_bytes(32)

Potresti voler guardare la itsdangerouslibreria , che racchiude tutto questo con serializzazione e deserializzazione in vari formati.

Utilizzo della crittografia AES-GCM per fornire crittografia e integrità

Fernet si basa su AEC-CBC con una firma HMAC per garantire l'integrità dei dati crittografati; un malintenzionato non può fornire al tuo sistema dati senza senso per mantenere il tuo servizio impegnato in esecuzione in circoli con input errato, perché il testo cifrato è firmato.

La crittografia a blocchi in modalità Galois / Counter produce testo cifrato e un tag per avere lo stesso scopo, quindi può essere utilizzato per gli stessi scopi. Lo svantaggio è che, a differenza di Fernet, non esiste una ricetta unica e facile da usare da riutilizzare su altre piattaforme. Inoltre, AES-GCM non utilizza il riempimento, quindi questo testo cifrato corrisponde alla lunghezza del messaggio di input (mentre Fernet / AES-CBC crittografa i messaggi in blocchi di lunghezza fissa, oscurando un po 'la lunghezza del messaggio).

AES256-GCM prende il solito segreto di 32 byte come chiave:

key = secrets.token_bytes(32)

quindi utilizzare

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

Ho incluso un timestamp per supportare gli stessi casi d'uso time-to-live supportati da Fernet.

Altri approcci in questa pagina, in Python 3

AES CFB - come CBC ma senza bisogno di pad

Questo è l'approccio seguito da All Іs Vаиітy , anche se in modo errato. Questaèla cryptographyversione, ma nota che includo l'IV nel testo cifrato , non dovrebbe essere memorizzato come globale (riutilizzare un IV indebolisce la sicurezza della chiave e memorizzarlo come modulo globale significa che verrà rigenerato la successiva invocazione di Python, che rende tutto il testo cifrato non decodificabile):

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

Questo manca l'armatura aggiunta di una firma HMAC e non c'è il timestamp; dovresti aggiungerli tu stesso.

Quanto sopra illustra anche quanto sia facile combinare i blocchi di base della crittografia in modo errato; La gestione errata del valore IV da parte di Vаиітy può portare a una violazione dei dati o a tutti i messaggi crittografati illeggibili perché l'IV è perso. Usare Fernet invece ti protegge da tali errori.

AES ECB - non sicuro

Se in precedenza hai implementato la crittografia AES ECB e devi ancora supportarla in Python 3, puoi farlo anche tu cryptography. Si applicano gli stessi avvertimenti, ECB non è abbastanza sicuro per applicazioni nella vita reale . Reimplementare quella risposta per Python 3, aggiungendo la gestione automatica del padding:

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

Ancora una volta, questo manca della firma HMAC e comunque non dovresti usare ECB. Quanto sopra è solo per illustrare che è in cryptographygrado di gestire i comuni blocchi crittografici, anche quelli che non dovresti effettivamente usare.


51

Il "encoded_c" menzionato nella risposta cifrata Vigenere di @ smehmood dovrebbe essere "key_c".

Qui stanno lavorando le funzioni di codifica / decodifica.

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)

Dichiarazione di non responsabilità: come implicito nei commenti, questo non dovrebbe essere usato per proteggere i dati in un'applicazione reale, a meno che tu non abbia letto questo e non ti dispiaccia parlare con gli avvocati:

Cosa c'è di sbagliato nella crittografia XOR?


2
Molto utile, grazie. Ho pubblicato una versione di Python 3 qui sotto (sembrava brutta nei commenti)
Ryan Barrett

1
"Legge di Schneier" : chiunque, dal dilettante più incapace al miglior crittografo, può creare un algoritmo che lui stesso non può rompere. Non usarlo, non è nemmeno vicino alla sicurezza.
Zaf

3
Grande! Questa versione funziona anche per le stringhe con accenti, mentre la versione di @ smehmood no. Esempio di utilizzo: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)funziona bene.
Basj

2
Ecco un plugin di testo Sublime basato su questo codice, che consente una facile codifica / decodifica del testo con CTRL + MAIUSC + P quindi "Eeencode" o "Dddecode".
Basj

2
@basj Grazie per l'esempio
sk03

49

Ecco una versione Python 3 delle funzioni dalla risposta di @qneill :

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)

La codifica / decodifica extra sono necessarie perché Python 3 ha diviso stringhe / array di byte in due concetti diversi e ha aggiornato le loro API per riflettere questo ..


4
Grazie Ryan, fwiw hai digitato @qniell
qneill

3
Per coloro che si chiedono, le differenze sono le .encode()).decode(). nel ritorno di encode(), e .decode()nella seconda riga in decode().
RolfBly

2
Hmm, il codice codificato non è davvero unico, ho eseguito un test e mostra ogni chiave del codice di 11,22,33,44, ..., 88,99,111,222, ... ha sempre un altro codice uguale a prima. Ma lo apprezzo
Hzzkygcs

1
@ Ryan Barrett, è possibile verificare la correttezza della password durante la decodifica. Diciamo che mando uno una stringa codificata che conosce la chiave, e se inserisse la chiave con un errore di battitura? La decodifica gli dà ancora una stringa "decodificata", ma non è quella giusta, come fa a saperlo?
Heinz

26

Disclaimer: come menzionato nei commenti, questo non dovrebbe essere usato per proteggere i dati in un'applicazione reale.

Cosa c'è di sbagliato nella crittografia XOR?

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

https://github.com/hellman/xortool


Come è stato detto, la libreria PyCrypto contiene una suite di cifrari. Il "cifrario" XOR può essere usato per fare il lavoro sporco se non vuoi farlo da solo:

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

Il codice funziona come segue senza dover riempire il testo in chiaro:

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

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

Ringraziamo https://stackoverflow.com/a/2490376/241294 per le funzioni di codifica / decodifica base64 (sono un principiante di Python).


nota: il modulo Crypto è installato in python3 da pycrptop installato, non Crypto. sudo pip3 install pycrypto.
Nikhil VJ

2
nota: pycrypto non è riuscito a installare su herokuapp alla mia fine. Ho trovato questo messaggio .. sembra dire che il pacchetto pycrypto è stato sostituito con un altro chiamato pycryptodome insteal, e che il metodo XOR è stato deprecato: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
Non usare mai questo metodo , nota la descrizione di questo "cifrario" nella documentazione : XOR toy cipher, XOR è uno dei più semplici cifrari a flusso. La crittografia e la decrittografia vengono eseguite dai dati XOR-ing con un keystream creato contatenando la chiave. Non usarlo per applicazioni reali! .
Martijn Pieters

@MartijnPieters hai ragione. Spero che la mia modifica abbia chiarito questo punto.
poida

12

Ecco un'implementazione della crittografia e decrittografia URL Safe utilizzando AES (PyCrypto) e 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))

Se riscontri problemi come questo https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) usa str(cipher)durante la decodifica come segue:

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

Test:

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

Bug con Windows x64 + Python 3.6 + PyCryptodome (come pycrypto è deprecato): TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@ Basj aww mi dispiace .. Non uso Windows, quindi non posso fare una correzione.
Tutto è previsto il

Non generare l'IV e memorizzarlo a livello di modulo, è necessario includere l'IV nel messaggio di testo cifrato restituito! Ora hai introdotto due problemi: il riavvio del processo Python ti dà un nuovo IV che rende impossibile decrittografare i messaggi precedentemente crittografati, e nel frattempo stai riutilizzando l'IV per più messaggi, il che riduce efficacemente la sicurezza a livello di ECB.
Martijn Pieters

1
@ AllІsVаиітy Risolto con b'...', ho modificato la risposta per riferimento futuro!
Basj

8

Funzioni di codifica / decodifica funzionanti in python3 (adattato molto poco dalla risposta di qneill):

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

Grazie per alcune ottime risposte. Niente di originale da aggiungere, ma ecco alcune riscritture progressive della risposta di qneill utilizzando alcune utili funzionalità di Python. Spero che tu sia d'accordo che semplificano e chiariscono il codice.

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()- abbina gli elementi di un elenco al loro indice

itera sui caratteri in una stringa

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)

costruire elenchi utilizzando una comprensione degli elenchi

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)

Spesso in Python non c'è affatto bisogno di elenchi di indici. Elimina completamente le variabili di indice del ciclo utilizzando zip e ciclo:

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)

e alcuni test ...

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

Molto carino @ Nick, buona progressione dei pitonismi e anche i test di avvio. Per dare il giusto credito Stavo solo la fissazione di un bug nella risposta originale di Smehmood stackoverflow.com/a/2490718/468252 .
qneill

4

Se vuoi essere sicuro, puoi usare Fernet, che è crittograficamente valido. Puoi usare un "salt" statico se non vuoi memorizzarlo separatamente: perderai solo il dizionario e la prevenzione degli attacchi arcobaleno. L'ho scelto perché posso scegliere password lunghe o brevi´, il che non è così facile con AES.

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

Se è troppo complicato, qualcuno ha suggerito semplicecripta

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

Basta generare un salt e includerlo nel risultato della crittografia in modo che password e messaggi ripetuti risultino comunque in output casuale. Includere anche il valore delle iterazioni per rendere l'algoritmo a prova di futuro, ma essere comunque in grado di decrittografare i messaggi utilizzando un conteggio di iterazioni diverso.
Martijn Pieters

3

Chiunque sia venuto qui (e il bountier) sembrava essere alla ricerca di battute con poca messa a punto, che altre risposte non forniscono. Quindi sto proponendo base64.

Ora, tieni presente che questo è solo un offuscamento di base ed è in ** NESSUN MODO OK PER LA SICUREZZA ** , ma qui ci sono alcune battute:

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'

Alcune cose da notare:

  • dovrai occuparti di più / meno codifica / decodifica da byte a stringa da solo, a seconda del tuo I / O. Guarda dentro bytes()ebytes::decode()
  • base64 è facilmente riconoscibile dai tipi di caratteri utilizzati e spesso termina con =caratteri. Le persone come me vanno assolutamente in giro a decodificarle nella console javascript quando le vediamo sui siti web. È facile comebtoa(string) (js)
  • l'ordine è chiave + dati, come in b64, quali caratteri compaiono alla fine dipende da quali caratteri sono all'inizio (a causa degli offset di byte. Wikipedia ha alcune belle spiegazioni). In questo scenario, l'inizio della stringa codificata sarà lo stesso per tutto ciò che è codificato con quella chiave. Il vantaggio è che i dati saranno più offuscati. In caso contrario, la parte dei dati sarà esattamente la stessa per tutti, indipendentemente dalla chiave.

Ora, se quello che volevi non avesse nemmeno bisogno di una chiave di alcun tipo, ma solo un po 'di offuscamento, puoi ancora una volta usare base64, senza alcun tipo di chiave:

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'

2
Sì, se non sei preoccupato per la sicurezza, allora base64 è molto meglio che crittografare.
Martijn Pieters

Riguardo alle altre risposte che non sono battute: non è questo il punto della domanda. Chiedono due funzioni da chiamare. Ed Fernet(key).encrypt(message)è solo un'espressione proprio come la tua chiamata base64.
Martijn Pieters

E dovresti rimuovere il keytutto. Carichi di sviluppatori stanno andando a copiare e incollare da Stack Overflow senza prestare attenzione e si assumono la chiave per essere segreta. Se devi includerlo, almeno non usarlo e avvisa o solleva un'eccezione se usato comunque. Non sottovalutare la stupidità della cultura del copia e incolla e le tue responsabilità nel fornire funzioni sane.
Martijn Pieters

3

Darò 4 soluzioni:

1) Utilizzo della crittografia Fernet con la cryptographylibreria

Ecco una soluzione utilizzando il pacchetto cryptography, che puoi installare come al solito con 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')

Puoi adattarti con il tuo sale, conteggio delle iterazioni, ecc. Questo codice non è molto lontano dalla risposta di @ HCLivess, ma l'obiettivo è qui per avere funzioni encrypte pronte all'uso decrypt. Fonte: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Nota: usa .encode()e .decode()ovunque se vuoi stringhe 'John Doe'invece di byte come b'John Doe'.


2) Semplice crittografia AES con Crypto libreria

Funziona con 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

Nota: è possibile rimuovere base64.b64encodee .b64decodese non si desidera un output leggibile come testo e / o se si desidera comunque salvare il testo cifrato su disco come file binario.


3) AES che utilizza una migliore funzione di derivazione della chiave della password e la capacità di verificare se "è stata inserita una password errata", con Crypto libreria

La soluzione 2) con AES "CFB mode" va bene, ma ha due svantaggi: il fatto che SHA256(password)può essere facilmente forzata con una tabella di ricerca, e che non c'è modo di verificare se è stata inserita una password sbagliata. Questo viene risolto qui tramite l'uso di AES in "modalità GCM", come discusso in AES: come rilevare che è stata inserita una password errata? e Questo metodo per dire "La password inserita è sbagliata" è sicuro? :

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) Utilizzo di RC4 (nessuna libreria necessaria)

Adattato da 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'

(Non aggiornato rispetto alle ultime modifiche, ma conservato per riferimento futuro): ho avuto problemi con Windows + Python 3.6 + tutte le risposte che coinvolgono pycrypto(non in grado di pip install pycryptosu Windows) o pycryptodome(le risposte qui from Crypto.Cipher import XORnon XORsono riuscite perché non sono supportate da questo pycryptofork; e anche le soluzioni che utilizzano ... AESnon sono riuscite con TypeError: Object type <class 'str'> cannot be passed to C code). Inoltre, la libreria simple-cryptha pycryptocome dipendenza, quindi non è un'opzione.


1
Non vuoi codificare il sale e conta l'iterazione; generare un salt casuale e rendere configurabile il conteggio delle iterazioni sulla crittografia, includere tali informazioni nel risultato della crittografia ed estrarre e utilizzare i valori sulla decrittografia. Il sale ti protegge dal banale riconoscimento delle password riutilizzate su un dato messaggio, il conteggio delle iterazioni rende l'algoritmo a prova di futuro.
Martijn Pieters

Sì certo @MartijnPieters, ma l'obiettivo è qui avere un codice semplice per scopi semplici, come richiesto da OP, con due parametri : testo normale + password. Ovviamente per scenari più complessi (cioè un database), userete tutti questi parametri aggiuntivi.
Basj

Non sono necessari parametri aggiuntivi! Includo tali informazioni codificate nel valore di ritorno opaco di password_encrypt().
Martijn Pieters

@MartijnPieters Bella soluzione davvero.
Basj

2

Funziona, ma la lunghezza della password dovrebbe essere esattamente 8. Questo è semplice e richiede 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

PRODUZIONE:

³.\Þ\åS¾+æÅ`;Ê
John Doe

Non utilizzare una flebo fissa! Randomizza l'IV e includilo invece con il testo cifrato. Altrimenti puoi anche usare la modalità ECB; i messaggi di testo normale ripetuti sono altrimenti banali da riconoscere.
Martijn Pieters

Inoltre, il progetto pyDes sembra essere morto; la homepage è scomparsa e l'ultima versione su PyPI ha ora 9 anni.
Martijn Pieters

2

Un'altra implementazione del codice @qneill che include il checksum CRC del messaggio originale, genera un'eccezione se il controllo fallisce:

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

Puoi utilizzare AES per crittografare la tua stringa con una password. Tuttavia, ti consigliamo di scegliere una password abbastanza sicura in modo che le persone non possano indovinare facilmente di cosa si tratta (mi dispiace non posso farci niente. Sono un aspirante fanatico della sicurezza).

AES è forte con una buona dimensione della chiave, ma è anche facile da usare con PyCrypto.


3
Grazie Alan. Ma per chiarimenti, non sto crittografando le password stesse. Nell'esempio sopra, sto crittografando la stringa "John Doe" in base alla password "mypass", che è una semplice password che uso nel mio codice sorgente. Le password degli utenti non sono coinvolte, né altre informazioni molto sensibili. Ho modificato la mia domanda per chiarire questo aspetto.
RexE

1
AES è ottimo, se usato correttamente. Tuttavia è facile utilizzarlo in modo errato; c'è almeno una risposta qui che utilizza una modalità di cifratura a blocchi non sicura, altre due che non riescono a gestire il valore IV. Meglio usare una buona libreria con una ricetta ben definita come Fernet!
Martijn Pieters

In realtà, è un'osservazione molto astuta. Una volta ho armeggiato con la flebo.
Alan

0

Le librerie esterne forniscono algoritmi di crittografia a chiave segreta.

Ad esempio, il Cyphermodulo in PyCrypto offre una selezione di molti algoritmi di crittografia:

  • 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 è un Pythonwrapper per OpenSSL e fornisce (tra le altre funzioni) una libreria di crittografia generale a piena potenza. Sono inclusi cifrari simmetrici (come AES).


0

se desideri una crittografia sicura:

per python 2, dovresti usare keyczar http://www.keyczar.org/

per python 3, fino a quando non sarà disponibile keyczar, ho scritto simple-crypt http://pypi.python.org/pypi/simple-crypt

entrambi useranno un rafforzamento chiave che li rende più sicuri della maggior parte delle altre risposte qui. e dal momento che sono così facili da usare potresti volerli usare anche quando la sicurezza non è fondamentale ...


Dal repository Keyczar : Nota importante: Keyczar è deprecato. Gli sviluppatori di Keyczar raccomandano Tink , ma non esiste una versione Python di Tink.
Martijn Pieters

0

Quindi, poiché non viene codificato nulla di mission critical , vuoi solo crittografare per offuscare .

Permettetemi di presentare il codice di Cesare

inserisci qui la descrizione dell'immagine

Caesar's cipher o Caesar shift, è una delle tecniche di crittografia più semplici e conosciute. È un tipo di cifratura di sostituzione in cui ogni lettera nel testo in chiaro è sostituita da una lettera di un numero fisso di posizioni lungo l'alfabeto. Ad esempio, con uno spostamento a sinistra di 3, D verrebbe sostituito da A, E diventerebbe B e così via.

Codice di esempio per riferimento:

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

Vantaggi: soddisfa le tue esigenze ed è semplice e fa la cosa di codifica "y".

Svantaggio: può essere violato da semplici algoritmi di forza bruta (altamente improbabile che qualcuno tenti di passare attraverso tutti i risultati extra).


0

Aggiunta di un altro codice con decodifica e codifica per riferimento

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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.