Bir dizeyi bir parolaya göre kodlamanın basit yolu?


123

Python, bir parola kullanarak dizeleri kodlamanın / çözmenin yerleşik, basit bir yolu var mı?

Bunun gibi bir şey:

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

Dolayısıyla "John Doe" dizesi "sjkl28cn2sx0" olarak şifrelenir. Orijinal dizeyi elde etmek için, kaynak kodumdaki bir parola olan 'mypass' anahtarıyla bu dizenin "kilidini açardım". Bir Word belgesini bir parola ile şifreleme / şifre çözme yöntemimin bu olmasını istiyorum.

Bu şifrelenmiş dizeleri URL parametreleri olarak kullanmak istiyorum. Amacım şaşkınlıktır, güçlü güvenlik değil; kritik önem taşıyan hiçbir şey kodlanmıyor. Anahtarları ve değerleri depolamak için bir veritabanı tablosu kullanabileceğimin farkındayım, ancak minimalist olmaya çalışıyorum.


28
Buradaki "şifre" terimi uygun değildir. Bunu bir kriptografik ANAHTAR olarak kullanıyorsunuz ve bu terminolojiyi sorularınızda ve ayrıca herhangi bir dokümanda, yorumda, teknik özelliklerde, test planlarında vb. Karışıklığı önlemek için kullanmalısınız.
Jim Dennis

2
"Bunun bir Word belgesini bir parola ile şifreleyebilmem / şifresini çözebilmem için olmasını istiyorum.", Word belgelerini şifrelemeniz gerekiyorsa , Word zaten belgelerinizi şifrelemek için yerleşik bir seçeneğe sahiptir .
Byron Filer

2
İlginç bir şekilde, bunun gibi şifre depolama tuzakları hakkındaki bu araştırma belgesine göre , Stack Overflow kullanan geliştiriciler daha az güvenli kod üretme eğilimindedir. Tanrım, acaba neden?
orman

Ayrıca, bu yanıtı güvenlikten
gerekir.SE

Basitçe kodlama / kod çözme uygulanmaz
luckyging3r

Yanıtlar:


70

Varsayarsak olan sadece şeyleri karanlık olacak basit şaşırtmaca için arayan çok gözlemciye ve üçüncü parti kütüphaneleri kullanmak isteyen değildir. Vigenere şifresi gibi bir şey tavsiye ederim. Basit antik şifrelerin en güçlülerinden biridir.

Vigenère şifresi

Uygulaması hızlı ve kolaydır. Gibi bir şey:

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)

Anahtarı çıkarmanız dışında kod çözme hemen hemen aynıdır.

Kodladığınız dizeler kısaysa ve / veya kullanılan parolanın uzunluğunu tahmin etmek zorsa, kırmak çok daha zordur.

Kriptografik bir şey arıyorsanız, PyCrypto muhtemelen en iyi seçeneğinizdir, ancak önceki cevaplar bazı ayrıntıları gözden kaçırır: PyCrypto'daki ECB modu, mesajınızın uzunluğunun 16 karakterin katları olmasını gerektirir. Yani, doldurmalısınız. Ayrıca, bunları URL parametreleri olarak kullanmak istiyorsanız base64.urlsafe_b64_encode(), standart parametre yerine kullanın . Bu, base64 alfabesindeki birkaç karakteri URL güvenli karakterlerle değiştirir (adından da anlaşılacağı gibi).

Ancak, bunu kullanmadan önce bu çok ince gizleme katmanının ihtiyaçlarınız için yeterli olduğundan KESİNLİKLE emin olmalısınız . Bağlantı verdiğim Wikipedia makalesi, şifreyi kırmak için ayrıntılı talimatlar veriyor, böylece makul miktarda kararlılığı olan herkes onu kolayca kırabilir.


6
Smehmood'un komut dosyasını düzelttim ve kod çözme işlevini ekledim gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester

3
Dikkat! smehmood'un kodu ve Adrian Mester'ın düzeltmesi yalnızca düşük ascii aralığındaki karakterlere sahip dizeler için çalışır! % Operatörü, unicode girişi vb. Önceliğine bakın. Qneill'in çalışma kodu için cevabına bakın
le_m

2
@smehmood Şu hatayı alıyorum'str' object cannot be interpreted as an integer
Rohit

3
"xrange (string) 'de i için", "xrange'de i için (len (string))" olarak
değişmesi gerekebilir

2
python 2 ve 3 için kodlayıcı ve kod çözücü: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman

71

