Получение разных результатов шифрования — RSA — Java и Openssl

Когда я шифрую строку текста с помощью Java и Openssl, я не могу расшифровать шифр (используя Javascript Crypto Subtle), полученный от Openssl, однако шифр Java успешно расшифровывается (с помощью Crypto Subtle Web API JS). Не могли бы вы посмотреть и подсказать, где ошибка?

  1. Вот примеры открытых и закрытых ключей RSA в Base64, сгенерированных JS Crypto Sublte API:

Открытый ключ (RSA-OAEP, monulus 2048, хэш SHA-256):

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8JhmO71HZ1vb8uxKhw6MM+ZvtTmc9Tw6AFpJVgXKiTjebj1SPdnxhdhJ5Bj15RN0rGNACXhAyUdn7zsp66/tmjNuC91L+9PvQBjDbXLsx7XUV9nIGJ3aYO5/qruVNXwyemf7wlwZVDF7oeZ8JUfjABTg7a7xui2WdXDHgvhTQdvQ9TS0NkX9xWAiDSn/HWfoEBC7TLeKeVjHsT7g1JnskGxfVhFrLfxQCxYZle4ebXP7dCPsff4WNNCxzBQtHHt8iEoZ0SVKBzS5zhdLHIdAIW4ELdnYsM7iTlWZO+kfWnlV2i13lAJobhxOAqwsg4OqkTsrx0KtWfZH40bNtFzx8wIDAQAB

Закрытый ключ (те же параметры):

   MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwmGY7vUdnW9vy7EqHDowz5m+1OZz1PDoAWklWBcqJON5uPVI92fGF2EnkGPXlE3SsY0AJeEDJR2fvOynrr+2aM24L3Uv70+9AGMNtcuzHtdRX2cgYndpg7n+qu5U1fDJ6Z/vCXBlUMXuh5nwlR+MAFODtrvG6LZZ1cMeC+FNB29D1NLQ2Rf3FYCINKf8dZ+gQELtMt4p5WMexPuDUmeyQbF9WEWst/FALFhmV7h5tc/t0I+x9/hY00LHMFC0ce3yIShnRJUoHNLnOF0sch0AhbgQt2diwzuJOVZk76R9aeVXaLXeUAmhuHE4CrCyDg6qROyvHQq1Z9kfjRs20XPHzAgMBAAECggEAPtCjPGyeFFu00LclfB5tt20t9CQ/GP3o7MelxvpLF0mMNT74VmKs/rNqE037ARxzxUBCa1aEn6hvd9O8DadIgw5zaFCWMoDyQYtVlqE/NaMA9hDLf7XS2qTaGyLPgX/UFAZLAkMWb9ddfncVKYybtR0+Xn/i56dYVYAk7spTvmkai5Q4Li5eEJqrRNse5fg/wiwDda/cWlEvlvfziAjU51gS/Y9ItUT8Z1g8A2NYfqgFVgBVb0qrtbP76TWPbeLZr1FXTMDB0MO+zuC+1lq48+CYqSJqi9a4GlW2gi0wkaQe3VIOsHipv82kSh20LczXiSKqF58F8m4whWqpa02rrQKBgQD6tbuFiY8fVXi44m1mFO7KDIVXFjuMca0+qXisVaUhG4q42fnlHN0kyuH2IFXNxHD+gE4MnvAUkXDkG1znaz8vM2tfQn6vSpU7OytwOt6UEMWm+112083nGMUy1Me/6rAJbBWfTAl0KXY9/Y/SuEL8u+W6Eas+eASUIe1DooI4xwKBgQD1rAfUXjuRH6WFnbITdG8B3WVZhRCx3YuQVE1UnHR8N6qfx6qKIricvKMSvtWAj0UiIbSA8DENyfMiyAbGpZ0cILxtdXZOtZFW9hfiOeLptnYKTJXOiGoju1prxpUNUKu/1kd7e0clxN75mooge8yFFA6il43wYhhwO2si+FYJdQKBgQDL+1TpX3StX9NrSf8MkXd/uRQ8OQCWUl9MnoJqZPyHpWsG34Ms4IElUFTs9n4Zfv0YdLgMGLzpXzRkw8ahG2c7NjDkPqvoX1xv5sJ++8bg3YyTQe1XoxjiMAsyQmGLSp2T7PbitvDyLFHiOg3surL2AsL00y9rEidXhwsOfohJPQKBgQCqhw4sQHjShIgNlmfMj06amcZG/FGZVPISbiH7cFp++tjp7duX5QAGc/4x/dsPUOOpDIJR2egC7UJiyzvA2aaTprmEtTs46VmIZmwvsQSsO+X1wjFeWlxqjxr1orNFudBt6dxWfzzkn6Iy2i203Jobac+61r5EtKLIDMaSUJTQHQKBgQDaNTfgakpyZBSp/Ydzu76qDKpzGuDqrLBnR/lui4DrSyUoOjNwaGlkU/B6USdaC/BGO7lI08HZU7FzutvXb1mKS+nUI7rKvSTN9NKUJF4MaBv9q40RViKkF9vbq2f1wMs2KUNc0ex2ZL+5BOBICJNHyVulKSxY54Z55L/H4FZmWw==


  1. Вот код Java, который получает открытый ключ в виде строки Base64 и создает экземпляр PublicKey из ite. Затем я шифрую строку текста с помощью этого ключа и кодирую шифр в строку Base64.

    String stringToEncrypt = "This is encrypted message!!!";
    
    byte[] publicKeyBytes = Base64.getDecoder().decode("MIIBIjANBgkqhki....");
    
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
    
    OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new 
    MGF1ParameterSpec("SHA-256"),PSource.PSpecified.DEFAULT);
    
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
    PublicKey rsaPublicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
    
    cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey,oaepParams);
    
    String cipherBytes = 
    Base64.getEncoder().encodeToString(cipher.doFinal(stringToEncrypt.getBytes()));
    
    

    Затем строка cipherBytes Base64 успешно расшифровывается кодом JS Crypto Subtle ниже:

  2. Код генерации первой пары ключей:

