Расшифровка JavaScript Web Crypto API (RSA-OAEP) в Java

Я пытаюсь зашифровать строку с помощью RSA-OAEP-SHA-512 в API WebCrypto (на стороне клиента), а затем хочу снова расшифровать строку с помощью Java (на стороне сервера). Однако расшифровка на стороне сервера завершается с ошибкой «Ошибка: ошибка заполнения» при расшифровке.

Я генерирую ключи RSA в JavaScript, используя:

async function generateRSAKeyPair() {
    const rsaKeyPair = await window.crypto.subtle.generateKey(
      {
        name: 'RSA-OAEP',
        modulusLength: 4096,
        publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
        hash: { name: 'SHA-512' },
      },
      true,
      ['encrypt', 'decrypt']
    );
    return rsaKeyPair;
}

Я шифрую строку JSON, используя:

sync function encryptWithRSA(jsonString, jsonRSAPublicKey) {
    // Parse JSON keys
    const rsaPublicKey = JSON.parse(jsonRSAPublicKey);

    // Import RSA public key
    const importedRSAPublicKey = await window.crypto.subtle.importKey(
      'jwk',
      rsaPublicKey,
      { name: 'RSA-OAEP', hash: { name: 'SHA-512' } },
      true,
      ['encrypt']
    );

    // Convert JSON String to ArrayBuffer
    const aesKeyBuffer = new TextEncoder().encode(jsonString);

    // Encrypt AES key using RSA public key
    const encryptedAesKeyBuffer = await window.crypto.subtle.encrypt(
      { name: 'RSA-OAEP' },
      importedRSAPublicKey,
      aesKeyBuffer
    );

    // Convert encrypted AES key to base64
      const encryptedAesKeyBase64 = window.btoa(String.fromCharCode(...new Uint8Array(encryptedAesKeyBuffer)));
    return encryptedAesKeyBase64;
}

Я пытаюсь расшифровать его на Java, используя:

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

public void decrypt() {
    try {
        PrivateKey privateKey = loadPrivateKey();

        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-512AndMGF1Padding");

        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

        this.clearText = new String(decryptedBytes);
        System.out.println("Decrypted clear text: " + clearText);
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println("Decryption failed: " + e.getMessage());
    }
}

    private PrivateKey loadPrivateKey() throws Exception {
        // Parse the JWK JSON string
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> jwk = mapper.readValue(rsaKey, Map.class);
        System.out.println("JWK parsed: " + jwk);

        // Extract the key components from the JWK
        String nBase64 = (String) jwk.get("n");
        String dBase64 = (String) jwk.get("d");
        String pBase64 = (String) jwk.get("p");
        String qBase64 = (String) jwk.get("q");
        String dpBase64 = (String) jwk.get("dp");
        String dqBase64 = (String) jwk.get("dq");
        String qiBase64 = (String) jwk.get("qi");
        String eBase64 = (String) jwk.get("e");

        // Decode the Base64 URL-encoded components
        BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(nBase64));
        BigInteger privateExponent = new BigInteger(1, Base64.getUrlDecoder().decode(dBase64));
        BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(eBase64));
        BigInteger primeP = new BigInteger(1, Base64.getUrlDecoder().decode(pBase64));
        BigInteger primeQ = new BigInteger(1, Base64.getUrlDecoder().decode(qBase64));
        BigInteger primeExponentP = new BigInteger(1, Base64.getUrlDecoder().decode(dpBase64));
        BigInteger primeExponentQ = new BigInteger(1, Base64.getUrlDecoder().decode(dqBase64));
        BigInteger crtCoefficient = new BigInteger(1, Base64.getUrlDecoder().decode(qiBase64));

        // Create the RSA private key specification
        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);

        // Generate the private key
        KeyFactory keyFactory = KeyFactory.getInstance(AppConfig.CRYPTO_RSA);
        return keyFactory.generatePrivate(keySpec);
    }


Это дает следующий результат:

javax.crypto.BadPaddingException: Padding error in decryption
    at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:383)
    at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:419)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2244)
    at zkgitclientcli.crypto.RsaEncryptionHandler2.decrypt(RsaEncryptionHandler2.java:48)
    at zkgitclientcli.commands.login.DecryptAesKey.execute(DecryptAesKey.java:23)
    at zkgitclientcli.commands.CommandManager.executeCommand(CommandManager.java:44)
    at zkgitclientcli.ZkGit.main(ZkGit.java:12)
    at org.codehaus.mojo.exec.ExecJavaMojo.doMain(ExecJavaMojo.java:385)
    at org.codehaus.mojo.exec.ExecJavaMojo.doExec(ExecJavaMojo.java:374)
    at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0(ExecJavaMojo.java:296)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Decryption failed: Padding error in decryption

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

Кроме того, я могу написать метод JavaScript, который успешно расшифровывает зашифрованную строку, поэтому зашифрованную строку в кодировке Base64 можно расшифровать, но не в Java...

Я пробовал следующие настройки, но безуспешно: SHA-1: RSA/ECB/OAEPWithSHA-1AndMGF1Padding SHA-224: RSA/ECB/OAEPWithSHA-224AndMGF1Padding SHA-256: RSA/ECB/OAEPWithSHA-256AndMGF1Padding SHA-384: RSA/ECB/OAEPWithSHA-384AndMGF1Padding

Вероятно, проблема связана с разными дайджестами MGF1: если RSA/ECB/OAEPWithSHA-512AndMGF1Padding провайдер SunJCE указывает дайджест OAEP как SHA-512, дайджест MGF1 по умолчанию имеет значение SHA1. Напротив, WebCrypto использует один и тот же дайджест для OAEP и MGF1, то есть SHA-512. Попробуйте явно указать оба дайджеста на стороне Java, используя OAEPParameterSpec (как SHA-512).

Topaco 30.06.2024 22:30

Работает как шарм, спасибо!

Roggab32 30.06.2024 22:40
Поведение ключевого слова "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) для оценки ваших знаний,...
0
2
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вот рабочий код, основанный на приведенном выше комментарии:

public void decrypt() {
    try {
       PrivateKey privateKey = loadPrivateKey();

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

  Cipher cipher = Cipher.getInstance(AppConfig.CRYPTO_RSA_OAEP);
         cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
         byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
       byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

       clearText = new String(decryptedBytes);

     } catch (Exception e) {
       clearText = e.getMessage();
     }
}

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

Похожие вопросы

Можно ли добавить прослушиватель событий в пользовательскую строку заголовка, используемую для перетаскивания окна в безрамочном окне?
Реакция: «.map не является функцией»
Обновление документа Firestore несколько раз с помощью функции триггера события перезаписывает или прекращает обновление после нескольких триггеров
Не отмечено runtime.lastError: порт сообщения закрыт до получения ответа. Расширение Chrome
Window.opener и window.parent не работают во всплывающем дочернем окне
Получите значение null в контроллере при реализации IEnumerable в модели ASP NET
Как программно проверить, отключен ли на веб-сайте щелчок правой кнопкой мыши?
Vue 3 API композиции, не может вернуть значение из вспомогательной функции в компонент
422 Ошибка необрабатываемого объекта при отправке данных формы на серверную часть FastAPI с использованием ReactJs и Fetch API во внешнем интерфейсе
Получить заголовок из JSON