Простой способ кодировать строку в соответствии с паролем? - PullRequest
105 голосов
/ 22 марта 2010

Имеет ли Python встроенный простой способ кодирования / декодирования строк с использованием пароля?

Примерно так:

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

Таким образом, строка "John Doe" шифруетсякак 'sjkl28cn2sx0'.Чтобы получить исходную строку, я бы «разблокировал» эту строку с помощью ключа «mypass», который является паролем в моем исходном коде.Я хотел бы, чтобы это был способ, которым я могу зашифровать / расшифровать документ Word с помощью пароля.

Я хотел бы использовать эти зашифрованные строки в качестве параметров URL.Моя цель - запутывание, а не сильная безопасность;ничего критически важного не кодируется.Я понимаю, что могу использовать таблицу базы данных для хранения ключей и значений, но пытаюсь быть минималистичным.

Ответы [ 18 ]

64 голосов
/ 22 марта 2010

Поскольку вы прямо заявляете, что хотите скрыть, а не безопасность, мы не будем делать выговор за слабость того, что вы предлагаете:)

Итак, используя PyCrypto:

from Crypto.Cipher import AES
import base64

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

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

кто-то завладеет вашей базой данных и вашей кодовой базой, они смогут декодировать зашифрованные данные.Держите свой секретный ключ в безопасности!

63 голосов
/ 22 марта 2010

Предполагая, что вы только ищете простое запутывание, которое затемнит случайность от очень случайного наблюдателя, и вы не собираетесь использовать сторонние библиотеки.Я бы порекомендовал что-то вроде шифра Vigenere.Это один из самых сильных простых древних шифров.

Шифр ​​Vigenère

Быстро и легко внедрить.Что-то вроде:

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)

Декодирование почти такое же, за исключением того, что вы вычитаете ключ.

Разбить намного сложнее, если строки, которые вы кодируете, короткие и / или еслиТрудно угадать длину используемой парольной фразы.

Если вы ищете что-то криптографическое, PyCrypto, вероятно, является вашей лучшей ставкой, хотя предыдущие ответы не учитывают некоторые детали: режим ECB в PyCrypto требует, чтобы ваше сообщение было кратно 16 символам.Итак, вы должны дополнить.Также, если вы хотите использовать их в качестве параметров URL, используйте base64.urlsafe_b64_encode() вместо стандартного.Это заменяет несколько символов в алфавите base64 на URL-безопасные символы (как следует из названия).

Однако вы должны быть АБСОЛЮТНО уверены, что этого тонкого слоя очень достаточнодля ваших нужд, прежде чем использовать это.Статья в Википедии, на которую я ссылался, содержит подробные инструкции по взлому шифра, так что любой человек с умеренной степенью решимости может легко его сломать.

47 голосов
/ 01 мая 2013

"encoded_c", упомянутый в ответе шифра Vigenere @ smehmood, должен быть "key_c".

Здесь работают функции кодирования / декодирования.

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)
46 голосов
/ 06 июля 2016

Вот Python 3-версия функций из @qneill's answer :

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 разделил строки / байтовые массивы на две разные концепции и обновил их API, чтобы отразить это

29 голосов
/ 13 марта 2019

Python не имеет встроенных схем шифрования, нет. Вы также должны серьезно относиться к хранению зашифрованных данных; тривиальные схемы шифрования, которые один разработчик считает небезопасными, и игрушечная схема может быть ошибочно принята за безопасную схему менее опытным разработчиком. Если вы шифруете, шифруйте правильно.

Однако для реализации правильной схемы шифрования вам не нужно много работать. Прежде всего, не изобретайте колесо криптографии , используйте доверенную библиотеку криптографии, чтобы справиться с этим за вас. Для Python 3 эта доверенная библиотека - cryptography.

Я также рекомендую, чтобы шифрование и дешифрование применялось к байтам ; сначала кодировать текстовые сообщения в байты; stringvalue.encode() кодирует в UTF8, легко восстанавливается снова с помощью bytesvalue.decode().

И последнее, но не менее важное: при шифровании и дешифровании мы говорим о ключах , а не о паролях. Ключ не должен быть человеком запоминающимся, это что-то, что вы храните в секретном месте, но машиночитаемое, тогда как пароль часто может быть читаем и запомнен человеком. Вы можете получить ключ из пароля, с небольшой осторожностью.

Но для того, чтобы веб-приложение или процесс, работающий в кластере без участия человека, продолжал его запускать, вам нужно использовать ключ. Пароли предназначены для тех случаев, когда только конечному пользователю необходим доступ к конкретной информации. Даже тогда вы обычно защищаете приложение паролем, а затем обмениваетесь зашифрованной информацией с помощью ключа, возможно, одного из них, прикрепленного к учетной записи пользователя.

