Cara sederhana untuk menyandikan string menurut kata sandi?


122

Apakah Python memiliki cara bawaan dan sederhana untuk mengenkode / mendekode string menggunakan kata sandi?

Sesuatu seperti ini:

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

Jadi string "John Doe" akan dienkripsi sebagai 'sjkl28cn2sx0'. Untuk mendapatkan string asli, saya akan "membuka" string itu dengan kunci 'mypass', yang merupakan sandi dalam kode sumber saya. Saya ingin ini menjadi cara saya mengenkripsi / mendekripsi dokumen Word dengan kata sandi.

Saya ingin menggunakan string terenkripsi ini sebagai parameter URL. Tujuan saya adalah kebingungan, bukan keamanan yang kuat; tidak ada misi penting yang dikodekan. Saya menyadari bahwa saya dapat menggunakan tabel database untuk menyimpan kunci dan nilai, tetapi saya berusaha untuk minimalis.


28
Istilah "sandi" di sini tidak tepat. Anda menggunakan ini sebagai KUNCI kriptografi dan Anda harus menggunakan terminologi itu untuk menghindari kebingungan dalam pertanyaan Anda serta dokumen, komentar, spesifikasi, rencana pengujian, dll.
Jim Dennis

2
"Saya ingin ini menjadi cara saya dapat mengenkripsi / mendekripsi dokumen Word dengan kata sandi.", Word sudah memiliki opsi bawaan untuk mengenkripsi dokumen Anda jika Anda hanya perlu mengenkripsi dokumen word.
Byron Filer

2
Menariknya, menurut makalah penelitian tentang perangkap penyimpanan kata sandi seperti ini , pengembang yang menggunakan Stack Overflow cenderung menghasilkan kode yang kurang aman. Wah, kenapa?
hutan

Juga, seseorang harus membaca jawaban ini dari keamanan.
SE

Seseorang tidak hanya mengimplementasikan encoding / decoding dengan mudah
luckyging3r

Jawaban:


70

Dengan asumsi Anda hanya mencari kebingungan sederhana yang akan mengaburkan hal-hal dari pengamat yang sangat biasa, dan Anda tidak ingin menggunakan pustaka pihak ketiga. Saya akan merekomendasikan sesuatu seperti sandi Vigenere. Ini adalah salah satu yang terkuat dari sandi kuno sederhana.

Sandi Vigenère

Cepat dan mudah diterapkan. Sesuatu seperti:

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)

Dekode kurang lebih sama, kecuali Anda mengurangi kuncinya.

Jauh lebih sulit untuk memutuskan jika string yang Anda enkode pendek, dan / atau sulit menebak panjang frasa sandi yang digunakan.

Jika Anda mencari sesuatu yang kriptografik, PyCrypto mungkin adalah pilihan terbaik Anda, meskipun jawaban sebelumnya mengabaikan beberapa detail: Mode ECB di PyCrypto mengharuskan pesan Anda terdiri dari kelipatan 16 karakter. Jadi, Anda harus pad. Selain itu, jika Anda ingin menggunakannya sebagai parameter URL, gunakanbase64.urlsafe_b64_encode() , bukan yang standar. Ini menggantikan beberapa karakter dalam alfabet base64 dengan karakter aman URL (seperti yang disarankan namanya).

Namun, Anda harus BENAR-BENAR yakin bahwa lapisan yang sangat tipis dari obfuscation ini cukup untuk kebutuhan Anda sebelum menggunakan ini. Artikel Wikipedia yang saya tautkan memberikan instruksi terperinci untuk memecahkan sandi, jadi siapa pun dengan tekad yang moderat dapat dengan mudah memecahkannya.


6
Saya memperbaiki skrip smehmood, dan menambahkan fungsi decoding gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester

3
Perhatian! Kode smehmood dan perbaikan Adrian Mester hanya berfungsi untuk string dengan karakter dari rentang ascii yang lebih rendah! Lihat prioritas operator%, input unicode, dll. Lihat jawaban qneill untuk kode yang berfungsi
le_m

2
@smehmood Saya mendapatkan kesalahan berikut'str' object cannot be interpreted as an integer
Rohit Khatri

3
"for i in xrange (string)" mungkin perlu diubah menjadi "for i in xrange (len (string))"
user3113626

2
encoder dan decoder untuk python 2 dan 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman

71

