Home > Software engineering >  Window Crypto Subtle : How we can use encrypt / decrypt method if we generate public private key wit
Window Crypto Subtle : How we can use encrypt / decrypt method if we generate public private key wit

Time:01-31

I want to use the same public-private key to do some encryption/decryption at the code level and want to send encrypted data to the backend along with my public key which I am appending inside of my JWT AUTH Token. So pls help me to encrypt / decrypt with that approach if possible, as I can't change this code due to reusability

      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'] 
      );
      

I tried that way but got an error.

Also, I want to use my exported public and private keys which I am doing with that approach

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.log(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'.

CodePudding user response:

RSASSA-PKCS1-v1_5 is a padding that is applied during signing/verification. It cannot be used for encryption/decryption. The padding for encryption/decryption is RSAES-PKCS1-v1_5, but this is not supported by WebCrypto API. WebCrypto only supports RSAES-OAEP for encryption/decryption. See RFC8017 and WebCrypto API for more details.
Also, the exported JWK keys must first be adapted for encryption/decryption. Then the keys must be imported before they can be used in encryption/decryption.

The following example demonstrates this: First, a key pair for signing/verifying with RSASSA-PKCS1-v1_5 is generated. Both keys are exported as JWK. Then the key_ops and alg parameters are adjusted. Afterwards, the modified keys are re-imported and used for encryption/decryption with 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.log(keyDetails)

// Export
const publicKey = await window.crypto.subtle.exportKey('jwk', keyDetails.publicKey);
const privateKey = await window.crypto.subtle.exportKey('jwk', keyDetails.privateKey);
console.log(publicKey)
console.log(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.log(publicKeyReloaded)
console.log(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.log(ab2b64(encryptedText));
const dec = new TextDecoder();
const decryptedText = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKeyReloaded, encryptedText)
console.log(dec.decode(decryptedText));

// Helper
function ab2b64(arrayBuffer) {
    return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}

})();

Note that in general the same key pair should actually be used for either signing/verifying or encrypting/decrypting, not for both together, see here.
WebCrypto API provides some protection against this misuse by binding the purpose of the key to it (however, this protection can be circumvented all too easily, as shown above).

CodePudding user response:

Well, the answer is already provided by @topaco. now I Just want to add one more approch here. If someone wants to Encrypt and Decrypt sensitive data with help of [JSON Web Encryption - Ciphertext] JOSE npm lib. with that public/ private key which is generated for sign/ verify only!

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.log('encodedText', encodedText);

 const decodedText =  await this.jose.jwe.decrypt(privateKey, encodedText)
        console.log('decodedText', decodedText);
}
  • Related