const keyPair = await window.crypto.subtle.generateKey(
            {
                name: "RSA-OAEP",
                modulusLength: 2048,
                publicExponent: new Uint8Array([1, 0, 1]),
                hash: "SHA-256"
            },
            true,
            ["encrypt", "decrypt"]
        )

Импорт закрытого ключа =

    return window.crypto.subtle
          .importKey("pkcs8", base64StringToArrayBuffer(privateKeyInPem), {
              name: "RSA-OAEP",
              hash: "SHA-256",
          }, true, ["decrypt"]).then(key=>{
              return key;
          })

А потом расшифровать шифр

        const dec = new TextDecoder();

        function str2ab(str: any) {
            const buf = new ArrayBuffer(str.length);
            const bufView = new Uint8Array(buf);
            for (let i = 0, strLen = str.length; i < strLen; i++) {
                bufView[i] = str.charCodeAt(i);
            }

            return buf;
        }

        console.info('cipher: '+cipher)
        return window.crypto.subtle.decrypt({
                name: "RSA-OAEP",
            },
            privateKey,
            str2ab(window.atob(cipher))
        ).then(result=>{
            console.info('DECRYPTED MSG:'+dec.decode(result));
        })


Но проблема в том, что когда я пытаюсь расшифровать шифр из openssl - не получается. Вот команда Openssl, которую я использую:

OpenSSL> rsautl -encrypt -pubin -keyform DER -inkey publicKey.der -oaep -in input.txt -out out.bin

Я использую тот же открытый ключ, только что преобразованный из строки Base64 в формат DER (преобразованный строковый ключ Base64 в DER с помощью Java - 1. Декодировать строку base64 -> байт [] -> записать в файл каждый байт)

И, наконец, последняя операция Openssl для преобразования двоичных данных из out.bin в строку Base64.

OpenSSL> enc -A -base64 -in out.bin -out base64.txt

Поведение ключевого слова "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
0
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

