Простой способ закодировать строку по паролю?


123

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

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

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

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

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


28
Термин «пароль» здесь неуместен. Вы используете его как криптографический КЛЮЧ, и вы должны использовать эту терминологию, чтобы избежать путаницы в ваших вопросах, а также в любых документах, комментариях, спецификациях, планах тестирования и т. Д.
Джим Деннис,

2
«Я бы хотел, чтобы это был способ, которым я могу зашифровать / расшифровать документ Word с помощью пароля». В Word уже есть встроенная опция для шифрования ваших документов, если вам просто нужно зашифровать текстовые документы.
Байрон Филер,

2
Интересно, что согласно этой исследовательской статье о подобных подводных камнях хранения паролей , разработчики, использующие Stack Overflow, как правило, создают менее безопасный код. Гы, интересно, почему?
forest

Также следует прочитать этот ответ службы безопасности.SE
kelalaka

Нельзя просто реализовать кодирование / декодирование просто
luckyging3r

Ответы:


70

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

Шифр Виженера

Это быстро и легко реализовать. Что-то вроде:

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-безопасные символы (как следует из названия).

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


6
Я исправил скрипт Смехмуда и добавил функцию декодирования gist.github.com/ilogik/6f9431e4588015ecb194
Адриан Местер

3
Внимание! Код smehmood и исправление Адриана Местера работают только для строк с символами из нижнего диапазона ascii! См. Приоритет оператора%, ввода Unicode и т. Д. Рабочий код см. В ответе
qneill

2
@smehmood Я получаю следующую ошибку'str' object cannot be interpreted as an integer
Рохит Хатри

3
"for i in xrange (string)" может потребоваться заменить на "for i in xrange (len (string))"
user3113626

2
кодер и декодер для Python 2 и 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
имана

71

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

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

import base64
from Crypto.Cipher import AES

msg_text = b'test some plain text here'.rjust(32)
secret_key = b'1234567890123456'

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

Если кто-то завладеет вашей базой данных и вашей кодовой базой, он сможет расшифровать зашифрованные данные. Береги себя secret_key!


3
Я не думаю, что это сработает, если длина msg_text не кратна 16 байтам, поскольку для шифрования AES требуются блоки, кратные 16 по длине. В рабочей реализации для msg_text произвольной длины потребуется добавить к строке заполнение, чтобы получить ее длину, кратную 16.
tohster 05

6
Пример с заполнением: paste.ubuntu.com/11024555 Работает с произвольным паролем и длиной сообщения.
Иман

3
@Ethan Нет, эта конкретная encryptфункция имеет состояние dlitz.net/software/pycrypto/api/current/… поэтому вам не следует пытаться использовать ее повторно.
Уилл

1
@Will +1 Спасибо за информацию и ссылку. Спас меня от очень дорогого исправления ошибки в будущем.
Итан,

3
re - «очевидно, никогда не использовать ECB в сильных системах»: я просто экспериментирую с этим для собственного удовольствия. но я видел приведенный выше комментарий в вашем коде. для тех из нас, у кого очень мало знаний в области безопасности / шифрования / теории информации, почему «никогда не использовать»? может быть нужен другой вопрос ... а может есть ссылка на него.
Тревор Бойд Смит,

69

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'

Фернет с паролем - ключ, полученный из пароля, несколько снижает безопасность

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

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

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

Сокрытие

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

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

Галуа / Счетчик Режим блочного шифра производит зашифрованный текст и тег , чтобы служить той же цели, поэтому могут быть использованы , чтобы служить той же цели. Обратной стороной является то, что, в отличие от 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, но без подкладки

Это подход, которому следует All Іs Vаиітy , хотя и неправильно. Это 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 и временная метка; вам придется добавить их самостоятельно.

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


51

"Encoded_c", упомянутый в ответе шифра Виженера @ 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)

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

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


