Я хочу использовать один и тот же открытый и закрытый ключ для некоторого шифрования/дешифрования на уровне кода и хочу отправить зашифрованные данные в серверную часть вместе с моим открытым ключом, который я добавляю в свой токен JWT AUTH. Поэтому, пожалуйста, помогите мне зашифровать / расшифровать этот подход, если это возможно, так как я не могу изменить этот код из-за возможности повторного использования.
const keyDetails = await window.crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: { name: 'SHA-256' },
},
true,
['verify', 'sign']
);
Я пробовал так, но получил ошибку.
Кроме того, я хочу использовать свои экспортированные открытые и закрытые ключи, которые я делаю с таким подходом.
const publicKey: any = await window.crypto.subtle.exportKey('jwk', keyDetails.publicKey);
const privateKey: any = await window.crypto.subtle.exportKey('jwk', keyDetails.privateKey);
const enc = new TextEncoder();
const encodedText = enc.encode("testing 1234");
const encryptedText = await window.crypto.subtle.encrypt({
name: "RSASSA-PKCS1-v1_5"
},
publicKey,
encodedText
)
console.info(encryptedText);
const decryptedText = await window.crypto.subtle.decrypt({
name: "RSASSA-PKCS1-v1_5"
},
privateKey,
encryptedText
)
TypeError: Failed to execute 'encrypt' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'.
RSASSA-PKCS1-v1_5 — это заполнение, которое применяется во время подписания/проверки. Его нельзя использовать для шифрования/дешифрования. Заполнение для шифрования/дешифрования — RSAES-PKCS1-v1_5, но оно не поддерживается WebCrypto API. WebCrypto поддерживает только RSAES-OAEP для шифрования/дешифрования. См. RFC8017 и WebCrypto API для более подробной информации.
Кроме того, экспортированные ключи JWK должны быть предварительно адаптированы для шифрования/дешифрования. Затем ключи должны быть импортированы, прежде чем их можно будет использовать в шифровании/дешифровании.
Следующий пример демонстрирует это: Сначала создается пара ключей для подписи/проверки с помощью RSASSA-PKCS1-v1_5. Оба ключа экспортируются как JWK. Затем настраиваются параметры key_ops
и alg
. После этого измененные ключи повторно импортируются и используются для шифрования/дешифрования с помощью RSAES-OAEP:
(async () => {
// Generate
const keyDetails = await window.crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: { name: 'SHA-256' },
},
true,
['verify', 'sign']
);
console.info(keyDetails)
// Export
const publicKey = await window.crypto.subtle.exportKey('jwk', keyDetails.publicKey);
const privateKey = await window.crypto.subtle.exportKey('jwk', keyDetails.privateKey);
console.info(publicKey)
console.info(privateKey)
// Adapt parameters and import
publicKey.key_ops = ['encrypt'];
privateKey.key_ops = ['decrypt'];
publicKey.alg = 'RSA-OAEP-256';
privateKey.alg = 'RSA-OAEP-256';
const publicKeyReloaded = await window.crypto.subtle.importKey("jwk", publicKey, {name: "RSA-OAEP", hash: {name: "SHA-256"}}, true, ["encrypt"]);
const privateKeyReloaded = await window.crypto.subtle.importKey("jwk", privateKey,{name: "RSA-OAEP", hash: {name: "SHA-256"}}, true, ["decrypt"]);
console.info(publicKeyReloaded)
console.info(privateKeyReloaded)
// Encrypt/Decrypt
const enc = new TextEncoder();
const encodedText = enc.encode("testing 1234");
const encryptedText = await window.crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKeyReloaded, encodedText)
console.info(ab2b64(encryptedText));
const dec = new TextDecoder();
const decryptedText = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKeyReloaded, encryptedText)
console.info(dec.decode(decryptedText));
// Helper
function ab2b64(arrayBuffer) {
return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}
})();
Обратите внимание, что, как правило, одна и та же пара ключей должна фактически использоваться либо для подписи/проверки, либо для шифрования/дешифрования, а не для того и другого вместе, см. здесь.
WebCrypto API обеспечивает некоторую защиту от такого неправильного использования, привязывая к нему назначение ключа (однако эту защиту можно легко обойти, как показано выше).
Что ж, ответ уже дал @topaco. теперь я просто хочу добавить еще один подход. Если кто-то хочет зашифровать и расшифровать конфиденциальные данные с помощью [JSON Web Encryption — Ciphertext] JOSE npm lib. с тем открытым/закрытым ключом, который генерируется только для подписи/проверки!
const jose = require('jose'); // npm i jose
async encryptDecryptLogic(data: string): Promise<any>{
const keyDetails = await window.crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: { name: 'SHA-256' },
},
true,
['verify', 'sign']
);
// updating operation from sign-varify to encrypt-decrypt.
// As that private/ public key is generated for sign and verification purposes only but here we extended its purpose. So we need to update a few properties to do encryption/decryption
publicKey.key_ops = ['encrypt'];
privateKey.key_ops = ['decrypt'];
// updating algo from sign-varify[RS256] to encrypt-decrypt[RSA-OAEP]
// Defines the algorithm used to encrypt the Content Encryption Key (CEK). This MUST be set to “RSA-OAEP”.
publicKey.alg = 'RSA-OAEP';
privateKey.alg = 'RSA-OAEP';
const encodedText = await this.jose.jwe.encrypt(publicKey, "lets encrypt me!!")
console.info('encodedText', encodedText);
const decodedText = await this.jose.jwe.decrypt(privateKey, encodedText)
console.info('decodedText', decodedText);
}
Спасибо @topaco за то, что поделились своим подробным решением с ценной статьей. Теперь у меня есть лучшее понимание.