rsautl неизменно использует SHA1 для дайджестов OAEP, см. здесь . Поскольку вы применяете SHA256 в коде WebCrypto, вы должны вместо этого использовать pkeyutl, что позволяет указывать дайджесты:

openssl pkeyutl -in plaintext.txt -out ciphertext.bin -encrypt -keyform DER -pubin -inkey spki.der -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 

Сгенерированный таким образом зашифрованный текст можно расшифровать с помощью кода WebCrypto:

(async () => {

// your private key
var privateKeyInPem = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDwmGY7vUdnW9vy7EqHDowz5m+1OZz1PDoAWklWBcqJON5uPVI92fGF2EnkGPXlE3SsY0AJeEDJR2fvOynrr+2aM24L3Uv70+9AGMNtcuzHtdRX2cgYndpg7n+qu5U1fDJ6Z/vCXBlUMXuh5nwlR+MAFODtrvG6LZZ1cMeC+FNB29D1NLQ2Rf3FYCINKf8dZ+gQELtMt4p5WMexPuDUmeyQbF9WEWst/FALFhmV7h5tc/t0I+x9/hY00LHMFC0ce3yIShnRJUoHNLnOF0sch0AhbgQt2diwzuJOVZk76R9aeVXaLXeUAmhuHE4CrCyDg6qROyvHQq1Z9kfjRs20XPHzAgMBAAECggEAPtCjPGyeFFu00LclfB5tt20t9CQ/GP3o7MelxvpLF0mMNT74VmKs/rNqE037ARxzxUBCa1aEn6hvd9O8DadIgw5zaFCWMoDyQYtVlqE/NaMA9hDLf7XS2qTaGyLPgX/UFAZLAkMWb9ddfncVKYybtR0+Xn/i56dYVYAk7spTvmkai5Q4Li5eEJqrRNse5fg/wiwDda/cWlEvlvfziAjU51gS/Y9ItUT8Z1g8A2NYfqgFVgBVb0qrtbP76TWPbeLZr1FXTMDB0MO+zuC+1lq48+CYqSJqi9a4GlW2gi0wkaQe3VIOsHipv82kSh20LczXiSKqF58F8m4whWqpa02rrQKBgQD6tbuFiY8fVXi44m1mFO7KDIVXFjuMca0+qXisVaUhG4q42fnlHN0kyuH2IFXNxHD+gE4MnvAUkXDkG1znaz8vM2tfQn6vSpU7OytwOt6UEMWm+112083nGMUy1Me/6rAJbBWfTAl0KXY9/Y/SuEL8u+W6Eas+eASUIe1DooI4xwKBgQD1rAfUXjuRH6WFnbITdG8B3WVZhRCx3YuQVE1UnHR8N6qfx6qKIricvKMSvtWAj0UiIbSA8DENyfMiyAbGpZ0cILxtdXZOtZFW9hfiOeLptnYKTJXOiGoju1prxpUNUKu/1kd7e0clxN75mooge8yFFA6il43wYhhwO2si+FYJdQKBgQDL+1TpX3StX9NrSf8MkXd/uRQ8OQCWUl9MnoJqZPyHpWsG34Ms4IElUFTs9n4Zfv0YdLgMGLzpXzRkw8ahG2c7NjDkPqvoX1xv5sJ++8bg3YyTQe1XoxjiMAsyQmGLSp2T7PbitvDyLFHiOg3surL2AsL00y9rEidXhwsOfohJPQKBgQCqhw4sQHjShIgNlmfMj06amcZG/FGZVPISbiH7cFp++tjp7duX5QAGc/4x/dsPUOOpDIJR2egC7UJiyzvA2aaTprmEtTs46VmIZmwvsQSsO+X1wjFeWlxqjxr1orNFudBt6dxWfzzkn6Iy2i203Jobac+61r5EtKLIDMaSUJTQHQKBgQDaNTfgakpyZBSp/Ydzu76qDKpzGuDqrLBnR/lui4DrSyUoOjNwaGlkU/B6USdaC/BGO7lI08HZU7FzutvXb1mKS+nUI7rKvSTN9NKUJF4MaBv9q40RViKkF9vbq2f1wMs2KUNc0ex2ZL+5BOBICJNHyVulKSxY54Z55L/H4FZmWw= = ";

var privateKey = await window.crypto.subtle.importKey("pkcs8", base64StringToArrayBuffer(privateKeyInPem), {name: "RSA-OAEP", hash: "SHA-256"}, true, ["decrypt"]);   
console.info(privateKey);

// Base64 encoded ciphertext from openssl pkeyutl
var cipher = "V2QWzwoOHPAlKhGqv0fDSv+lSPytBW4tTxVVJgneyvfIqTDvllhbZJzUAszdAC0IEow+YgbIWHyIBCw9YVS+EDZ3jbuIU97nx5NxAimiUFKvUmHE8p3oP6AP/etJhGQGC+fMiTbhmGn5FQhMnH/2lVei3yJypXWbgI6ONRmncYalq73q7VGelFUSubuPWQA3bKzuIOSorpQFy9sGIMDvW+YOMLrClVmUujVrEXrdsIbvzSb6hooKHbwjOaAmN4XRw0sr+YaF3n2PwazpLSvJmuugF26GxhmJAMmNViUvvsN+ycpJZdyRKNehQGqmahpC0XXihZ9dsHEH7vIDDmPAZQ= = ";
console.info('cipher: '+cipher)

// successfull decryption
var decrypted = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, str2ab(window.atob(cipher)));
const dec = new TextDecoder();
console.info('DECRYPTED MSG:' + dec.decode(decrypted));

})();