Saat Anda secara eksplisit menyatakan bahwa Anda menginginkan ketidakjelasan, bukan keamanan, kami tidak akan menegur Anda karena kelemahan dari apa yang Anda sarankan :)

Jadi, menggunakan 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)

Jika seseorang mendapatkan database Anda dan basis kode Anda, mereka akan dapat memecahkan kode data yang dienkripsi. Jaga secret_keykeamananmu!


3
Saya tidak berpikir ini akan berfungsi kecuali msg_text adalah kelipatan panjang 16 byte, karena enkripsi AES membutuhkan blok yang panjangnya kelipatan 16. Implementasi yang berfungsi untuk msg_text dengan panjang arbitrer perlu menambahkan padding ke string untuk membuatnya menjadi kelipatan panjang 16.
tohster

6
Contoh dengan padding: paste.ubuntu.com/11024555 Ia bekerja dengan kata sandi dan panjang pesan yang berubah-ubah.
Iman

3
@Ethan tidak, encryptfungsi khusus ini adalah stateful dlitz.net/software/pycrypto/api/current/… jadi Anda tidak boleh mencoba dan menggunakannya kembali.
Akankah

1
@Will +1 Terima kasih atas infonya dan tautannya. Menyelamatkan saya dari perbaikan bug yang sangat mahal di masa depan.
Ethan

3
kembali - "jelas tidak pernah menggunakan ECB dalam sistem yang kuat": Saya hanya bereksperimen dengan ini untuk kesenangan saya sendiri. tapi saya melihat komentar di atas dalam kode Anda. bagi kita dengan latar belakang keamanan / enkripsi / teori informasi yang sangat minim, mengapa "tidak pernah menggunakan"? mungkin perlu pertanyaan lain ... atau mungkin ada tautan di atasnya.
Trevor Boyd Smith

68

Python tidak memiliki skema enkripsi built-in, tidak. Anda juga harus menganggap serius penyimpanan data terenkripsi; Skema enkripsi sepele yang dipahami oleh satu pengembang sebagai tidak aman dan skema mainan mungkin disalahartikan sebagai skema aman oleh pengembang yang kurang berpengalaman. Jika Anda mengenkripsi, enkripsi dengan benar.

Namun, Anda tidak perlu melakukan banyak pekerjaan untuk menerapkan skema enkripsi yang tepat. Pertama-tama, jangan menemukan kembali roda kriptografi , gunakan pustaka kriptografi tepercaya untuk menangani ini untuk Anda. Untuk Python 3, pustaka tepercaya itu adalah cryptography.

Saya juga merekomendasikan bahwa enkripsi dan dekripsi berlaku untuk byte ; menyandikan pesan teks menjadi byte terlebih dahulu; stringvalue.encode()menyandikan ke UTF8, dengan mudah dikembalikan lagi menggunakan bytesvalue.decode().

Last but not least, ketika mengenkripsi dan mendekripsi, kita berbicara tentang kunci , bukan kata sandi. Kunci tidak boleh mudah diingat manusia, ini adalah sesuatu yang Anda simpan di lokasi rahasia tetapi dapat dibaca oleh mesin, sedangkan kata sandi seringkali dapat dibaca dan diingat oleh manusia. Anda dapat memperoleh kunci dari kata sandi, dengan sedikit hati-hati.

Tetapi untuk aplikasi web atau proses yang berjalan dalam cluster tanpa perhatian manusia untuk tetap menjalankannya, Anda ingin menggunakan kunci. Kata sandi digunakan ketika hanya pengguna akhir yang membutuhkan akses ke informasi tertentu. Meski begitu, Anda biasanya mengamankan aplikasi dengan kata sandi, kemudian bertukar informasi terenkripsi menggunakan kunci, mungkin yang dilampirkan ke akun pengguna.

Enkripsi kunci simetris

Fernet - AES CBC + HMAC, sangat disarankan

The cryptographyperpustakaan meliputi resep Fernet , resep-praktek terbaik untuk menggunakan kriptografi. Fernet adalah standar terbuka , dengan implementasi siap dalam berbagai bahasa pemrograman dan paket enkripsi AES CBC untuk Anda dengan informasi versi, stempel waktu, dan tanda tangan HMAC untuk mencegah gangguan pesan.

Fernet membuatnya sangat mudah untuk mengenkripsi dan mendekripsi pesan dan membuat Anda tetap aman. Ini adalah metode ideal untuk mengenkripsi data dengan rahasia.