Güvenlik değil, belirsizlik istediğinizi açıkça belirttiğiniz gibi, önerdiğiniz şeyin zayıflığı nedeniyle sizi azarlamaktan kaçınacağız :)

Yani, PyCrypto kullanarak:

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)

Birisi veritabanınızı ve kod tabanınızı ele geçirirse, şifrelenmiş verilerin kodunu çözebilir. Güvenliğinizi koruyun secret_key!


3
Msg_text'in uzunluğu 16 baytın katı olmadığı sürece bunun işe yarayacağını sanmıyorum, çünkü AES şifreleme uzunluğu 16'nın katları olan bloklar gerektiriyor. Rasgele uzunluktaki msg_text için çalışan bir uygulama, 16'nın katına ulaşmak için dizeye dolgu eklemeye ihtiyaç duyar.
tohster

6
Dolgu ile bir örnek: paste.ubuntu.com/11024555 İsteğe bağlı parola ve mesaj uzunluğu ile çalışır.
iman

3
@Ethan no, bu özel encryptişlev durum bilgili dlitz.net/software/pycrypto/api/current/… , bu yüzden denememeli ve yeniden kullanmamalısınız.
Will

1
@ Will +1 Bilgi ve bağlantı için teşekkürler. Beni gelecekte çok pahalı bir hata düzeltmesinden kurtardı.
Ethan

3
re - "kesinlikle güçlü sistemlerde ECB kullanmayın": Bunu kendi eğlencem için deniyorum. ancak yukarıdaki yorumu kodunuzda gördüm. asgari düzeyde güvenlik / şifreleme / bilgi teorisi geçmişi olan bizler için, neden "asla kullanmayın"? belki başka bir soruya ihtiyaç var ... veya belki de üzerinde bir bağlantı var.
Trevor Boyd Smith

69

Python'da yerleşik şifreleme şemaları yoktur, hayır. Şifrelenmiş veri depolamayı da ciddiye almalısınız; Bir geliştiricinin güvensiz olduğunu anladığı önemsiz şifreleme şemaları ve bir oyuncak şeması, daha az deneyimli bir geliştirici tarafından güvenli bir şema sanılabilir. Şifrelerseniz, doğru şekilde şifreleyin.

Bununla birlikte, uygun bir şifreleme şeması uygulamak için çok fazla iş yapmanıza gerek yoktur. Her şeyden önce, şifreleme tekerleğini yeniden icat etmeyin , bunu sizin için halletmek için güvenilir bir şifreleme kitaplığı kullanın. Python 3 için bu güvenilir kitaplıkcryptography .

Ayrıca, şifreleme ve şifre çözmenin baytlar için de geçerli olmasını tavsiye ederim ; metin mesajlarını önce bayt olarak kodlayın; stringvalue.encode()UTF8'e kodlar, kullanılarak tekrar kolayca geri alınabilir bytesvalue.decode().

Son olarak, şifreleme ve şifre çözme sırasında şifrelerden değil anahtarlardan bahsediyoruz . Bir anahtar, insanlar tarafından akılda kalıcı olmamalıdır, gizli bir yerde sakladığınız ancak makine tarafından okunabilen bir şeydir, oysa bir şifre genellikle insan tarafından okunabilir ve hafızaya alınabilir. Sen edebilirsiniz Biraz dikkatli bir parola anahtarı türetmek.

Ancak bir kümede insan dikkati olmadan çalışan bir web uygulaması veya işlemin onu çalıştırmaya devam etmesi için bir anahtar kullanmak istersiniz. Parolalar, yalnızca bir son kullanıcının belirli bilgilere erişmesi gerektiğinde kullanılır. O zaman bile, genellikle uygulamayı bir parola ile güvence altına alırsınız ve ardından, belki de kullanıcı hesabına eklenmiş bir anahtar kullanarak şifrelenmiş bilgileri değiş tokuş edersiniz.

Simetrik anahtar şifreleme

Fernet - AES CBC + HMAC, şiddetle tavsiye edilir

cryptographyKütüphane içerir Fernet tarifi , şifreleme kullanmak için bir en iyi uygulamaları tarifi. Fernet, çok çeşitli programlama dillerinde hazır uygulamalara sahip açık bir standarttır ve mesajın kurcalanmasını önlemek için sürüm bilgisi, bir zaman damgası ve bir HMAC imzası ile sizin için AES CBC şifrelemesini paketler.

Fernet, mesajları şifrelemeyi ve şifresini çözmeyi ve sizi güvende tutmayı çok kolaylaştırır . Verileri bir sır ile şifrelemek için ideal bir yöntemdir.