2
Очень полезно, спасибо. Ниже я разместил версию Python 3 (в комментариях она выглядела некрасиво)
Райан Барретт

1
«Закон Шнайера» : любой, от самого невежественного любителя до лучшего криптографа, может создать алгоритм, который он сам не сможет сломать. Не используйте это, это даже близко не безопасно.
zaph

3
Большой! Эта версия работает также для строк с акцентами, тогда как версия @ smehmood - нет. Пример использования: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)работает нормально.
Basj

2
Вот плагин Sublime text на основе этого кода, позволяющий легко кодировать / декодировать текст с помощью CTRL + SHIFT + P, затем «Eeencode» или «Dddecode».
Basj

2
@basj Спасибо за пример
sk03

49

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

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, чтобы отразить это.


4
Спасибо, Райан, черт возьми, ты опечатал @qniell
qneill

3
Для тех, кому интересно, различия заключаются в .encode()).decode(). в возврате encode(), а .decode()во второй строке в decode().
RolfBly

2
Хм, закодированный код не совсем уникальный, я провел тест, и он показывает, что каждый кодовый ключ 11,22,33,44, ..., 88,99,111,222, ... всегда имеет другой тот же код, что и раньше. Но я ценю это
Hzzkygcs

1
@Ryan Barrett, можно ли при расшифровке проверить правильность пароля. Допустим, я отправил закодированную строку одному человеку, который знает ключ, а что, если он введет ключ с опечаткой? Декодирование по-прежнему дает ему "декодированную" строку, но она не та, как он может определить?
Heinz

26

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

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

/crypto/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).


Примечание: модуль Crypto устанавливается в python3 с помощью установленного pycrptop, а не Crypto. sudo pip3 install pycrypto,
Nikhil VJ

2
примечание: pycrypto не удалось установить на herokuapp с моей стороны. Я нашел эту публикацию ... кажется, говорит, что пакет pycrypto был заменен другим пакетом, называемым pycryptodome insteal, и этот метод XOR устарел: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
Никогда не используйте этот метод , обратите внимание на описание этого «шифра» в документации : игрушечный шифр XOR, XOR - один из самых простых потоковых шифров. Шифрование и дешифрование выполняются с помощью операции XOR с потоком ключей, созданным путем соединения ключа. Не используйте его в реальных приложениях! ,
Мартин Питерс

@MartijnPieters, ты прав. Надеюсь, моя редакция прояснила этот момент.
poida

12

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

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

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

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

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

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

Если вы столкнулись с такой проблемой, как эта, https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) используйте str(cipher)при декодировании следующим образом:

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

Тест:

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 осуждается) TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@Basj, извини .. Я не использую окна, поэтому не могу исправить.
All Іs Vаиity

Не создавайте IV и не храните его на уровне модуля, вам необходимо включить IV в возвращаемое сообщение с зашифрованным текстом! Теперь вы столкнулись с двумя проблемами: перезапуск процесса Python дает вам новый IV, что делает невозможным расшифровку ранее зашифрованных сообщений, а тем временем вы повторно используете IV для нескольких сообщений, что эффективно снижает безопасность до уровня ECB.
Мартейн Питерс

1
@ AllІsVаиітy Решено b'...', я отредактировал ответ для использования в будущем!
Basj

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)

8

Спасибо за отличные ответы. Ничего оригинального, чтобы добавить, но вот несколько прогрессивных переписываний ответа 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 и cycle:

from itertools import cycle


def encode_zip_cycle(key, clear):
    enc = [chr((ord(clear_char) + ord(key_char)) % 256)
                for clear_char, key_char in zip(clear, cycle(key))]
    return base64.urlsafe_b64encode("".join(enc))


def decode_zip_cycle(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(enc_char) - ord(key_char)) % 256)
                for enc_char, key_char in zip(enc, cycle(key))]
    return "".join(dec)

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