Saya sarankan Anda menggunakan Fernet.generate_key()untuk menghasilkan kunci aman. Anda juga dapat menggunakan kata sandi (bagian selanjutnya), tetapi kunci rahasia 32-byte penuh (16 byte untuk dienkripsi, ditambah 16 byte lainnya untuk tanda tangan) akan lebih aman daripada kebanyakan kata sandi yang dapat Anda pikirkan.

Kunci yang dihasilkan Fernet adalah bytesobjek dengan URL dan karakter base64 yang aman untuk file, sehingga dapat dicetak:

from cryptography.fernet import Fernet

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

Untuk mengenkripsi atau mendekripsi pesan, buat Fernet()instance dengan kunci yang diberikan, dan panggil Fernet.encrypt()atau Fernet.decrypt(), pesan teks biasa untuk dienkripsi dan token yang dienkripsi adalah bytesobjek.

encrypt()dan decrypt()fungsinya akan terlihat seperti:

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 dengan kata sandi - kunci yang berasal dari kata sandi, agak melemahkan keamanan

Anda dapat menggunakan sandi sebagai ganti kunci rahasia, asalkan Anda menggunakan metode derivasi kunci yang kuat . Anda kemudian harus menyertakan salt dan hitungan iterasi HMAC dalam pesan, sehingga nilai yang dienkripsi tidak lagi kompatibel dengan Fernet tanpa terlebih dahulu memisahkan token salt, count dan Fernet:

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'

Menyertakan salt dalam output memungkinkan untuk menggunakan nilai salt acak, yang pada gilirannya memastikan output terenkripsi dijamin sepenuhnya acak terlepas dari penggunaan ulang sandi atau pengulangan pesan. Menyertakan hitungan iterasi memastikan bahwa Anda dapat menyesuaikan agar kinerja CPU meningkat seiring waktu tanpa kehilangan kemampuan untuk mendekripsi pesan yang lebih lama.

Kata sandi saja bisa seaman kunci acak Fernet 32-byte, asalkan Anda membuat kata sandi acak yang benar dari kumpulan ukuran yang sama. 32 byte memberi Anda 256 ^ 32 jumlah kunci, jadi jika Anda menggunakan alfabet dengan 74 karakter (26 atas, 26 lebih rendah, 10 digit dan 12 simbol yang mungkin), maka kata sandi Anda harus setidaknya math.ceil(math.log(256 ** 32, 74))== 42 karakter. Namun, sejumlah besar iterasi HMAC yang dipilih dengan baik dapat mengurangi kurangnya entropi karena ini membuatnya jauh lebih mahal bagi penyerang untuk memaksa masuk secara brutal.

Ketahuilah bahwa memilih kata sandi yang lebih pendek tetapi masih cukup aman tidak akan melumpuhkan skema ini, ini hanya mengurangi jumlah kemungkinan nilai yang harus dicari oleh penyerang brute force; pastikan untuk memilih kata sandi yang cukup kuat untuk kebutuhan keamanan Anda .

Alternatif

Mengaburkan

Alternatifnya adalah tidak mengenkripsi . Jangan tergoda untuk hanya menggunakan sandi dengan keamanan rendah, atau implementasi rumahan, katakanlah Vignere. Tidak ada keamanan dalam pendekatan ini, tetapi dapat memberikan pengembang yang tidak berpengalaman yang diberi tugas untuk menjaga kode Anda di masa depan ilusi keamanan, yang lebih buruk daripada tidak ada keamanan sama sekali.

Jika yang Anda butuhkan hanyalah ketidakjelasan, cukup base64 datanya; untuk persyaratan aman URL, base64.urlsafe_b64encode()fungsinya baik-baik saja. Jangan gunakan kata sandi di sini, cukup encode dan Anda selesai. Paling banyak, tambahkan beberapa kompresi (seperti 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))

Ini berubah b'Hello world!'menjadi b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Integritas saja

Jika yang Anda butuhkan hanyalah cara untuk memastikan bahwa data dapat dipercaya tidak diubah setelah dikirim ke klien yang tidak tepercaya dan diterima kembali, maka Anda ingin menandatangani data, Anda dapat menggunakan hmacperpustakaan untuk ini dengan SHA1 (masih dianggap aman untuk penandatanganan HMAC ) atau lebih baik:

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)