Fernet.generate_key()Güvenli bir anahtar oluşturmak için kullanmanızı tavsiye ederim . Bir parola da kullanabilirsiniz (sonraki bölüm), ancak tam 32 baytlık bir gizli anahtar (şifrelemek için 16 bayt, artı imza için 16 bayt) aklınıza gelebilecek çoğu paroladan daha güvenli olacaktır.

Fernet'in ürettiği anahtar, bytesURL'si ve dosya güvenli base64 karakterleri olan bir nesnedir, dolayısıyla yazdırılabilir:

from cryptography.fernet import Fernet

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

Şifrelemek veya şifresini mesajlar için, oluşturma Fernet()verilen anahtar ile örnek ve çağrı Fernet.encrypt()veya Fernet.decrypt()şifrelemek için düz metin mesajı ve şifrelenmiş belirteci hem de bytesnesneler.

encrypt()ve decrypt()işlevler şöyle görünür:

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'

Parola ile Fernet - paroladan türetilen anahtar, güvenliği biraz zayıflatır

Güçlü bir anahtar türetme yöntemi kullanmanız koşuluyla, gizli anahtar yerine parola kullanabilirsiniz . Ardından, tuz ve HMAC yineleme sayısını mesaja dahil etmeniz gerekir, böylece şifrelenmiş değer, önce salt, count ve Fernet token'ı ayırmadan artık Fernet uyumlu değildir:

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'

Tuzun çıktıya dahil edilmesi, rasgele bir tuz değerinin kullanılmasını mümkün kılar ve bu da şifrelenmiş çıktının, şifrenin yeniden kullanılması veya mesaj tekrarından bağımsız olarak tamamen rasgele olmasını garanti eder. Yineleme sayımının dahil edilmesi, eski mesajların şifresini çözme yeteneğini kaybetmeden zamanla CPU performansı artışlarını ayarlayabilmenizi sağlar.

Benzer boyuttaki bir havuzdan uygun şekilde rastgele bir parola oluşturmanız koşuluyla, tek başına bir parola Fernet 32 ​​bayt rasgele anahtar kadar güvenli olabilir . 32 bayt size 256 ^ 32 tuş sayısı verir, bu nedenle 74 karakterlik bir alfabe kullanıyorsanız (26 üst, 26 alt, 10 basamak ve 12 olası simge), şifreniz en az math.ceil(math.log(256 ** 32, 74))== 42 karakter uzunluğunda olmalıdır. Ancak, iyi seçilmiş çok sayıda HMAC yinelemesi , entropi eksikliğini bir şekilde azaltabilir çünkü bu, bir saldırganın içeri girmeye zorla girmesini çok daha pahalı hale getirir.

Daha kısa ama yine de makul ölçüde güvenli bir şifre seçmenin bu düzeni bozmayacağını bilin, sadece bir kaba kuvvet saldırganının aramak zorunda kalacağı olası değerlerin sayısını azaltır; güvenlik gereksinimleriniz için yeterince güçlü bir şifre seçtiğinizden emin olun .

Alternatifler

engellemeyecek

Bir alternatif şifrelememektir . Vignere diyelim ki, sadece düşük güvenlikli bir şifre ya da ev yapımı bir uygulama kullanmaktan çekinmeyin. Bu yaklaşımlarda güvenlik yoktur, ancak gelecekte kodunuzu koruma görevi verilen deneyimsiz bir geliştiriciye güvenlik yanılsamasını verebilir, ki bu hiç güvenlik olmamasından daha kötüdür.

İhtiyacınız olan tek şey belirsizlikse, verileri temel alın; URL güvenli gereksinimler için base64.urlsafe_b64encode()işlev iyidir. Burada şifre kullanmayın, sadece kodlayın ve bitirdiniz. En fazla, biraz sıkıştırma ekleyin (gibi 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))

Bu döner b'Hello world!'içine b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Yalnızca bütünlük

İhtiyacınız olan tek şey, güvenilmeyen bir istemciye gönderildikten ve geri alındıktan sonra verilerin değiştirilmemesi için güvenilebileceğinden emin olmanın bir yoluysa, verileri imzalamak istiyorsanız, bunun için hmackitaplığı SHA1 ile kullanabilirsiniz (yine de HMAC imzalama için güvenli kabul edilir ) veya daha iyisi:

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)

