Реализация шифрования/дешифрования в Typescript с использованием Web Crypto API

У меня есть требование безопасности не отправлять данные на сервер в виде обычного текста, и учетные данные не должны быть видны на вкладке сети браузера. Я мог бы использовать хеширование, но загвоздка в том, что у нас уже есть старые сохраненные пароли с применением хеширования и соли. Теперь, чтобы не беспокоить старую базу данных, я подумал просто зашифровать данные и расшифровать их на стороне сервера (узла).

Впредь я пытаюсь использовать Web Crypto API для достижения желаемого результата. Ниже приведен простой сценарий, который я пытался написать перед экспортом логики как в клиентскую, так и в серверную среду:

function base64ToArrayBuffer(base64): ArrayBuffer {
    return Uint8Array.from(atob(base64), c => c.charCodeAt(0))
}

function ArrayBufferToBase64(buffer): string {
    return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}

async function encryptString(privateKey: string, publicKey: string, data: string) {

    let params: AesGcmParams = {
        name: 'AES-GCM',
        iv: base64ToArrayBuffer(publicKey)
    }

    let keyImported: CryptoKey = await window.crypto.subtle.importKey("raw", base64ToArrayBuffer(privateKey), "AES-GCM", true, ["encrypt", "decrypt"]);
    let dataInstance: ArrayBuffer = new TextEncoder().encode(data);
    return Promise.resolve(await window.crypto.subtle.encrypt(params, keyImported, dataInstance));
}

async function decryptString(privateKey: string, publicKey: string, data: string) {

    let params: AesGcmParams = {
        name: 'AES-GCM',
        iv: base64ToArrayBuffer(publicKey)
    }

    let keyImported: CryptoKey = await window.crypto.subtle.importKey("raw", base64ToArrayBuffer(privateKey), "AES-GCM", true, ["encrypt", "decrypt"]);
    let dataInstance: ArrayBuffer = new TextEncoder().encode(data);
    return Promise.resolve(await window.crypto.subtle.decrypt(params, keyImported, dataInstance));
}

// Key for encryption/decryption process
let PRIVATE_KEY: string = 'Fdz2z54vAwIjq0tginHyzgyL62q+f4sBlMi5IOOXJ7c=';
let PUBLIC_KEY: string = btoa(String.fromCharCode(...window.crypto.getRandomValues(new Uint8Array(12))));

// Password to be encrypted before being sent to server
let password: string = "Test@123";

(async () => {

    let encrypted_data: string = ArrayBufferToBase64(await encryptString(PRIVATE_KEY, PUBLIC_KEY, password));
    console.info(encrypted_data);

    let decrypted_data: string = ArrayBufferToBase64(await decryptString(PRIVATE_KEY, PUBLIC_KEY, encrypted_data));
    console.info(decrypted_data);

})();


Я могу зашифровать строку, хранящуюся в переменной password, но не могу ее расшифровать.
Получаю следующее сообщение об ошибке:

Promise {<pending>}
VM98:33 FSWTBSgdQNVgYOelgRpU5JZm/4G0TRynIyr2jg==
VM98:36 Uncaught (in promise) Error
await (async)
(anonymous) @ VM98:36

Также прикрепляю скриншот с деталями, не вставленными в журналы:

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

Если кто-то знает о библиотеке, которую я могу легко использовать как на стороне клиента, так и на стороне сервера, это также будет оценено как ответ.

Все эти return Promise.resolve(await ... неверны, он должен просто возвращать криптовызовы. Хотя все это всего лишь театр безопасности, если у вас есть шифрование сообщений на стороне клиента, любой может легко их расшифровать.

Guy Incognito 07.06.2024 08:07

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

Jaromanda X 07.06.2024 08:11

вы, наверное, хотите window.crypto.subtle.decrypt(params, keyImported, base64ToArrayBuffer(data) - и let dataInstance: ArrayBuffer = new TextEncoder().encode(data); в decryptString вам тоже не нужен

Jaromanda X 07.06.2024 08:13

Решение следует опубликовать в отдельном ответе. Это парадигма SO.

Topaco 07.06.2024 08:53
return Promise.resolve(await p) === return Promise.resolve(p) === return p обычно вы этого хотите, await если у вас есть обещание и вам нужна ценность. Но вы хотите использовать Promise.resolve(), чтобы убедиться, что у вас есть обещание. Использование обоих — нонсенс: вы ждете, пока обещание преобразуется в значение, чтобы обернуть его обратно в обещание. Асинхронные функции в любом случае уже возвращают обещание, поэтому они в любом случае сделают за вас обертку обещаний.
VLAZ 07.06.2024 12:57
Поведение ключевого слова "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
5
156
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Ниже приведен сценарий, который сработал у меня, согласно информации, предоставленной сообществом:



    function base64ToArrayBuffer(base64: string): ArrayBuffer {
        return Uint8Array.from(atob(base64), c => c.charCodeAt(0))
    }
    
    function ArrayBufferToBase64(buffer: ArrayBuffer): string {
        return btoa(String.fromCharCode(...new Uint8Array(buffer)));
    }
    
    async function encryptString(privateKey: string, publicKey: string, data: string) {
    
        let params: AesGcmParams = {
            name: 'AES-GCM',
            iv: base64ToArrayBuffer(publicKey)
        }
    
        let keyImported: CryptoKey = await window.crypto.subtle.importKey("raw", base64ToArrayBuffer(privateKey), "AES-GCM", true, ["encrypt", "decrypt"]);
        let dataInstance: ArrayBuffer = new TextEncoder().encode(data);
        return window.crypto.subtle.encrypt(params, keyImported, dataInstance);
    }
    
    async function decryptString(privateKey: string, publicKey: string, data: string) {
    
        let params: AesGcmParams = {
            name: 'AES-GCM',
            iv: base64ToArrayBuffer(publicKey)
        }
    
        let keyImported: CryptoKey = await window.crypto.subtle.importKey("raw", base64ToArrayBuffer(privateKey), "AES-GCM", true, ["encrypt", "decrypt"]);
        return window.crypto.subtle.decrypt(params, keyImported, base64ToArrayBuffer(data));
    }
    
    // Key for encryption/decryption process
    let PRIVATE_KEY: string = 'Fdz2z54vAwIjq0tginHyzgyL62q+f4sBlMi5IOOXJ7c=';
    let PUBLIC_KEY: string = btoa(String.fromCharCode(...window.crypto.getRandomValues(new Uint8Array(12))));
    
    // Password to be encrypted before being sent to server
    let password: string = "Test@123";
    
    (async () => {
    
        let encrypted_data: string = ArrayBufferToBase64(await encryptString(PRIVATE_KEY, PUBLIC_KEY, password));
        console.info(encrypted_data);
    
        let decrypted_data: any = new TextDecoder().decode(await decryptString(PRIVATE_KEY, PUBLIC_KEY, encrypted_data));
        console.info(decrypted_data);
    
    })();

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