Gunakan ini untuk menandatangani data, lalu lampirkan tanda tangan dengan data dan kirimkan ke klien. Saat Anda menerima data kembali, pisahkan data dan tanda tangan dan verifikasi. Saya telah menetapkan algoritme default ke SHA256, jadi Anda memerlukan kunci 32-byte:

key = secrets.token_bytes(32)

Anda mungkin ingin melihat itsdangerousperpustakaan , yang mengemas semua ini dengan serialisasi dan de-serialisasi dalam berbagai format.

Menggunakan enkripsi AES-GCM untuk memberikan enkripsi dan integritas

Fernet mengembangkan AEC-CBC dengan tanda tangan HMAC untuk memastikan integritas data terenkripsi; penyerang jahat tidak dapat memberi makan data sistem Anda yang tidak masuk akal untuk membuat layanan Anda sibuk berjalan berputar-putar dengan masukan yang buruk, karena ciphertext ditandatangani.

The blok modus Galois / Kontra cipher menghasilkan ciphertext dan tag untuk melayani tujuan yang sama, sehingga dapat digunakan untuk melayani tujuan yang sama. Kelemahannya adalah tidak seperti Fernet, tidak ada resep satu ukuran untuk semua yang mudah digunakan untuk digunakan kembali di platform lain. AES-GCM juga tidak menggunakan padding, jadi ciphertext enkripsi ini cocok dengan panjang pesan input (sedangkan Fernet / AES-CBC mengenkripsi pesan ke blok dengan panjang tetap, agak mengaburkan panjang pesan).

AES256-GCM menggunakan rahasia 32 byte biasa sebagai kunci:

key = secrets.token_bytes(32)

lalu gunakan

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

Saya telah menyertakan stempel waktu untuk mendukung kasus penggunaan waktu-ke-langsung yang didukung Fernet.

Pendekatan lain di halaman ini, dengan Python 3

AES CFB - seperti CBC tetapi tanpa perlu bantalan

Ini adalah pendekatan yang diikuti All Іѕ Vаиітy , meskipun salah. Ini adalah cryptographyversinya, tetapi perhatikan bahwa saya menyertakan IV di ciphertext , itu tidak boleh disimpan sebagai global (menggunakan kembali IV melemahkan keamanan kunci, dan menyimpannya sebagai modul global berarti itu akan dihasilkan kembali permintaan Python berikutnya, membuat semua ciphertext tidak dapat dienkripsi):

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

Ini tidak memiliki pelindung tambahan dari tanda tangan HMAC dan tidak ada stempel waktu; Anda harus menambahkannya sendiri.

Hal di atas juga menggambarkan betapa mudahnya menggabungkan blok penyusun kriptografi dasar secara tidak benar; Semua penanganan Vаиітy yang salah atas nilai IV dapat menyebabkan pelanggaran data atau semua pesan terenkripsi tidak dapat dibaca karena IV hilang. Menggunakan Fernet malah melindungi Anda dari kesalahan semacam itu.

AES ECB - tidak aman

Jika sebelumnya Anda menerapkan enkripsi AES ECB dan masih perlu mendukungnya di Python 3, Anda juga dapat melakukannya cryptography. Peringatan yang sama berlaku, ECB tidak cukup aman untuk aplikasi kehidupan nyata . Mengimplementasikan ulang jawaban itu untuk Python 3, menambahkan penanganan padding otomatis:

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

Sekali lagi, ini tidak memiliki tanda tangan HMAC, dan Anda tidak boleh menggunakan ECB. Hal di atas hanya untuk ilustrasi yang cryptographydapat menangani blok penyusun kriptografi umum, bahkan yang seharusnya tidak Anda gunakan.


51

"Encoded_c" yang disebutkan dalam jawaban sandi Vigenere @ smehmood harus "key_c".

Berikut adalah fungsi encode / decode yang berfungsi.

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)

Penafian: Seperti yang tersirat dalam komentar, ini tidak boleh digunakan untuk melindungi data dalam aplikasi nyata, kecuali Anda membaca ini dan tidak keberatan berbicara dengan pengacara:

Apa yang salah dengan enkripsi XOR?


2
Sangat berguna, terima kasih. Saya telah memposting versi Python 3 di bawah ini (terlihat jelek di komentar)
Ryan Barrett