Симметричный ключ шифрования

Fernet - AES CBC + HMAC, настоятельно рекомендуется

Библиотека cryptography содержит рецепт Fernet , наилучший рецепт использования криптографии. Fernet - это открытый стандарт , с готовыми реализациями на широком спектре языков программирования, и он упаковывает шифрование AES CBC для вас с информацией о версии, отметкой времени и подписью HMAC для предотвращения подделки сообщений.

Fernet позволяет очень легко шифровать и дешифровать сообщения и защищать вас. Это идеальный метод для шифрования данных с помощью секрета.

Я рекомендую использовать Fernet.generate_key() для генерации безопасного ключа. Вы также можете использовать пароль (следующий раздел), но полный 32-байтовый секретный ключ (16 байтов для шифрования, плюс еще 16 для подписи) будет более безопасным, чем большинство паролей, о которых вы могли подумать.

Ключ, который генерирует Fernet, - это bytes объект с URL-адресом и файлом безопасные символы base64, которые можно распечатать:

from cryptography.fernet import Fernet

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

Чтобы зашифровать или расшифровать сообщения, создайте экземпляр Fernet() с данным ключом и вызовите Fernet.encrypt() или Fernet.decrypt(), оба сообщения в виде открытого текста для шифрования и зашифрованный токен - bytes объектов.

Функции

encrypt() и decrypt() будут выглядеть следующим образом:

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)

Демо-версия:

>>> 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 с паролем - ключ, полученный из пароля, несколько ослабляет безопасность

Вы можете использовать пароль вместо секретного ключа, при условии, что вы используете метод деривации сильного ключа . Затем необходимо включить соль и число итераций HMAC в сообщение, чтобы зашифрованное значение больше не было совместимо с Fernet без предварительного разделения соли, счетчика и токена 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)

Демо-версия:

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

Включение соли в выводе позволяет использовать случайное значение соли, что, в свою очередь, гарантирует, что зашифрованный вывод гарантированно будет полностью случайным независимо от повторного использования пароля или повторения сообщения. Включение счетчика итераций обеспечивает возможность повышения производительности ЦП с течением времени, не теряя возможности расшифровывать старые сообщения.

Aодин пароль может быть таким же безопасным, как 32-байтовый случайный ключ Fernet, при условии, что вы генерируете случайный пароль из пула аналогичного размера.32 байта дают 256 ^ 32 количества ключей, поэтому если вы используете алфавит из 74 символов (26 верхних, 26 нижних, 10 цифр и 12 возможных символов), тогда ваш пароль должен быть длиной не менее math.ceil(math.log(256 ** 32, 74)) == 42 символа,Тем не менее, правильно выбранное большее количество итераций HMAC может несколько уменьшить недостаток энтропии, поскольку это значительно увеличивает стоимость атаки злоумышленником.

Просто знайте, чтовыбор более короткого, но все же достаточно надежного пароля не приведет к нарушению этой схемы, а лишь уменьшит количество возможных значений, которые злоумышленник должен будет найти;убедитесь, что вы выбрали достаточно надежный пароль для ваших требований безопасности .

Альтернативы

Затенение

Альтернативой является не шифровать .Не поддавайтесь искушению просто использовать шифр с низким уровнем безопасности или реализацию, созданную в домашних условиях, скажем Виньере.В этих подходах нет защиты, но они могут дать неопытному разработчику, которому поручено поддерживать ваш код в будущем, иллюзию безопасности, которая хуже, чем отсутствие безопасности вообще.

Если все, что вам нужно, этонеизвестность, просто base64 данные;для безопасных URL-адресов вполне подойдет функция base64.urlsafe_b64encode() .Не используйте пароль здесь, просто закодируйте, и все готово.Самое большее, добавьте сжатие (например, 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))

Это превращает b'Hello world!' в b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Только целостность

Если все, что вам нужноэто способ убедиться, что данные можно доверять как без изменений после того, как они были отправлены ненадежному клиенту и получены обратно, затем вы хотите подписать данные, вы можете использовать hmac библиотека для этого с SHA1 (все еще считается безопасным для подписи HMAC ) или лучше:

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)

Используйте это для подписи данных, затем прикрепите подпись с данными и отправьтеэто клиенту.Когда вы получите данные обратно, разделите данные и подпись и проверьте.Я установил алгоритм по умолчанию SHA256, поэтому вам понадобится 32-байтовый ключ:

key = secrets.token_bytes(32)

Возможно, вы захотите взглянуть на библиотеку itsdangerous , котораявсе это связано с сериализацией и десериализацией в различных форматах.

Использование шифрования AES-GCM для обеспечения шифрования и целостности