msg = 'The quick brown fox jumps over the lazy dog.'
key = 'jMG6JV3QdtRh3EhCHWUi'
print('cleartext: {0}'.format(msg))
print('ciphertext: {0}'.format(encode_zip_cycle(key, msg)))

encoders = [qneill_encode, encode_enumerate, encode_comprehension, encode_zip_cycle]
decoders = [qneill_decode, decode_enumerate, decode_comprehension, decode_zip_cycle]

# round-trip check for each pair of implementations
matched_pairs = zip(encoders, decoders)
assert all([decode(key, encode(key, msg)) == msg for encode, decode in matched_pairs])
print('Round-trips for encoder-decoder pairs: all tests passed')

# round-trip applying each kind of decode to each kind of encode to prove equivalent
from itertools import product
all_combinations = product(encoders, decoders)
assert all(decode(key, encode(key, msg)) == msg for encode, decode in all_combinations)
print('Each encoder and decoder can be swapped with any other: all tests passed')

>>> python crypt.py
cleartext: The quick brown fox jumps over the lazy dog.
ciphertext: vrWsVrvLnLTPlLTaorzWY67GzYnUwrSmvXaix8nmctybqoivqdHOic68rmQ=
Round-trips for encoder-decoder pairs: all tests passed
Each encoder and decoder can be swapped with any other: all tests passed

Очень хороший @Nick, хорошая прогрессия питонизмов и тесты тоже загружаются. Чтобы отдать должное, я просто исправлял ошибку в исходном ответе Smehmood stackoverflow.com/a/2490718/468252 .
qneill

4

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

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

3

Кто бы ни пришел сюда (и щедрый), похоже, искал однострочные с небольшой настройкой, чего не предоставляют другие ответы. Так что выдвигаю 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'

2
Да, если вы не беспокоитесь о безопасности, тогда base64 лучше, чем шифрование.
Мартейн Питерс

О том, что другие ответы не являются однострочными: вопрос не в этом. Они просят вызвать две функции. И Fernet(key).encrypt(message)это всего лишь одно выражение, подобное вашему вызову base64.
Мартейн Питерс

И вы должны удалить его keyсовсем. Множество разработчиков будут копировать и вставлять из Stack Overflow, не обращая внимания, и будут считать, что ключ секретный. Если вы должны включить его, то, по крайней мере, не используйте его и предупреждайте или вызывайте исключение, если оно все равно используется. Не стоит недооценивать глупость культуры копирования и вставки и свою ответственность за выполнение разумных функций.
Мартейн Питерс

3

Приведу 4 решения:

1) Использование шифрования Fernet с cryptographyбиблиотекой

Вот решение с использованием пакета cryptography, которое вы можете установить как обычно 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')

Вы можете адаптироваться со своей собственной солью, количеством итераций и т. Д. Этот код не очень далеко от ответа @ HCLivess, но цель здесь - иметь готовые к использованию encryptи decryptфункции. Источник: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Примечание: используйте .encode()и .decode()везде, если вам нужны строки 'John Doe'вместо байтов, например b'John Doe'.


2) Простое шифрование AES с Cryptoбиблиотекой

Это работает с Python 3:

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

def cipherAES(password, iv):
    key = SHA256.new(password).digest()
    return AES.new(key, AES.MODE_CFB, iv)

def encrypt2(plaintext, password):
    iv = Random.new().read(AES.block_size)
    return base64.b64encode(iv + cipherAES(password, iv).encrypt(plaintext))

def decrypt2(ciphertext, password):
    d = base64.b64decode(ciphertext)
    iv, ciphertext = d[:AES.block_size], d[AES.block_size:]
    return cipherAES(password, iv).decrypt(ciphertext)

# Example:    

print(encrypt2(b'John Doe', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'wrongpass'))  # wrong password: no error, but garbled output

Примечание: вы можете удалить base64.b64encodeи, .b64decodeесли вы не хотите выводить текст в читаемом виде и / или если вы все равно хотите сохранить зашифрованный текст на диск в виде двоичного файла.


