Python Crypto, открытый / закрытый ключ RSA, с большим файлом

Теперь я знаю, что открытый / закрытый ключ RSA может за один раз зашифровать только очень короткий ввод, но может ли кто-нибудь предоставить способ зашифровать любой тип файла (.txt, .phf, .exe и т. Д.) С помощью только открытого / закрытого ключа? Я не хочу иметь дополнительный ключ AES.

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

from Crypto.PublicKey import RSA
from Crypto import Random


random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
public_key = key.publickey()

f = open('C:\Users\Administrator\Desktop\jack.txt','r').read()

print 'original content: '+ f

enc_data = public_key.encrypt(f, 32)
print 'encrypted data: '
print enc_data

dec_data = key.decrypt(enc_data)
print 'decrypted data: '+ dec_data

Вот результат:

original content: Python Cryptography Toolkit

A collection of cryptographic modules implementing various algorithms and protocols.

Subpackages:

Crypto.Cipher
Secret-key (AES, DES, ARC4) and public-key encryption (RSA PKCS#1) algorithms
Crypto.Hash
Hashing algorithms (MD5, SHA, HMAC)
Crypto.Protocol
Cryptographic protocols (Chaffing, all-or-nothing transform, key derivation functions). This package does not contain any network protocols.
Crypto.PublicKey
Public-key encryption and signature algorithms (RSA, DSA)
Crypto.Signature
Public-key signature algorithms (RSA PKCS#1)
Crypto.Util
Various useful modules and functions (long-to-string conversion, random number generation, number theoretic functions)
encrypted data: 
('\x08\xe3\x9d\x03\x1e\xe9(\xe2\xc7\xc6e\x0b5\x02\xc0\xd8G\x1f\xf5\xb8\x9cMC\x93Z\x982\xa5\x97\xec\xab4\x18\xc2\xc8\xd9\xd3\x99aX\xd96b\x19\x96\xdc\x1d|F\xe0\xa9\xa9\xea\x03\x10>0g\x83\xdb\xeb\xdb\x13\x91\xc6\xd8\xf6\x95\xedE@A\x0bc\xae\xbe\xbe\xf0\xde\xcc\xcexk\x10\xb3\x86\xd3\xdd\xd0\xca@T2\x9a\x8a6ut\xb1\xaf\x07\x1f\xa2M\r\xf0D\xa2`h\xc3\x89\x18\x0e\xd4\xca\xee\xf5\xfc\x01\xed\x95}X\x1f\x13 1',)
decrypted data: ���J�rPX �����ju�a,�xm�'�]��ٟ�?y;�)��tĹ�,�D4^�ba�8����9q
+�i��l �q]Kd�Y���u��S�B���Ϲ�^�A3
.7��j��m�
�6�dl� qU

person Jack Feng    schedule 10.02.2015    source источник


Ответы (2)


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

На первый взгляд, ваши зашифрованные данные выглядят немного маленькими, чтобы соответствовать вашим входным данным, но я не так хорошо знаком с Crypto (я сам установил его только несколько дней назад), поэтому я не могу объяснить, что он сделал с вашим данные.

Но этот код у меня работает:

#!/usr/bin/env python

from Crypto.PublicKey import RSA
from Crypto import Random

src_data = 'To be, or not to be - that is the question.'
print `src_data`

random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print 'Key generated'

pub_key = key.publickey()
print 'Public key', pub_key

enc_data = pub_key.encrypt(src_data, 32)[0]
print `enc_data`

dec_data = key.decrypt(enc_data)
print `dec_data`

типичный результат

'To be, or not to be - that is the question.'
Key generated
Public key <_RSAobj @0xb7114dcc n(1024),e>
',\x17\xb1\x8a\x98\xb0-z\x8c\xb8r\x17\xa2\xfe[\x10I\x97\x93\x9d[\x93\x19&\\\x16V\xc2\xa3\x99\x80\xa5\x08\xafT\xb5iA|\x89\xeeJ\x90%\xceXv\x9f\x9f\xcb\\P"i\x00D\xd4\x16\xee\xa9\xe49\x18[\xa5\x0f\xd3\xfb\x91\xd5\x98\x1bP\xbf\xa4\xa5Dz\x8b7\x13\x9dqk+\xf7A\xd3\x12\x1c\x06\xcep\xf2\xba\xc6\xee\xf8\xa2\xb4\x04v\xfb\xb7>\xb3U\x17\xban\xf7\xc0oM+Tq\xce\xe3D\x83\xb9\xa4\x90\xe6c,\x18'
'To be, or not to be - that is the question.'

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

В Python 3 мы не можем передавать строки в функции шифрования или дешифрования, мы должны передавать байты. Кроме того, Python 3 не поддерживает синтаксис обратных кавычек, который получает repr объекта в Python 2.

Этот код вызывает методы строки .encode() и байтов .decode() для выполнения преобразований. Мы могли указать кодек кодирования, например

src_data.encode('utf-8')

но в этом нет необходимости, поскольку кодек UTF-8 используется по умолчанию.

from __future__ import print_function

from Crypto.PublicKey import RSA
from Crypto import Random

src_data = 'To be, or not to be - that is the question.'
print(repr(src_data))

random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
pub_key = key.publickey()
print('Key generated')

print(key.exportKey().decode())
print(pub_key.exportKey().decode())

enc_data = pub_key.encrypt(src_data.encode(), 32)[0]
print('Encoded\n', repr(enc_data))

dec_data = key.decrypt(enc_data).decode()
print('Decoded\n', repr(dec_data))    

Типичный вывод Python 3

'To be, or not to be - that is the question.'
Key generated
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDL/TzI4yHmlcC8qP3xWNieujmXR7CnEaZJrDH1Hyr/tGNa0aEE
jDIz+RlMntBbhOuiQMkMtCSB5X28h7HetiD4XkWTXmlIiKZQLZ074cO5mxF+HhF7
WIG30VONpX+Q4t/beqtaqbzyeIWvDdcCjUwOSQLrUKU5PX9LFzX+FnN1UwIDAQAB
AoGASRVZib+Wjb5pZy5EjQt/0J53s7ODnte78/k1jNS12xcN4aPpRG/WLLi6T7E2
hROCOIdtgJep3MAT5E/ZciledflaDwwmLo6+NsrhMppsNhpIHsvxWxmwxnH+bC2H
lpyeUmxku4xzqwYW4kuF3iaR45K2eUpXQyWTE9+pgvepgoECQQDT6Waiavstvs4W
btW2P4J+7O//NmTcvnyLTnhFTXklxTxnSun54HYOce8+TttsXWESTbzf91saN5SW
0vPyKK25AkEA9m3gbwFppiA65464jnDCP1AnrR15n3tbsLpaadYdRm07b+4BB0R2
M820cgber14JiGndOfv1uPl1Ooy0IH4hawJBAJKRC/uqIrAxGDlLz2SN6KQBHL1X
0csbtOhlDaphOzl0gaKvncTGCuFSzDY8NGdu7oTKX6hIXSp05sCqhy8mE4ECQE49
xKx5/llIkmtC3UYcdxAzGuXUHfGM8SfDg0FnQhRCSkTXhGwSSJVaEpjBpaJ4cP5m
3l6yqOn6CkZ0thw679ECQCWNC5hVEtsAb0TcjGdTpw+xTFusiZciNyHTQ64Zq2cc
ehQrxTRDIEBA4wIgUwrTwdVXk10OtpcVZvLIVjqdC84=
-----END RSA PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/TzI4yHmlcC8qP3xWNieujmX
R7CnEaZJrDH1Hyr/tGNa0aEEjDIz+RlMntBbhOuiQMkMtCSB5X28h7HetiD4XkWT
XmlIiKZQLZ074cO5mxF+HhF7WIG30VONpX+Q4t/beqtaqbzyeIWvDdcCjUwOSQLr
UKU5PX9LFzX+FnN1UwIDAQAB
-----END PUBLIC KEY-----
Encoded
 b'\x843\x9aJ\xe6\x91p\xd2\x9c\xd0r{37\xa2G\x13Q\xc7~\xbd5\xce\x9f\xd4\x16\xda\x11\x02.\xb7\xf1\xf3Q\x8c|\xb0R2B\x1b)e\xeaD\x8e\x11\x1b\xb0J:\xbal\xac\x86\xdcb}_\x16IX\xccd\x0c\xb5E?Im<\x04ORT\xc9\xc6K|;\xf3\xbcK\xfd\x89\x96ZF(\x0b\x82v\x19`\xc3\xa1N\x934*\x9c\xfcT\xf4i\x02g\x1fl\xec\xc1\x19z\x9f7\xa6}\xe2\xe3}\xaa|\x1e\x13z\xd9$\xea'
Decoded
 'To be, or not to be - that is the question.'

Нам не действительно здесь нужна кодировка UTF-8. Поскольку src_data - это чистая 7-битная строка ASCII, и мы встроили ее в скрипт как литерал, мы могли бы вместо этого предоставить ее как буквальную строку байтов:

src_data = b'To be, or not to be - that is the question.' 
person PM 2Ring    schedule 10.02.2015
comment
Чтобы это работало в Python 3, вам нужно закодировать src_data с помощью .encode('UTF-8') - person ScottMcC; 10.05.2017
comment
@ScottMcC Спасибо за предупреждение. Я обновил свой ответ, включив в него код, совместимый с Python 2/3. Я полагаю, что этот ответ не действительно отвечает на вопрос OP, но я думаю, что это все еще полезная демонстрация для тех, кто хочет использовать RSA для небольшого количества текста. - person PM 2Ring; 10.05.2017
comment
@PM 2Ring Добавленный вами дополнительный контент, безусловно, полезен, спасибо за обновление. К сожалению, вывод ошибки из pycrypto не очень полезен, когда вы забываете правильно кодировать строку в Python 3: TypeError: argument 1 must be int, not str. Мне потребовалось немного времени, чтобы понять, что это проблема с кодировкой ... - person ScottMcC; 10.05.2017
comment
@ScottMcC Крипто по имени, крипто по своей природе. ;) Хорошо, что Python 3 проводит четкое различие между текстовыми строками и байтовыми строками, и, хотя я понимаю, почему элементы в объекте bytes (или bytearray) являются целыми числами, многие программисты Python соглашаются, что это может немного сбивать с толку когда случается такая ошибка. Даже некоторые из основных разработчиков задавались вопросом, правильно ли они приняли там решение. - person PM 2Ring; 10.05.2017

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

