Простой способ кодирования строки в соответствии с паролем?

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

Что-то вроде этого:

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

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

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

Ответ 1

Предполагая, что вы ищете только простое запутывание, которое будет скрывать вещи от очень случайного наблюдателя, и вы не собираетесь использовать сторонние библиотеки. Я бы порекомендовал что-то вроде шифра 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-безопасные символы (как следует из названия).

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

Ответ 2

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

Итак, используя 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()

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

Ответ 3

"encoded_c", упомянутый в ответе шифрования @smehmood Vigenere, должен быть "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)

Ответ 4

Здесь версия Python 3 функций из @qneill 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, чтобы отразить это.

Ответ 5

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'

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

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

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

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

Сокрытие

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

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

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

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

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

Ответ 6

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

Что не так с шифрованием 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!'

Благодарим fooobar.com/questions/23350/... за функции кодирования/декодирования base64 (я новичок в Python).

Ответ 7

Вот реализация 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 (шифр) при декодировании следующим образом

вернуть 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

Работающие функции кодирования/декодирования в 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)

Ответ 9

Спасибо за отличные ответы. Ничего оригинального для добавления, но вот некоторые прогрессивные перезаписи qneill answer, используя некоторые полезные возможности 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

Ответ 10

Если вы хотите быть в безопасности, вы можете использовать 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)

Ответ 11

Примечание: у меня были проблемы с использованием Windows + Python 3.6 + все ответы, включающие pycrypto (не удалось pip install pycrypto в Windows с помощью pip install pycrypto) или pycryptodome (ответы здесь с помощью from Crypto.Cipher import XOR неудачно, поскольку XOR не поддерживается этой вилкой pycrypto; и решения, использующие ... AES тоже не удалось с TypeError: Object type <class 'str'> cannot be passed to C code). Кроме того, библиотека simple-crypt имеет pycrypto качестве зависимости, поэтому это не вариант.


Вот решение с использованием пакета cryptography, которое вы можете установить как обычно с помощью pip install cryptography:

import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

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

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

Использование:

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

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

Замечания:

  • Вы можете адаптировать свою собственную соль, количество итераций и т.д.

  • не очень далеко от ответа @HCLivess, но цель здесь в том, чтобы иметь готовые к использованию функции encrypt и decrypt

  • Источник: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet

Ответ 12

Вы можете использовать AES для шифрования вашей строки паролем. Тем не менее, вы захотите выбрать достаточно надежный пароль, чтобы люди не могли легко догадаться, что это такое (извините, я не могу с этим поделать. Я подонок безопасности).

AES хорош с хорошим размером ключа, но его также легко использовать с PyCrypto.

Ответ 13

Это работает, но длина пароля должна быть точно 8. Это просто и требует 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

ВЫВОД:

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

Ответ 14

Другая реализация кода @qneill, которая включает контрольную сумму CRC исходного сообщения, генерирует исключение, если проверка не выполняется:

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

Ответ 15

Простым способом является использование библиотеки, а PyCrypto является хорошим.

Ответ 16

Внешние библиотеки предоставляют алгоритмы шифрования секретного ключа.

Например, модуль Cypher в PyCrypto предлагает множество алгоритмов шифрования:

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

Ответ 17

Кто бы ни пришел сюда (и баунтир), казалось, искал однострочники без особых настроек, которые другие ответы не дают. Итак, я выдвигаю base64.

Теперь, имейте в виду, что это только базовая запутанность, и она в ** НЕТ ПУТИ ДЛЯ БЕЗОПАСНОСТИ **, но вот некоторые из них:

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'

Несколько вещей, на которые стоит обратить внимание:

  • Вы будете хотеть иметь дело с большим/меньшим количеством кодирования/декодирования байтов в строку самостоятельно, в зависимости от вашего ввода/вывода. Посмотрите на bytes() и bytes::decode()
  • base64 легко узнаваем по типам используемых символов и часто заканчивается символами =. Такие люди, как я, абсолютно безразлично декодируют их в консоли javascript, когда мы видим их на веб-сайтах. Это так же просто, как btoa(string) (JS)
  • порядок - ключ + данные, как в b64, то, какие символы появляются в конце, зависит от того, какие символы находятся в начале (из-за смещения байтов. Википедия имеет несколько хороших объяснений). В этом случае начало закодированной строки будет одинаковым для всего, что закодировано этим ключом. Плюс в том, что данные будут более запутанными. Если сделать это наоборот, часть данных будет одинаковой для всех, независимо от ключа.

Теперь, если то, что вы хотели, даже не нуждалось ни в каком ключе, а просто в некотором запутывании, вы можете снова использовать base64, без каких-либо ключей:

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'

Ответ 18

если вы хотите защищенное шифрование:

для python 2, вы должны использовать keyczar http://www.keyczar.org/

для python 3, пока keyczar не доступен, я написал простую криптографию http://pypi.python.org/pypi/simple-crypt

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

Ответ 19

Таким образом, поскольку ничего критически важного не кодируется, и вы просто хотите зашифровать для обфускации.

Позвольте мне представить шифр Caeser

enter image description here

Шифр Цезаря или Смещение Цезаря, является одним из самых простых и широко известных методов шифрования. Это тип шифра замещения, в котором каждая буква в открытом тексте заменяется буквой с фиксированным числом позиций по алфавиту. Например, при сдвиге влево 3 D будет заменен на A, E станет B и так далее.

Пример кода для вашей справки:

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

Преимущества: он отвечает вашим требованиям, прост и выполняет кодировку 'y'.

Недостаток: может быть взломан простыми алгоритмами грубой силы (маловероятно, что кто-либо попытается пройти через все дополнительные результаты).