Verileri imzalamak için bunu kullanın, ardından imzayı verilerle ekleyin ve bunu müşteriye gönderin. Verileri geri aldığınızda, verileri ve imzayı bölün ve doğrulayın. Varsayılan algoritmayı SHA256 olarak ayarladım, bu nedenle 32 baytlık bir anahtara ihtiyacınız olacak:

key = secrets.token_bytes(32)

Tüm bunları çeşitli formatlarda serileştirme ve serileştirme çözme ile paketleyen itsdangerouskitaplığa bakmak isteyebilirsiniz .

Şifreleme ve bütünlük sağlamak için AES-GCM şifrelemesini kullanma

Fernet, şifrelenmiş verilerin bütünlüğünü sağlamak için bir HMAC imzasıyla AEC-CBC üzerine inşa eder; Kötü niyetli bir saldırgan, şifreli metin imzalandığı için, hizmetinizin kötü girdilerle çevrelerde çalışmasını sağlamak için sisteminizin saçma verilerini besleyemez.

Galois / Sayaç modu blok şifreleme şifreli ve üreten etiketi nedenle aynı amaçlara hizmet etmek için kullanılabilir, aynı amaca hizmet etmek. Olumsuz yanı, Fernet'ten farklı olarak, diğer platformlarda yeniden kullanılacak, kullanımı kolay tek beden herkese uyan bir tarifin olmamasıdır. AES-GCM de doldurma kullanmaz, bu nedenle bu şifreleme şifreli metni giriş mesajının uzunluğuyla eşleşir (Fernet / AES-CBC, mesajları sabit uzunluktaki bloklara şifreler ve mesaj uzunluğunu bir şekilde gizler).

AES256-GCM, anahtar olarak normal 32 baytlık sırrı alır:

key = secrets.token_bytes(32)

sonra kullan

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

Fernet'in desteklediği aynı yaşam süresi kullanım durumlarını desteklemek için bir zaman damgası ekledim.

Bu sayfadaki diğer yaklaşımlar, Python 3'te

AES CFB - CBC'ye benzer, ancak tamponlamaya gerek yoktur

Yanlış da olsa All Іѕ Vаиітy'nin izlediği yaklaşım budur . Bu cryptographysürümdür, ancak IV'ü şifreli metne dahil ettiğime dikkat edin, küresel olarak depolanmamalıdır (bir IV'ü yeniden kullanmak anahtarın güvenliğini zayıflatır ve bunu bir modül global olarak depolamak, yeniden oluşturulacağı anlamına gelir. sonraki Python çağrısı, tüm şifreli metni şifrelenemez hale getirir):

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

Bu, bir HMAC imzasının ek zırhından yoksundur ve zaman damgası yoktur; bunları kendiniz eklemeniz gerekir.

Yukarıdakiler ayrıca temel kriptografi yapı taşlarını yanlış bir şekilde birleştirmenin ne kadar kolay olduğunu göstermektedir; Tüm Іѕ Vаиітy'nin IV değerinin yanlış işlenmesi bir veri ihlaline veya tüm şifrelenmiş mesajların okunamaz olmasına neden olabilir çünkü IV kaybolur. Bunun yerine Fernet'i kullanmak sizi bu tür hatalardan korur.

AES ECB - güvenli değil

Daha önce AES ECB şifrelemesini uyguladıysanız ve bunu Python 3'te hala desteklemeniz gerekiyorsa, bunu yine cryptographyde yapabilirsiniz. Aynı uyarılar geçerlidir, ECB gerçek hayattaki uygulamalar için yeterince güvenli değildir . Bu cevabı Python 3 için yeniden uygulayarak, otomatik doldurma işlemlerini ekleyerek:

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

Yine, bu HMAC imzasından yoksundur ve yine de ECB'yi kullanmamalısınız. Yukarıdakiler, yalnızca cryptography, aslında kullanmamanız gerekenler de dahil olmak üzere, yaygın kriptografik yapı taşlarının üstesinden gelebileceğini göstermek içindir .


51

@ Smehmood'un Vigenere şifre cevabında bahsedilen "kodlanmış_c", "key_c" olmalıdır.

İşte kodlama / kod çözme işlevleri çalışıyor.

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)

Feragatname: Yorumlarda belirtildiği gibi, bunu okumadığınız ve avukatlarla konuşmaktan çekinmediğiniz sürece, gerçek bir uygulamadaki verileri korumak için bu kullanılmamalıdır :

XOR şifrelemesinin nesi var?


2
Çok faydalı, teşekkürler. Aşağıda bir Python 3 sürümü yayınladım (yorumlarda çirkin görünüyordu)
Ryan Barrett