1
"Hukum Schneier" : Siapapun, dari amatir yang paling tidak mengerti sampai kriptografer terbaik, dapat membuat algoritma yang dia sendiri tidak dapat merusaknya. Jangan gunakan ini, itu bahkan tidak aman.
zaf

3
Bagus! Versi ini juga berfungsi untuk string dengan aksen, sedangkan versi @ smehmood tidak. Contoh penggunaan:, encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)berfungsi dengan baik.
Basj

2
Berikut adalah plugin teks Sublime berdasarkan kode ini, memungkinkan encoding / decoding teks dengan mudah dengan CTRL + SHIFT + P lalu "Eeencode" atau "Dddecode".
Basj

2
@basj Terima kasih atas contohnya
sk03

49

Berikut adalah versi Python 3 fungsi dari jawaban @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)

Enkode / dekode tambahan diperlukan karena Python 3 telah membagi larik string / byte menjadi dua konsep berbeda, dan memperbarui API mereka untuk mencerminkan hal itu ..


4
Terima kasih Ryan, fwiw Anda salah mengetik @qniell
qneill

3
Bagi mereka yang bertanya-tanya, perbedaannya adalah .encode()).decode(). di balik encode(), dan .decode()di baris kedua masuk decode().
RolfBly

2
Hmm, kode yang disandikan tidak terlalu unik, saya menjalankan tes dan itu menunjukkan setiap kode kunci 11,22,33,44, ..., 88,99,111,222, ... selalu memiliki kode lain yang sama seperti sebelumnya. Tapi saya menghargainya
Hzzkygcs

1
@Ryan Barrett, apakah mungkin untuk memeriksa kebenaran kata sandi saat mendekode. Katakanlah saya mengirim satu string yang disandikan yang tahu kuncinya, bagaimana jika dia memasukkan kunci dengan kesalahan ketik? Decode tersebut masih memberinya string "decoded", tapi itu bukan yang benar, bagaimana dia bisa tahu?
Heinz

26

Penafian: Seperti yang disebutkan dalam komentar, ini tidak boleh digunakan untuk melindungi data dalam aplikasi nyata.

Apa yang salah dengan enkripsi XOR?

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

https://github.com/hellman/xortool


Seperti yang telah disebutkan, pustaka PyCrypto berisi serangkaian cipher. "Sandi" XOR dapat digunakan untuk melakukan pekerjaan kotor jika Anda tidak ingin melakukannya sendiri:

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

Sandi bekerja sebagai berikut tanpa harus memasukkan teks biasa:

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

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

Kredit ke https://stackoverflow.com/a/2490376/241294 untuk fungsi encode / decode base64 (saya pemula python).


catatan: modul Crypto diinstal di python3 dengan menginstal pycrptop, bukan Crypto. sudo pip3 install pycrypto.
Nikhil VJ

2
catatan: pycrypto gagal dipasang di herokuapp di akhir saya. Saya menemukan posting ini .. sepertinya memberi tahu bahwa paket pycrypto telah diganti dengan yang lain yang disebut pycryptodome insteal, dan metode XOR sudah tidak digunakan lagi: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
Jangan pernah menggunakan metode ini , perhatikan deskripsi 'sandi' ini dalam dokumentasi : sandi mainan XOR, XOR adalah salah satu sandi aliran paling sederhana. Enkripsi dan dekripsi dilakukan oleh data XOR dengan aliran kunci yang dibuat dengan menggabungkan kunci. Jangan gunakan untuk aplikasi nyata! .
Martijn Pieters

@Artijuziyah kamu benar. Semoga hasil edit saya memperjelas hal itu.
poida

12

Berikut implementasi enkripsi dan Dekripsi Aman URL menggunakan AES (PyCrypto) dan 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))

Jika Anda menghadapi beberapa masalah seperti ini https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) gunakan str(cipher)saat decoding sebagai berikut:

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

Uji:

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 dengan Windows x64 + Python 3,6 + PyCryptodome (sebagai pycrypto sudah ditinggalkan): TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@Basj aww maaf .. Saya tidak menggunakan windows, jadi saya tidak bisa memperbaikinya.
Semua Vаиітy

