Шифрование RSA с OAEP между Java и JavaScript

Я пытаюсь зашифровать короткую строку в JavaScript и расшифровать ее в Java. Расшифровка не удалась, и я думаю, что это из-за разницы в блочном режиме и/или дополнении между двумя платформами. Я попытался зашифровать одну и ту же строку как в Java, так и в JavaScript и получил разные результаты, что указывает на то, что разница действительно есть. Вот код Java, который создает ключ:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();

А вот код Java, который я использовал для проверки шифрования:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] bytes = cipher.doFinal("asdf".getBytes());

Я отправляю открытый ключ в процесс JavaScript и преобразовываю его в ArrayBuffer с именем переменной publicKey. Я проверил, что ключ на стороне JavaScript совпадает с ключом на стороне Java (путем экспорта с помощью crypto.subtle.exportKey и проверки байтов). Вот код JavaScript, который я использовал для проверки шифрования:

crypto.subtle.importKey('spki', publicKey,
                        {hash: 'SHA-256', name: 'RSA-OAEP'}, true,
                        ['encrypt'])
      .then((key) => {
        crypto.subtle.encrypt({name: 'RSA-OAEP'}, key,
                              new TextEncoder().encode('asdf'))
              .then((buffer) => {

              });
      });

Содержимое массива байтов в Java и буфера массива в JavaScript не совпадают. Настройки, в которых я не уверен, — это параметр Cipher#getInstance на стороне Java и параметры importKey и encrypt на стороне JavaScript. Существуют ли какие-либо настройки, которые будут работать между Java и JavaScript с использованием встроенных классов? Или мне стоит посмотреть на сторонние библиотеки (например, Bouncy Castle)?

Вы видели это?

kelalaka 05.04.2019 00:32

@kelalaka Я не видел этого вопроса, спасибо! К сожалению, похоже, что единственным рабочим предложением из этого вопроса было использование пакета node-forge, и я действительно не хочу использовать сторонний пакет, если его можно избежать.

Andy King 05.04.2019 07:22

@AndyKing спасибо за вопрос. У меня была такая же проблема, и я мог найти решение без использования сторонних библиотек. Извините, если уже поздно :).

Chip 10.01.2020 09:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
3
7 175
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Похоже, встроенное шифрование/дешифрование в JavaScript и Java не имеет совместимых настроек для шифрования RSA. Жизнеспособным решением является библиотека forge из github (кузница на гитхабе). Настройки ключа описаны на странице github следующим образом (Примеры RSA):

// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  mgf1: {
    md: forge.md.sha1.create()
  }
});

Это старое, но вот альтернативное решение, если вы хотите использовать тонкое шифрование в javascript и контролировать расшифровку java.

Вот как вы расшифровываете в Java, предполагая, что вы использовали исходный код JS в вопросе для шифрования:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams);
byte[] decrypted = cipher.doFinal(encryptedBytes)

Проблема с шифром RSA/ECB/OAEPWithSHA-256AndMGF1Padding заключается в том, что он по умолчанию использует SHA-1 для заполнения MGF1. Javascript использует SHA-256, что приводит к несоответствию. Указав MGF1ParamterSpec, мы можем заставить Java использовать тот же алгоритм хэширования, что и Javascript по умолчанию.

В дополнение к ответу @Chip, который был действительно полезен, я хотел бы добавить следующий случай:

Предположим, вы хотите использовать следующее для расшифровки в Javascript (webcrypto):

window.crypto.subtle.decrypt(
        {
            name: "RSA-OAEP",
            hash: { name: "SHA-512" }
            //label: Uint8Array([...]) //optional
        },
        privateRsaKey, //CryptoKey object containing private RSA key
        encdata //ArrayBuffer containing to be decrypted data
    )
    .catch(function(err){
        console.error(err);
    })

Затем вам нужно использовать следующий OAEPParameterSpec для шифрования в Java (и, вероятно, наоборот, но я этого не пробовал):

OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1", 
        new MGF1ParameterSpec("SHA-512"), PSource.PSpecified.DEFAULT);

Поскольку @Chip ссылался только на заполнение MGF1 I, я предположил, что нужно будет использовать

OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1", 
        new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);

но, видимо, нужно изменить обе хэш-функции на SHA-512, как показано в моем первом блоке кода OAEPParameterSpec.

Другие вопросы по теме