1
"Schneier Yasası" : En bilgisiz amatörden en iyi kriptografa kadar herkes kendi kıramayacağı bir algoritma yaratabilir. Bunu kullanma, güvenliğe yakın bile değil.
zaph

3
Harika! Bu sürüm, aksanlı dizeler için de çalışır, oysa @ smehmood'un sürümü işe yaramaz. Kullanım örneği: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)iyi çalışıyor.
Basj

2
İşte bu koda dayalı bir Sublime metin eklentisi , CTRL + SHIFT + P ve ardından "Eeencode" veya "Dddecode" ile metnin kolay kodlanmasına / kodunun çözülmesine izin verir.
Basj

2
@basj Örnek için teşekkür ederim
sk03

49

İşte @qneill 'ın bazı görevlerini Python 3 versiyonu bulunuyor cevap :

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)

Python 3, dizeleri / bayt dizilerini iki farklı konsepte ayırdığı ve API'lerini bunu yansıtacak şekilde güncellediği için ekstra kodlama / kod çözme gereklidir.


4
Teşekkürler Ryan, @qniell yazım hatası yaptın
qneill

3
Merak edenler için farklar .encode()).decode(). dönüşte encode()ve .decode()ikinci satırda decode().
RolfBly

2
Hmm, kodlanmış kod gerçekten benzersiz değil, bir test yaptım ve 11,22,33,44, ..., 88,99,111,222'nin her kod anahtarını gösteriyor ... her zaman öncekiyle aynı koda sahip. Ama minnettarım
Hzzkygcs

1
@Ryan Barrett, şifre çözerken şifrenin doğruluğunu kontrol etmek mümkün mü? Diyelim ki anahtarı bilen birine şifreli bir dize gönderdim, ya anahtarı bir yazım hatasıyla girerse? Kod çözme ona hala "kodu çözülmüş" bir dizi veriyor, ama bu doğru olan değil, nasıl anlayabilir?
Heinz

26

Feragatname: Yorumlarda belirtildiği gibi, bu gerçek bir uygulamada verileri korumak için kullanılmamalıdır .

XOR şifrelemesinin nesi var?

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

https://github.com/hellman/xortool


Belirtildiği gibi, PyCrypto kitaplığı bir dizi şifreleme içerir. XOR "şifresi", kendiniz yapmak istemiyorsanız kirli işleri yapmak için kullanılabilir:

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

Şifre, düz metni doldurmak zorunda kalmadan aşağıdaki gibi çalışır:

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

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

Base64 kodlama / kod çözme işlevleri için https://stackoverflow.com/a/2490376/241294 adresine kredi verin (Ben bir python acemiyim).


not: Crypto modülü python3'e Crypto değil, kurulu pycrptop tarafından yüklenir. sudo pip3 install pycrypto.
Nikhil VJ

2
not: pycrypto benim tarafımda herokuapp'a yüklenemedi. Bu gönderiyi buldum .. pycrypto paketinin pycryptodome insteal adlı başka bir paketle değiştirildiğini ve bu XOR yönteminin kullanımdan kaldırıldığını söylüyor gibi görünüyor: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
Bu yöntemi asla kullanmayın , dokümantasyondaki bu 'şifrenin' açıklamasına dikkat edin: XOR oyuncak şifresi, XOR en basit akış şifrelerinden biridir. Şifreleme ve şifre çözme, verileri XORing tarafından, anahtarın birleştirilmesiyle yapılan bir anahtar akışı ile gerçekleştirilir. Gerçek uygulamalar için kullanmayın! .
Martijn Pieters

@MartijnPieters haklısın. Umarım düzenlemem bu noktayı netleştirmiştir.
poida

12

İşte AES (PyCrypto) ve base64 kullanarak URL Güvenli şifreleme ve Şifre Çözme uygulaması.

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

Böyle bir sorunla karşılaşırsanız https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) str(cipher)kodunu çözerken aşağıdaki gibi kullanın :

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

Ölçek:

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

Windows x64 + Python 3.6 + PyCryptodome (pycrypto kullanımdan kaldırılmıştır gibi) ile Hata: TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@Basj aww üzgünüm .. Windows kullanmıyorum, bu yüzden bir düzeltme yapamıyorum.
All Іѕ Vаиітy