Jangan menghasilkan IV dan menyimpannya di level modul, Anda harus menyertakan IV dalam pesan ciphertext yang dikembalikan! Anda sekarang memperkenalkan dua masalah: memulai ulang proses Python memberi Anda IV baru sehingga tidak mungkin mendekripsi pesan yang sebelumnya dienkripsi, dan sementara itu Anda menggunakan kembali IV untuk beberapa pesan, yang secara efektif mengurangi keamanan kembali ke level ECB.
Martijn Pieters

1
@ AllІѕVаиітy Diselesaikan dengan b'...', saya mengedit jawabannya untuk referensi di masa mendatang!
Basj

8

Fungsi encode / decode yang berfungsi di python3 (diadaptasi sangat sedikit dari jawaban 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

Terima kasih atas jawaban yang bagus. Tidak ada yang orisinal untuk ditambahkan, tetapi berikut adalah beberapa penulisan ulang progresif dari jawaban qneill menggunakan beberapa fasilitas Python yang berguna. Saya harap Anda setuju bahwa mereka menyederhanakan dan mengklarifikasi kode.

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()- pasangkan item dalam daftar dengan indeksnya

mengulangi karakter dalam sebuah string

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)

membangun daftar menggunakan pemahaman daftar

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)

Seringkali di Python tidak diperlukan indeks daftar sama sekali. Hapus variabel indeks loop sepenuhnya menggunakan zip dan 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)

dan beberapa tes ...

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 yang sangat bagus, perkembangan pythonisme yang baik, dan tes juga boot. Untuk memberikan kredit yang tepat, saya baru saja memperbaiki bug di jawaban asli Smehmood stackoverflow.com/a/2490718/468252 .
qneill

4

Jika Anda ingin aman, Anda dapat menggunakan Fernet, yang terdengar secara kriptografis. Anda dapat menggunakan "garam" statis jika Anda tidak ingin menyimpannya secara terpisah - Anda hanya akan kehilangan kamus dan pencegahan serangan pelangi. Saya memilihnya karena saya dapat memilih kata sandi panjang atau pendek ', yang tidak mudah dilakukan dengan 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

Jika itu terlalu rumit, seseorang menyarankan simplecrypt

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

Buat saja garam dan sertakan dalam hasil enkripsi sehingga kata sandi dan pesan yang berulang masih menghasilkan keluaran acak. Sertakan nilai iterasi juga untuk membuktikan algoritme di masa mendatang, tetapi masih dapat mendekripsi pesan menggunakan jumlah iterasi yang berbeda.
Martijn Pieters

3

Siapa pun yang datang ke sini (dan bountier) tampaknya mencari satu baris dengan tidak banyak pengaturan, yang tidak disediakan oleh jawaban lain. Jadi saya mengedepankan base64.

Sekarang, perlu diingat bahwa ini hanyalah pengaburan dasar, dan dalam ** TIDAK ADA CARA BISA UNTUK KEAMANAN ** , tetapi berikut adalah beberapa kalimat satu kalimat:

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'

Beberapa hal yang perlu diperhatikan:

  • Anda akan ingin menangani lebih / lebih sedikit encoding / decoding byte-to-string Anda sendiri, tergantung pada I / O Anda. Perhatikan bytes()danbytes::decode()
  • base64 mudah dikenali berdasarkan jenis karakter yang digunakan, dan sering kali diakhiri dengan =karakter. Orang-orang seperti saya benar-benar berkeliling memecahkan kode mereka di konsol javascript ketika kita melihatnya di situs web. Semudah itubtoa(string) (js)
  • urutannya adalah key + data, seperti pada b64, karakter apa yang muncul di akhir tergantung pada karakter apa yang ada di awal (karena byte offset. Wikipedia memiliki beberapa penjelasan yang bagus). Dalam skenario ini, awal string yang dikodekan akan sama untuk semua yang dikodekan dengan kunci itu. Nilai tambahnya adalah bahwa data akan lebih dikaburkan. Melakukannya sebaliknya akan menghasilkan bagian data yang persis sama untuk semua orang, apa pun kuncinya.

Sekarang, jika yang Anda inginkan bahkan tidak memerlukan kunci apa pun, tetapi hanya beberapa kebingungan, Anda dapat menggunakan base64 lagi, tanpa kunci apa pun:

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
Ya, jika Anda tidak disibukkan dengan keamanan, maka base64 jauh lebih baik daripada mengenkripsi.
Martijn Pieters

Tentang jawaban lain yang tidak satu baris: itu bukan inti pertanyaannya. Mereka meminta dua fungsi untuk dipanggil. Dan Fernet(key).encrypt(message)hanya satu ekspresi seperti panggilan base64 Anda.
Martijn Pieters