3) AES с использованием улучшенной функции создания ключа пароля и возможности проверки «введен ли неправильный пароль» с помощью Cryptoбиблиотеки

Решение 2) с AES "режимом CFB" нормально, но имеет два недостатка: тот факт, что SHA256(password)его можно легко перебрать с помощью поисковой таблицы, и то, что нет возможности проверить, был ли введен неправильный пароль. Здесь это решается использованием AES в «режиме GCM», как обсуждалось в AES: как определить, что был введен неверный пароль? и Безопасен ли этот способ сказать «Вы ввели неправильный пароль»? :

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 (библиотека не требуется)

Взято из https://github.com/bozhu/RC4-Python/blob/master/rc4.py .

def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        yield S[(S[i] + S[j]) % 256]

def encryptRC4(plaintext, key, hexformat=False):
    key, plaintext = bytearray(key), bytearray(plaintext)  # necessary for py2, not for py3
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    keystream = PRGA(S)
    return b''.join(b"%02X" % (c ^ next(keystream)) for c in plaintext) if hexformat else bytearray(c ^ next(keystream) for c in plaintext)

print(encryptRC4(b'John Doe', b'mypass'))                           # b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a'
print(encryptRC4(b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a', b'mypass'))   # b'John Doe'

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


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

Да, конечно @MartijnPieters, но цель состоит в том, чтобы иметь простой код для простых целей, как того требует OP, с двумя параметрами : простой текст + пароль. Конечно, для более сложного сценария (например, базы данных) вы будете использовать все эти дополнительные параметры.
Basj

В дополнительных параметрах нет необходимости! Я включаю эту информацию, закодированную в непрозрачное возвращаемое значение password_encrypt().
Мартейн Питерс

@MartijnPieters Действительно хорошее решение.
Basj

2

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

Не используйте фиксированный IV! Рандомизируйте IV и вместо этого включите его в зашифрованный текст. В противном случае вы также можете использовать режим ECB; в противном случае легко распознать повторяющиеся текстовые сообщения.
Мартейн Питерс

Кроме того, кажется, что проект pyDes мертв; домашняя страница исчезла, а последней версии PyPI исполнилось 9 лет.
Мартейн Питерс

2

Другая реализация кода @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

2

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

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


3
Спасибо, Алан. Но для ясности я не шифрую сами пароли. В приведенном выше примере я шифрую строку «John Doe» в соответствии с паролем «mypass», который является простым паролем, который я использую в своем исходном коде. Не используются ни пароли пользователей, ни другая конфиденциальная информация. Я отредактировал свой вопрос, чтобы прояснить это.
RexE

1
AES великолепен, если его правильно использовать. Однако его легко использовать неправильно; здесь есть по крайней мере один ответ, который использует небезопасный режим блочного шифрования, а еще два ответа не справляются с обработкой значения IV. Лучше использовать хорошую библиотеку с четко определенным рецептом, такую ​​как Fernet!
Мартин Питерс

На самом деле, это очень проницательное наблюдение. Однажды я нащупал капельницу.
Алан

0

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

Например, 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).


0

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

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

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

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


Из репозитория Keyczar : Важное примечание: Keyczar устарел. Разработчики Keyczar рекомендуют Tink , но версии Tink для Python не существует.
Мартейн Питерс

0

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

Позвольте представить шифр цезаря

введите описание изображения здесь

Шифр Цезаря или сдвиг Цезаря - один из самых простых и широко известных методов шифрования. Это тип шифра подстановки, в котором каждая буква в открытом тексте заменяется буквой на фиксированное количество позиций в алфавите. Например, при сдвиге влево 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))

Достоинства: он отвечает вашим требованиям, прост и выполняет функции кодирования.

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


0

Добавление еще одного кода с декодированием и кодированием для справки

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()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.