IV'ü oluşturmayın ve modül düzeyinde saklamayın , IV'ü döndürülen şifreli metin mesajına eklemeniz gerekir ! Şimdi iki sorunu ortaya çıkardınız: Python sürecini yeniden başlatmak size önceden şifrelenmiş mesajların şifresini çözmeyi imkansız hale getiren yeni bir IV verir ve bu arada IV'ü birden çok mesaj için yeniden kullanırsınız, bu da güvenliği etkili bir şekilde ECB düzeyine düşürür.
Martijn Pieters

1
@ AllVаиітy Çözüldü b'...', ileride başvurmak üzere cevabı düzenledim!
Basj

8

Python3'te çalışan kodlama / kod çözme işlevleri (qneill'in cevabından çok az uyarlanmıştır):

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

Bazı harika cevaplar için teşekkürler. Eklenecek orijinal bir şey yok, ancak işte qneill'in yanıtının bazı yararlı Python olanaklarını kullanarak aşamalı olarak yeniden yazılması. Umarım kodu basitleştirdiklerine ve açıklığa kavuşturduklarına katılıyorsunuz.

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()- listedeki öğeleri indeksleriyle eşleştirin

bir dizedeki karakterleri yineleyin

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)

bir liste anlayışı kullanarak listeler oluşturun

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)

Python'da genellikle liste dizinlerine hiç gerek yoktur. Zip ve döngüsü kullanarak döngü dizini değişkenlerini tamamen ortadan kaldırın:

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)

ve bazı testler ...

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

Çok güzel @ Nick, pitonizmlerin iyi ilerlemesi ve çok önyükleme testleri. Doğru bir şekilde kredi vermek için, Smehmood'un orijinal cevabı stackoverflow.com/a/2490718/468252'deki bir hatayı düzeltiyordum .
qneill

4

Güvende olmak istiyorsanız kriptografik olarak sağlam olan Fernet'i kullanabilirsiniz. Ayrı olarak saklamak istemiyorsanız, statik bir "tuz" kullanabilirsiniz - yalnızca sözlüğü ve gökkuşağı saldırı önleme özelliğini kaybedersiniz. Bunu seçtim çünkü uzun veya kısa şifreler seçebiliyorum, ki bu AES ile o kadar kolay değil.

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

Bu çok karmaşıksa, birisi simplecrypt'i önerdi

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

Sadece bir tuz oluşturun ve şifreleme sonucuna dahil edin, böylece tekrarlanan şifreler ve mesajlar yine de rastgele çıktılarla sonuçlanır. Algoritmanın geleceğe yönelik korumasını sağlamak için yineleme değerini de dahil edin, ancak yine de farklı bir yineleme sayısı kullanarak mesajların şifresini çözebilirsiniz.
Martijn Pieters

4

4 çözüm vereceğim:

1) cryptographyKitaplık ile Fernet şifrelemesini kullanma

cryptographyHer zamanki gibi yükleyebileceğiniz , paketi kullanan bir çözüm 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')

Kendi tuzunuzla, yineleme sayınızla vb. Uyum sağlayabilirsiniz. Bu kod @ HCLivess'in cevabından çok uzak değildir, ancak burada amaç kullanıma hazır encryptve decryptişlevlere sahip olmaktır . Kaynak: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Not: gibi baytlar yerine dizeler istiyorsanız .encode()ve .decode()her yerde kullanın .'John Doe'b'John Doe'


2) CryptoKitaplık ile basit AES şifreleme

Bu, Python 3 ile çalışır:

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

Not: kaldırabilir base64.b64encodeve .b64decodemetin okunabilir çıkışı istemiyorsanız ve / veya yine bir ikili dosya olarak diske şifreli kaydetmek istiyorsanız.


3) AES, daha iyi bir şifre anahtarı türetme işlevi ve "yanlış şifre girilip girilmediğini" test etme yeteneği, Cryptokütüphane ile

Çözüm 2) AES "CFB modu" ile tamamdır, ancak iki dezavantajı vardır: SHA256(password)bir arama tablosuyla kolayca zorlanabilen ve yanlış bir şifre girilip girilmediğini test etmenin bir yolu olmadığı gerçeği . Bu, burada AES'de anlatıldığı gibi "GCM modunda" AES kullanımıyla çözülür : kötü bir parola girilmiş olup olmadığı nasıl tespit edilir? ve Bu yöntem "girdiğiniz şifre yanlış" demek güvenli midir? :

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) RC4 kullanımı (kitaplık gerekmez)

Https://github.com/bozhu/RC4-Python/blob/master/rc4.py adresinden uyarlanmıştır .

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'