Dan Anda harus menghapus yang keysama sekali. Banyak pengembang akan menyalin dan menempel dari Stack Overflow tanpa memperhatikan dan akan menganggap kuncinya sebagai rahasia. Jika Anda harus memasukkannya, paling tidak jangan gunakan dan peringatkan atau buat pengecualian jika tetap digunakan. Jangan meremehkan kebodohan budaya salin dan tempel dan tanggung jawab Anda untuk memberikan fungsi yang waras.
Martijn Pieters

3

Saya akan memberikan 4 solusi:

1) Menggunakan enkripsi Fernet dengan cryptographyperpustakaan

Berikut adalah solusi menggunakan paket cryptographyyang dapat Anda instal seperti biasa dengan 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')

Anda dapat menyesuaikan dengan salt Anda sendiri, jumlah iterasi, dll. Kode ini tidak terlalu jauh dari jawaban @ HCLivess tetapi tujuannya adalah di sini untuk memiliki fungsi encryptdan siap pakai decrypt. Sumber: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Catatan: gunakan .encode()dan di .decode()mana - mana jika Anda menginginkan string, 'John Doe'bukan byte seperti b'John Doe'.


2) Enkripsi AES sederhana dengan Cryptoperpustakaan

Ini bekerja dengan 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

Catatan: Anda dapat menghapus base64.b64encodedan .b64decodejika Anda tidak ingin output yang dapat dibaca teks dan / atau jika Anda ingin menyimpan ciphertext ke disk sebagai file biner.


3) AES menggunakan fungsi derivasi kunci kata sandi yang lebih baik dan kemampuan untuk menguji apakah "kata sandi salah dimasukkan", dengan Cryptoperpustakaan

Solusi 2) dengan AES "mode CFB" baik-baik saja, tetapi memiliki dua kelemahan: fakta yang SHA256(password)dapat dengan mudah dipaksakan dengan tabel pencarian, dan bahwa tidak ada cara untuk menguji apakah sandi yang dimasukkan salah. Ini diselesaikan di sini dengan menggunakan AES dalam "mode GCM", sebagaimana dibahas dalam AES: bagaimana cara mendeteksi bahwa sandi yang salah telah dimasukkan? dan Apakah metode untuk mengatakan "Kata sandi yang Anda masukkan salah" aman? :

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) Menggunakan RC4 (tidak perlu perpustakaan)

Diadaptasi dari 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'

(Kedaluwarsa sejak pengeditan terbaru, tetapi disimpan untuk referensi di masa mendatang): Saya mengalami masalah menggunakan Windows + Python 3.6 + semua jawaban yang melibatkan pycrypto(tidak dapat pip install pycryptodi Windows) atau pycryptodome(jawaban di sini dengan from Crypto.Cipher import XORgagal karena XORtidak didukung oleh pycryptogarpu ini ; dan solusi menggunakan ... AESgagal juga dengan TypeError: Object type <class 'str'> cannot be passed to C code). Selain itu, pustaka simple-cryptmemiliki pycryptoketergantungan, jadi ini bukan opsi.


1
Anda tidak ingin hardcode garam dan jumlah iterasi; menghasilkan garam acak dan membuat hitungan iterasi dapat dikonfigurasi pada enkripsi, menyertakan informasi tersebut dalam hasil enkripsi, dan mengekstrak serta menggunakan nilai pada dekripsi. Salt melindungi Anda dari pengenalan remeh kata sandi yang digunakan kembali pada pesan tertentu, iterasi menghitung bukti masa depan algoritme.
Martijn Pieters

Ya tentu saja @MartijnPieters, tetapi tujuannya di sini untuk memiliki kode sederhana untuk tujuan sederhana, seperti yang diminta oleh OP, dengan dua parameter : teks biasa + kata sandi. Tentu saja untuk skenario yang lebih kompleks (misalnya database), Anda akan menggunakan semua parameter tambahan ini.
Basj

Tidak perlu parameter tambahan! Saya menyertakan informasi yang dikodekan dalam nilai kembalian buram password_encrypt().
Martijn Pieters

@ArtijnPerselesaiSolusi yang bagus.
Basj

2

Ini berfungsi tetapi panjang kata sandi harus persis 8. Ini sederhana dan membutuhkan 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

