Шифрование/дешифрование данных в питоне с солью

Я хотел бы знать, как я могу шифровать данные с помощью сгенерированного солевого ключа, а затем расшифровывать его с помощью python?

Я прошел через множество веб-сайтов и модулей, и все они отлично смотрятся на части encrpytion, но никто не может расшифровать, как кажется.

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

Я обнаружил, что модуль mcrypt будет работать лучше всего с этим, но документации для модуля python-mcrypt (которая в настоящее время устарела и не поддерживается) недостаточно.

Ответы

Ответ 1

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

В моем ответе я использовал pycrypto, поэтому нам нужно импортировать несколько из этих библиотек.

import Crypto.Random
from Crypto.Cipher import AES
import hashlib

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

# salt size in bytes
SALT_SIZE = 16

# number of iterations in the key generation
NUMBER_OF_ITERATIONS = 20

# the size multiple required for AES
AES_MULTIPLE = 16

Чтобы использовать соль, я сделал схему шифрования на основе пароля. Я использовал RSA PKCS # 5 стандарт для генерации и дополнения ключа шифрования на основе пароля, адаптированного для алгоритма шифрования AES.

Чтобы сгенерировать ключ, пароль и соль объединяются. Эта комбинация хэшируется столько раз, сколько требуется.

def generate_key(password, salt, iterations):
    assert iterations > 0

    key = password + salt

    for i in range(iterations):
        key = hashlib.sha256(key).digest()  

    return key

Чтобы проложить текст, вы выясните, сколько лишних байтов у вас осталось за четным числом 16. Если оно равно 0, вы добавляете 16 байтов заполнения, если оно равно 1, вы добавляете 15 и т.д. Таким образом, вы всегда добавляйте отступы. Символ, который вы набиваете, - это символ с тем же значением, что и количество байтов заполнения (chr(padding_size)), чтобы помочь удалить отступ в конце (ord(padded_text[-1])).

def pad_text(text, multiple):
    extra_bytes = len(text) % multiple

    padding_size = multiple - extra_bytes

    padding = chr(padding_size) * padding_size

    padded_text = text + padding

    return padded_text

def unpad_text(padded_text):
    padding_size = ord(padded_text[-1])

    text = padded_text[:-padding_size]

    return text

Шифрование требует создания случайной соли и использования этого вместе с паролем для генерации ключа шифрования. Текст дополняется с помощью вышеуказанной функции pad_text, а затем зашифровывается с помощью шифрованного объекта. Зашифрованный текст и соль объединяются и возвращаются в результате. Если вы хотите отправить это как открытый текст, вам нужно будет закодировать его с помощью base64.

def encrypt(plaintext, password):
    salt = Crypto.Random.get_random_bytes(SALT_SIZE)

    key = generate_key(password, salt, NUMBER_OF_ITERATIONS)

    cipher = AES.new(key, AES.MODE_ECB)

    padded_plaintext = pad_text(plaintext, AES_MULTIPLE)

    ciphertext = cipher.encrypt(padded_plaintext)

    ciphertext_with_salt = salt + ciphertext

    return ciphertext_with_salt

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

def decrypt(ciphertext, password):
    salt = ciphertext[0:SALT_SIZE]

    ciphertext_sans_salt = ciphertext[SALT_SIZE:]

    key = generate_key(password, salt, NUMBER_OF_ITERATIONS)

    cipher = AES.new(key, AES.MODE_ECB)

    padded_plaintext = cipher.decrypt(ciphertext_sans_salt)

    plaintext = unpad_text(padded_plaintext)

    return plaintext

Сообщите мне, есть ли у вас какие-либо другие вопросы/разъяснения.

Ответ 2

Вам не нужно ничего, кроме RNCryptor:

import rncryptor

data = '...'
password = '...'

# rncryptor.RNCryptor methods
cryptor = rncryptor.RNCryptor()
encrypted_data = cryptor.encrypt(data, password)
decrypted_data = cryptor.decrypt(encrypted_data, password)
assert data == decrypted_data

# rncryptor functions
encrypted_data = rncryptor.encrypt(data, password)
decrypted_data = rncryptor.decrypt(encrypted_data, password)
assert data == decrypted_data

Он обеспечивает семантически безопасное шифрование (произвольная соль и IV для каждого шифрования) и включает в себя безопасную проверку целостности (зашифрованный текст нельзя без каких-либо манипуляций) через HMAC.

RNCryptor также имеет определенный формат данных, поэтому вам не нужно об этом думать и реализовать на многих языках.