Fernet основывается на AEC-CBC с подписью HMAC для обеспечения целостностизашифрованные данные;злонамеренный злоумышленник не может передать ваши системные бессмысленные данные, чтобы ваша служба работала в кругах с неправильным вводом, поскольку зашифрованный текст подписан.

Блочный шифр режима Galois / Counter создает зашифрованный тексти тег для той же цели, поэтому может использоваться для тех же целей.Недостатком является то, что в отличие от Fernet нет простого в использовании универсального рецепта для повторного использования на других платформах.AES-GCM также не использует заполнение, поэтому этот шифрованный текст шифрования соответствует длине входного сообщения (тогда как Fernet / AES-CBC шифрует сообщения в блоки фиксированной длины, что несколько скрывает длину сообщения).

AES256-GCM принимает в качестве ключа обычный 32-байтовый секрет:

key = secrets.token_bytes(32)

, затем используйте

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.

Другие подходы на этой странице, в Python 3

AES CFB - как CBC, но без необходимости дополнения

ЭтоПодход, который Все Vaiíty следует, хотя и неправильно.Это версия cryptography, но обратите внимание, что я включаю IV в зашифрованный текст , он не должен храниться как глобальный (повторное использование IV ослабляет безопасность ключа и сохраняет его как модульglobal означает, что он будет заново сгенерирован при следующем вызове Python, что сделает весь зашифрованный текст недопустимым):

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

Thотсутствует добавленное сохранение подписи HMAC и нет отметки времени; Вы должны добавить их сами.

Вышеприведенное также иллюстрирует, как легко неправильно комбинировать базовые блоки криптографии; Все неправильные обращения со значением IV могут привести к утечке данных или невозможности чтения всех зашифрованных сообщений из-за потери IV. Использование Fernet вместо этого защищает вас от таких ошибок.

AES ECB - небезопасно

Если вы ранее внедрили шифрование AES ECB и вам все еще нужно поддерживать это в Python 3, вы можете сделать это все еще с cryptography. Применяются те же предостережения, ECB недостаточно безопасен для реальных приложений . Реализовать этот ответ для Python 3, добавив автоматическую обработку отступов:

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

Опять же, здесь отсутствует подпись HMAC, и вам все равно не следует использовать ECB. Выше приведено просто для иллюстрации того, что cryptography может работать с общими криптографическими строительными блоками, даже теми, которые вы на самом деле не должны использовать.

24 голосов
/ 13 февраля 2014

Отказ от ответственности: Как уже упоминалось в комментариях, этот не должен использоваться для защиты данных в реальном приложении.

Что не так с шифрованием XOR?

https://crypto.stackexchange.com/questions/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


Как уже упоминалось, библиотека PyCrypto содержит набор шифров. «Шифр» XOR можно использовать для выполнения грязной работы, если вы не хотите делать это самостоятельно:

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

Шифр ​​работает следующим образом без необходимости дополнения открытого текста:

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

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

Кредит https://stackoverflow.com/a/2490376/241294 для функций кодирования / декодирования base64 (я новичок в Python).

10 голосов
/ 13 февраля 2017

Вот реализация URL Safe шифрования и дешифрования с использованием AES (PyCrypto) и base64.

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

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

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


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


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

Если вы столкнулись с такой проблемой, как эта https://bugs.python.org/issue4329 (TypeError: отображение символов должно возвращать целое число, None или Unicode) используйте str (шифр) при декодировании следующим образом

return obj2.decrypt (base64.urlsafe_b64decode (str (шифр))) *

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

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

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

In [16]: %timeit decode(b'67jjg-8_RyaJ-28=')
100000 loops, best of 3: 15.2 µs per loop
8 голосов
/ 25 февраля 2016

Рабочие функции кодирования / декодирования в python3 (очень мало адаптировано из ответа 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)
5 голосов
/ 20 октября 2017

Спасибо за отличные ответы.Ничего оригинального добавить, но вот несколько прогрессивных переписываний ответа qneill с использованием некоторых полезных возможностей Python.Я надеюсь, что вы согласны с тем, что они упрощают и уточняют код.

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() - пара элементов в списке с их индексом

перебирает символы в строке

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)

сборка списков с использованием понимания списков

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 вообще не нужны индексы списков.Полностью исключите переменные индекса цикла, используя zip и цикл:

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)

и некоторые тесты ...

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
4 голосов
/ 10 января 2017

Если вы хотите быть в безопасности, вы можете использовать Fernet, который криптографически обоснован.Вы можете использовать статическую «соль», если не хотите хранить ее отдельно - вы потеряете только защиту от словаря и атаки радуги.Я выбрал его, потому что я могу выбрать длинные или короткие пароли », что не так просто с 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

Если это слишком сложно, кто-то предложил simplecrypt

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...