получение исключения IllegalBlockSizeException: данные не должны быть длиннее 256 байт при использовании rsa

Я использую ключ rsa для шифрования длинной строки, которую я отправлю на свой сервер (зашифрую ее с помощью открытого ключа сервера и моего закрытого ключа), но он выдает исключение типа javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes. Я чувствую, что не понял работу rsa должным образом до тех пор, пока сейчас (причиной этого является использование встроенных библиотек).
Кто-нибудь, пожалуйста, объясните, почему возникает это исключение. Разве невозможно отправить длинную строку в зашифрованном виде?


person Ashwin    schedule 04.04.2012    source источник
comment
Просто используйте HTTPS, и шифрование будет прозрачным.   -  person zaph    schedule 19.07.2016


Ответы (5)


Алгоритм RSA может шифровать только данные, которые имеют максимальную длину байта длины ключа RSA в битах, разделенных на восемь минус одиннадцать байтов заполнения, то есть количество максимальных байтов = длина ключа в битах / 8-11.

Итак, в основном вы делите длину ключа на 8-11 (если у вас есть отступы). Например, если у вас есть 2048-битный ключ, вы можете зашифровать 2048/8 = 256 байтов (- 11 байтов, если у вас есть заполнение). Итак, либо используйте больший ключ, либо вы зашифруете данные симметричным ключом и зашифруете этот ключ с помощью rsa (что является рекомендуемым подходом).

Это потребует от вас:

  1. сгенерировать симметричный ключ
  2. Зашифруйте данные симметричным ключом
  3. Зашифруйте симметричный ключ с помощью rsa
  4. отправить зашифрованный ключ и данные
  5. Расшифруйте зашифрованный симметричный ключ с помощью rsa
  6. расшифровать данные симметричным ключом
  7. сделано :)
person John Snow    schedule 04.04.2012
comment
Почему существует такое ограничение, что вы можете шифровать данные только до определенной длины? - person Ashwin; 07.04.2012
comment
Ограничение key length in bits / 8 - 11 действует только при использовании PKCS1Padding. Например, при NoPadding ограничение будет key length in bits / 8. - person divanov; 19.11.2013
comment
И заполнение PKCS # 1v1.5, и отсутствие заполнения небезопасны. Используйте заполнение OAEP (которое уменьшает размер блока более чем на 11 байт) - person CodesInChaos; 13.08.2014
comment
Превосходно. Этот ответ помог мне зашифровать огромные данные. пример - person Waleed Abdalmajeed; 19.10.2017
comment
Это правильный ответ. Более того, хороший пример - Валид Абдалмаджид :-) - person Jose Flavio Quispe Irrazábal; 30.04.2019
comment
Спасибо, приятель :) @ JoseFlavioQuispeIrrazábal - person Waleed Abdalmajeed; 27.02.2020
comment
@ john-snow, Спасибо, Ваш ответ спас мне день - person motevalizadeh; 30.08.2020