KELUARAN:

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

Jangan gunakan IV tetap! Acak IV dan sertakan dengan ciphertext sebagai gantinya. Jika tidak, Anda juga dapat menggunakan mode ECB; pesan teks biasa berulang jika tidak sepele untuk dikenali.
Martijn Pieters

Juga, proyek pyDes tampaknya mati; homepage tidak ada, dan rilis terakhir di PyPI sekarang berusia 9 tahun.
Martijn Pieters

2

Implementasi lain dari kode @qneill yang menyertakan checksum CRC dari pesan asli, ini memunculkan pengecualian jika pemeriksaan gagal:

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

Anda dapat menggunakan AES untuk mengenkripsi string Anda dengan kata sandi. Namun, Anda pasti ingin memilih kata sandi yang cukup kuat sehingga orang tidak dapat dengan mudah menebaknya (maaf, saya tidak bisa menahannya. Saya ingin tahu tentang keamanan).

AES kuat dengan ukuran kunci yang bagus, tetapi juga mudah digunakan dengan PyCrypto.


3
Terima kasih Alan. Tetapi untuk klarifikasi, saya tidak mengenkripsi sandi itu sendiri. Dalam contoh di atas, saya mengenkripsi string "John Doe" sesuai dengan sandi "mypass", yang merupakan sandi sederhana yang saya gunakan dalam kode sumber saya. Kata sandi pengguna tidak terlibat, begitu pula informasi lain yang sangat sensitif. Saya mengedit pertanyaan saya untuk mengklarifikasi ini.
RexE

1
AES bagus, jika digunakan dengan benar. Namun, mudah untuk menggunakannya secara tidak benar; setidaknya ada satu jawaban di sini yang menggunakan mode penyandian blok yang tidak aman, dua jawaban lainnya yang gagal menangani nilai IV. Lebih baik menggunakan perpustakaan yang bagus dengan resep yang terdefinisi dengan baik seperti Fernet!
Martijn Pieters

Sebenarnya, itu pengamatan yang sangat cerdik. Saya telah meraba-raba IV sekali.
Alan

0

Perpustakaan eksternal menyediakan algoritme enkripsi kunci rahasia.

Misalnya, Cyphermodul di PyCrypto menawarkan pilihan dari banyak algoritma enkripsi:

  • 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 adalah Pythonpembungkus untuk OpenSSL , dan menyediakan (di antara fungsi lainnya) pustaka kriptografi tujuan umum berkekuatan penuh. Termasuk adalah cipher simetris (seperti AES).


0

jika Anda ingin enkripsi aman:

untuk python 2, Anda harus menggunakan keyczar http://www.keyczar.org/

untuk python 3, hingga keyczar tersedia, saya telah menulis simple-crypt http://pypi.python.org/pypi/simple-crypt

keduanya akan menggunakan penguatan kunci yang membuatnya lebih aman daripada kebanyakan jawaban lain di sini. dan karena sangat mudah digunakan, Anda mungkin ingin menggunakannya meskipun keamanan tidak penting ...


Dari gudang Keyczar : Catatan penting: Keyczar sudah tidak digunakan lagi. Pengembang Keyczar merekomendasikan Tink , tetapi tidak ada versi Python dari Tink.
Martijn Pieters

0

Jadi, karena tidak ada misi penting yang dikodekan , dan Anda hanya ingin mengenkripsi untuk obsfuscation .

Izinkan saya menunjukkan sandi caaser

masukkan deskripsi gambar di sini

Sandi Caesar atau pergeseran Caesar, adalah salah satu teknik enkripsi yang paling sederhana dan paling dikenal. Ini adalah jenis sandi substitusi di mana setiap huruf dalam teks biasa diganti dengan sebuah huruf dengan beberapa posisi tetap di bawah alfabet. Misalnya, dengan pergeseran ke kiri sebesar 3, D akan diganti dengan A, E menjadi B, dan seterusnya.

Kode sampel untuk referensi Anda:

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

Keuntungan: ini memenuhi kebutuhan Anda dan sederhana dan melakukan pengkodean thing'y '.

Kekurangan: dapat dipecahkan dengan algoritma brute force sederhana (sangat tidak mungkin ada orang yang mencoba untuk melihat semua hasil tambahan).


0

Menambahkan satu kode lagi dengan decode dan encode untuk referensi

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()
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.