(En son düzenlemelerden beri güncelliğini yitirdi, ancak ileride başvurmak üzere saklandı): Windows + Python 3.6 + ile ilgili tüm yanıtları pycrypto( pip install pycryptoWindows'ta yapamıyorum ) veya pycryptodome( bu çatal tarafından desteklenmediği için buradaki cevaplar from Crypto.Cipher import XORbaşarısız oldu ) kullanırken sorunlar yaşadım ; ve kullanan çözümler de başarısız oldu ). Ayrıca, kütüphane vardır bağımlılık olarak, bu nedenle bir seçenek değil.XORpycrypto... AESTypeError: Object type <class 'str'> cannot be passed to C codesimple-cryptpycrypto


1
Tuz ve yineleme sayılarını sabit kodlamak istemezsiniz; rastgele bir tuz üretin ve yineleme sayısını şifrelemede yapılandırılabilir hale getirin, bu bilgileri şifreleme sonucuna dahil edin ve şifre çözmede değerleri ayıklayın ve kullanın. Tuz, sizi belirli bir mesajda yeniden kullanılan şifrelerin önemsiz bir şekilde tanınmasından korur, yineleme sayımı, algoritmayı geleceğe dönük olarak sayar.
Martijn Pieters

Evet, elbette @MartijnPieters, ancak burada amaç, OP tarafından talep edildiği gibi basit amaçlar için iki parametreli basit bir koda sahip olmak : düz metin + şifre. Elbette daha karmaşık bir senaryo için (yani bir veritabanı), tüm bu ek parametreleri kullanacaksınız.
Basj

Ek parametrelere gerek yoktur! Opak dönüş değerinde kodlanmış bilgileri dahil ediyorum password_encrypt().
Martijn Pieters

@MartijnPieters Gerçekten güzel bir çözüm.
Basj

3

Buraya kim gelirse gelsin (ve ödül sahibi), diğer yanıtların sağlamadığı, pek fazla düzene sahip olmayan tek gömlekleri arıyor gibiydi. Bu yüzden base64'ü öne sürüyorum.

Şimdi, bunun yalnızca basit bir gizleme olduğunu ve ** GÜVENLİK İÇİN YOKTUR ** içinde olduğunu unutmayın , ancak işte bazı tek satırlar:

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'

Dikkat edilmesi gereken birkaç nokta:

  • G / Ç'nize bağlı olarak kendi başınıza daha çok / daha az bayttan dizgeye kodlama / kod çözme ile uğraşmak isteyeceksiniz. Bak bytes()vebytes::decode()
  • base64, kullanılan karakter türleri tarafından kolayca tanınır ve genellikle =karakterlerle biter . Benim gibi insanlar onları web sitelerinde gördüğümüzde kesinlikle javascript konsolunda kodlarını çözüyorlar. btoa(string)(Js) kadar kolay
  • sıra anahtar + veridir, b64'te olduğu gibi, sonunda hangi karakterlerin görüneceği, başlangıçta hangi karakterlerin olduğuna bağlıdır (bayt kaymalarından dolayı. Wikipedia'nın bazı güzel açıklamaları vardır). Bu senaryoda, kodlanmış dizenin başlangıcı, o anahtarla kodlanan her şey için aynı olacaktır. Artı, verilerin daha karmaşık hale gelmesidir. Bunu tersine yapmak, anahtardan bağımsız olarak veri bölümünün herkes için tamamen aynı olmasına neden olur.

Şimdi, eğer istediğin herhangi bir türden bir anahtara bile ihtiyaç duymadıysa, sadece biraz şaşırtmaya ihtiyaç duyuyorsa, yine de herhangi bir anahtar olmadan base64'ü kullanabilirsin:

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
Evet, güvenlik konusunda endişelenmiyorsanız, base64 şifrelemekten çok daha iyidir.
Martijn Pieters

Diğer cevapların tek satırlık olmaması hakkında: Sorunun amacı bu değil. Çağırmak için iki işlev istiyorlar. Ve Fernet(key).encrypt(message)bu, base64 aramanız gibi yalnızca bir ifadedir.
Martijn Pieters

Ve gerektiğini kaldırmakkey tamamen. Geliştiricilerin Yükler dikkat etmeden kopyalayıp yığın taşması yapıştırmak için gidiyoruz ve edecektir gizli olması anahtarını varsayalım. Eklemeniz gerekiyorsa, en azından kullanmayın ve yine de kullanılırsa bir istisna bildirin veya uyarın. Kopyala ve yapıştır kültürünün aptallığını ve mantıklı işlevler sunma sorumluluklarınızı küçümsemeyin.
Martijn Pieters