Решение - использовать гибридное шифрование.

  1. Сгенерируйте случайную байтовую строку 16, 24 или 32 байта, которая будет использоваться в качестве ключа AES,
  2. Зашифруйте фактические данные с помощью AES, используя ранее сгенерированный ключ и
  3. Зашифруйте ключ AES с помощью RSA.

AES шифрование:

from Crypto.Cipher import AES
from Crypto import Random

aeskey = Random.new().read(32)
iv = Random.new().read(AES.block_size)
cipher = AES.new(aeskey, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')

Шифрование RSA (используйте правильное заполнение, например OAEP, потому что учебник RSA ужасно сломан):

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA

message = aeskey
random_generator = Random.new().read
rsakey = RSA.generate(1024, random_generator)
cipher = PKCS1_OAEP.new(rsakey.publickey())
ciphertext = cipher.encrypt(message)

И отправляйте только msg и ciphertext. Расшифровка аналогична, но в обратном порядке, потому что сначала нужно восстановить ключ AES из зашифрованного текста RSA. Не забудьте отрезать IV при расшифровке с помощью AES.

person Artjom B.    schedule 10.02.2015
comment
Могу ли я сделать это без дополнительного ключа AES? Потому что я действительно ищу просто зашифровать / расшифровать и подписать / проверить файл только с открытым / закрытым ключом. - person Jack Feng; 10.02.2015
comment
Я сказал, что если файл слишком велик, вы не можете его зашифровать / расшифровать с помощью простого или дополненного RSA. Вам нужно гибридное шифрование или RSA-KEM. Подпись и проверка будут работать, потому что подпись RSA - это шифрование (с закрытым ключом) хэша содержимого файла и, следовательно, не зависит от размера. - person Artjom B.; 10.02.2015
comment
мой профессор хотел зашифровать / подписать и расшифровать / проверить только с помощью пары ключей, поэтому, когда вы отправляете кому-то что-то, все, что им нужно, это зашифрованный файл и только один из ключей. Подпись, о которой вы говорили, отправляет 3 вещи: файл, подпись и открытый ключ. Он хотел, чтобы подпись была включена в файл при подписании, поэтому все, что вам нужно, это открытый ключ для проверки. - person Jack Feng; 10.02.2015
comment
Для проверки по-прежнему нужен только открытый ключ, а для расшифровки по-прежнему нужен только закрытый ключ. Это просто каскад. Вы можете увидеть _1 _ + _ 2_ шифрования как единый зашифрованный текст. Но подпись не включает данные точно так же, как ваша собственноручная подпись не включает вашу плоть и кровь. - person Artjom B.; 10.02.2015