Основываясь на ответе @John Snow, я сделал пример

  1. Сгенерировать симметричный ключ (AES со 128 битами)

    KeyGenerator generator = KeyGenerator.getInstance("AES");
    generator.init(128); // The AES key size in number of bits
    SecretKey secKey = generator.generateKey();
    
  2. Шифровать обычный текст с помощью AES

    String plainText = "Please encrypt me urgently..."
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
    
  3. Зашифруйте ключ с помощью открытого ключа RSA

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair keyPair = kpg.generateKeyPair();
    
    PublicKey puKey = keyPair.getPublic();
    PrivateKey prKey = keyPair.getPrivate();
    
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.PUBLIC_KEY, puKey);
    byte[] encryptedKey = cipher.doFinal(secKey.getEncoded()/*Seceret Key From Step 1*/);
    
  4. Отправить зашифрованные данные (byteCipherText) + зашифрованный ключ AES (encryptedKey)

  5. На стороне клиента расшифруйте симметричный ключ с помощью закрытого ключа RSA

    cipher.init(Cipher.PRIVATE_KEY, prKey);
    byte[] decryptedKey = cipher.doFinal(encryptedKey);
    
  6. Расшифровать зашифрованный текст с помощью расшифрованного симметричного ключа

    //Convert bytes to AES SecertKey
    SecretKey originalKey = new SecretKeySpec(decryptedKey , 0, decryptedKey .length, "AES");
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
    byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
    String plainText = new String(bytePlainText);`
    
person Waleed Abdalmajeed    schedule 19.10.2017
comment
есть ли ошибка на шаге 3? Я не вижу, чтобы secKey был зашифрован, intead Cipher инициализирован, поскольку RSA шифрует зашифрованные данные. - person user482963; 05.04.2018
comment
он говорит, что данные не должны быть длиннее 256 байт - person MrDumb; 14.06.2019
comment
@MrDumb, можете ли вы подробнее рассказать? на каком этапе? - person Waleed Abdalmajeed; 07.11.2019

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

У вас проблема с 256 байтами - это потому, что вы, вероятно, работаете с 2048-битными ключами. Ключи могут зашифровать любое целое число в диапазоне от 0 до 2^2048 - 1 в тот же диапазон, и это означает, что ваши данные должны быть 256 байтов или меньше.

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

person sarnold    schedule 04.04.2012
comment
Почему существует такое ограничение, что вы можете шифровать данные только до определенной длины? - person Ashwin; 07.04.2012
comment
Поскольку RSA выполняется на конечном кольце, только числа, которые существуют, являются целыми числами в диапазоне [0, 2^2048-1] включительно. Любое сообщение длиной более 2048 бит представляет собой число за пределами этого диапазона и должно быть закодировано либо двумя блоками, либо - если вы хотите безопасности - все сообщение должно быть зашифровано с помощью сеансового ключа. Реально развернутый RSA должен защищать от множественных атак и никогда не работать над необработанный открытый текст - одна из важных составляющих безопасного использования RSA. - person sarnold; 09.04.2012

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

Вы можете найти библиотеку на странице GitHub - random-symric-crypto

 final RandomSymmetricCipher cipher = new RandomSymmetricCipher();

 // Encrypt the data and the random symmetric key.
 final CryptoPacket cryptoPacket = cipher.encrypt(inputData, PRIVATE_KEY_BASE64);

 // Convert the CryptoPacket into a Base64 String that can be readily reconstituted at the other end.
 final CryptoPacketConverter cryptoPacketConverter = new CryptoPacketConverter();
 final String base64EncryptedData = cryptoPacketConverter.convert(cryptoPacket);
 System.out.println("Base64EncryptedData=" + base64EncryptedData);

 // Decrypt the Base64 encoded (and encrypted) String.
 final byte[] outputData = cipher.decrypt(base64EncryptedData, PUBLIC_KEY_BASE64);
person William    schedule 07.12.2013
comment
Я не могу найти, где вы указываете, какой режим заполнения использовать. Вы должны явно указать заполнение OAEP, поскольку другие общие заполнители, такие как PKCS # 1v1.5 или отсутствие заполнения вообще, небезопасны. - person CodesInChaos; 13.08.2014
comment
Рад принять запрос на перенос. У вас есть URL-адрес, подтверждающий, что отсутствие отступов небезопасно? - person William; 13.08.2014
comment
Бонех написал хороший обзор атак на RSA. На Crypto.se также есть множество вопросов о RSA для учебников. Я не смог найти MAC в симметричной части вашего кода. Это означает, что вы будете уязвимы для активных атак, таких как оракулы заполнения. Старый и все еще популярный padding PKCS # 1v1.5 также уязвим для активных атак (если вы не обойдете слабость действительно осторожно), а именно атаки Блейхенбахера. - person CodesInChaos; 13.08.2014
comment
Заполнения указываются в классе RandomSymmetricCipher. Он использует DESede / CBC / PKCS5Padding для симметричного и RSA / ECB / PKCS1Padding для шифра с открытым ключом. Их поменять несложно. stackoverflow.com/a/10935308/493682 перечисляет несколько вариантов и ссылки на другие. - person William; 15.08.2014
comment
Итак, если у вас есть сервер, который получает сообщения и напрямую их дешифрует, возвращая ошибку, если он недействителен (довольно распространенная конфигурация), злоумышленник может использовать этот сервер для дешифрования сообщения. Есть две соответствующие атаки: стандартный оракул заполнения против неаутентифицированного CBC и Блейхенбахера против RSA с заполнением PKCS # 1v1.5. - person CodesInChaos; 15.08.2014
comment
Да я понимаю нападение. Я не думаю, что ни одна из двух конфигураций заполнения, которые я использовал, подпадает под это. Хотя, если вы можете показать обратное и предоставить лучшую альтернативу, я весь уши. - person William; 15.08.2014
comment
Я загрузил и успешно использовал ваш код из git ... ОЧЕНЬ КЛАССНАЯ РАБОТА! Сэкономил кучу времени и избавил меня от головной боли. Благодарю вас! - person Michael Sims; 22.05.2019

вам нужно разделить свои данные по publicKey

int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = ""//the data after encrypted;
for (String s : datas) {
    mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;


public static String bcd2Str(byte[] bytes) {
    char temp[] = new char[bytes.length * 2], val;

    for (int i = 0; i < bytes.length; i++) {
        val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
        temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

        val = (char) (bytes[i] & 0x0f);
        temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
    }
   return new String(temp);
}
person Carl    schedule 19.07.2016
comment
То, что вы можете что-то делать, не означает, что вы должны это делать. Что необходимо, так это гибридное шифрование, при котором данные шифруются симметричным алгоритмом, таким как AES, с использованием случайного ключа, а ключ зашифровывается с помощью RSA. - person zaph; 19.07.2016
comment
Примечания: 1. data - множественное число, datum - единственное число. 2. Метод bcd2Str действительно выполняет шестнадцатеричное кодирование [0-9A-Z]. 3. Возможно, нет необходимости кодировать двоичный вывод с помощью шифрования RSA, и если кодирование необходимо, в то время как шестнадцатеричный код в порядке, обычно используется кодирование Base64, оно более компактно. - person zaph; 19.07.2016