2

Bu çalışır, ancak şifre uzunluğu tam olarak olmalıdır 8. Bu basittir ve pyDes gerektirir .

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

ÇIKTI:

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

Sabit bir IV kullanmayın! IV'ü rastgele seçin ve bunun yerine şifreli metinle birlikte ekleyin. Aksi takdirde ECB modunu da kullanabilirsiniz; aksi takdirde tekrarlanan düz metin mesajlarının tanınması önemsizdir.
Martijn Pieters

Ayrıca, pyDes projesi ölmüş gibi görünüyor; ana sayfa gitti ve PyPI'deki son sürüm artık 9 yaşında.
Martijn Pieters

2

Orijinal mesajın CRC sağlama toplamını içeren başka bir @qneill kodu uygulaması, kontrol başarısız olursa bir istisna atar:

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

Dizinizi bir parola ile şifrelemek için AES'i kullanabilirsiniz. Yine de, yeterince güçlü bir şifre seçmek isteyeceksiniz, böylece insanlar bunun ne olduğunu kolayca tahmin edemezler (özür dilerim, yardım edemem. Ben bir güvenlik zenciyim).

AES, iyi bir anahtar boyutuyla güçlüdür, ancak PyCrypto ile kullanımı da kolaydır.


3
Teşekkürler Alan. Ancak açıklığa kavuşturmak için, şifreleri kendileri şifrelemiyorum. Yukarıdaki örnekte, kaynak kodumda kullandığım basit bir parola olan "mypass" parolasına göre "John Doe" dizesini şifreliyorum. Kullanıcı şifreleri dahil değildir ve çok hassas başka bilgiler de yoktur. Bunu açıklığa kavuşturmak için sorumu düzenledim.
RexE

1
Doğru kullanılırsa AES harikadır. Ancak yanlış kullanmak kolaydır; Burada güvenli olmayan bir blok şifreleme kipini kullanan en az bir cevap var, diğeri IV değerini idare etmeyi beceremiyor. Fernet gibi iyi tanımlanmış bir tarif içeren iyi bir kitaplık kullanmak daha iyidir!
Martijn Pieters

Aslında bu çok zekice bir gözlem. IV'ü bir kez aradım.
Alan

0

Dış kitaplıklar gizli anahtar şifreleme algoritmaları sağlar.

Örneğin, CypherPyCrypto'daki modül birçok şifreleme algoritmasından oluşan bir seçim sunar:

  • 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 , OpenSSLPython için bir sarmalayıcıdır ve (diğer işlevlerin yanı sıra) tam güçlü bir genel amaçlı şifreleme kitaplığı sağlar. Simetrik şifreler (AES gibi) dahildir.


0

güvenli şifreleme istiyorsanız:

python 2 için keyczar'ı kullanmalısınız http://www.keyczar.org/

python 3 için, keyczar mevcut olana kadar basit-crypt yazdım http://pypi.python.org/pypi/simple-crypt

her ikisi de anahtar güçlendirmeyi kullanacak ve bu da onları buradaki diğer yanıtların çoğundan daha güvenli hale getirecek. ve kullanımları çok kolay olduğundan, güvenlik kritik olmasa bile kullanmak isteyebilirsiniz ...


Gönderen Keyczar depo : Önemli not: Keyczar kaldırılmıştır. Keyczar geliştiriciler tavsiye Tink , ancak Tink hiçbir Python sürümü vardır.
Martijn Pieters

0

Yani, kritik önem taşıyan hiçbir şey kodlanmadığından ve siz sadece gözlem için şifrelemek istiyorsunuz. .

Caeser'in şifresini sunmama izin ver

görüntü açıklamasını buraya girin

Sezar'ın şifresi veya Sezar kayması, en basit ve en çok bilinen şifreleme tekniklerinden biridir. Bu, düz metindeki her harfin, alfabedeki sabit sayıdaki konumların bir harfiyle değiştirildiği bir tür ikame şifresidir. Örneğin, sola kaydırma 3 ile D, A ile değiştirilir, E, B olur ve bu böyle devam eder.

Referansınız için örnek kod:

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

Avantajları: Gereksinimlerinizi karşılar ve basittir ve kodlama işini 'y' yapar.

Dezavantaj: Basit kaba kuvvet algoritmalarıyla kırılabilir (herhangi birinin tüm ekstra sonuçları gözden geçirme olasılığı çok düşüktür).


0

Referans için kod çözme ve kodlama ile bir kod daha ekleme

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.