function base64StringToArrayBuffer(base64String){
    return Uint8Array.from(window.atob(base64String), c => c.charCodeAt(0));
}

function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

Для полноты: в контексте OAEP необходимо указать два дайджеста: дайджест контента и дайджест MGF1. pkeyutl позволяет установить оба независимо. Если оба дайджеста идентичны (что обычно и бывает), достаточно установить только дайджест контента с помощью rsa_oaep_md, т. е. дайджест MGF1 неявно имеет одно и то же значение, пока никакой другой дайджест не задан явно через rsa_mgf1_md.

Большое спасибо! Трюк с openssl pkeyutl удался. Интересно, что глядя на код Java, когда я создаю экземпляр PublicKey - PublicKey rsaPublicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)) - здесь ничего не упоминается о дайджесте контента и MGF1. Только при вызове этого метода - cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey,oaepParams) я использую параметры OAEP Sepc и мне показалось, что они нужны для операции шифрования, а не для генерации открытого ключа. Извините, может я что-то путаю, не экспорт в крипто.

Ilja Veselov 30.01.2023 22:10

...Я использую OAEP Parameters Sepc, и мне показалось, что они нужны для операции шифрования, а не для генерации открытого ключа... Так оно и есть. Точнее, OAEP — это вариант заполнения для RSA, используемый при шифровании/дешифровании, см. RFC8017, 7.1. РГАЭС-ОАЭП . Еще один — дополнение PKCS#1 v1.5, s. 7.2. RSAES-PKCS1-v1_5. Для самой генерации ключа заполнение не играет никакой роли.

Topaco 30.01.2023 23:45

Причина, по которой WebCrypto API указывает алгоритм (RSA), заполнение (OAEP) и параметры заполнения (SHA-256) в generateKey() при создании пары ключей, заключается в том, что WebCrypto API связывает предполагаемое назначение ключа с этим ключом. т.е. сгенерированный таким образом ключ RSA впоследствии можно будет использовать только для шифрования (открытый ключ) или дешифрования (закрытый ключ) с помощью RSA и OAEP с SHA-256. Идея: никогда не используйте один и тот же ключ для нескольких целей.

Topaco 30.01.2023 23:48

Спасибо за разъяснение. Я был введен в заблуждение тонким методом generateKey() Crypto, думая, что Oaep и SHA-256 являются параметрами для ключей.

Ilja Veselov 30.01.2023 23:53

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