Web Crypto API: how to encrypt data with imported RSA public key and decrypt data it in Ruby

2.4k views Asked by At

I need to encrypt data on browser side and decrypt it in Rails application using RSA.

Currently I'm using JSEncrypt library on JS side but I want to replace it with builtin Web Crypto API.

I need to use existing RSA public key for encryption, which was generated by ruby OpenSSL standard library for backward compatibility with that already encrypted and saved.

I've managed to import RSA pubkey to JS as JWK or SPKI and encrypt data, but Ruby side failed to decrypt it.

JWK import and encryption:

  let pubKey = await crypto.subtle.importKey(
    "jwk",
    {
      kid: "1",
      kty: "RSA",
      use: "enc",
      key_ops: ["encrypt"],
      alg: "RSA-OAEP-256",
      e: pubKeyE,
      n: pubKeyN
    },
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: "SHA-256" }
    },
    false,
    ["encrypt"]
  );
  console.log("pubKey imported");

  let encryptedBuf = await crypto.subtle.encrypt(
    {
      name: "RSA-OAEP"
    },
    pubKey,
    stringToArrayBuffer(content)
  );
  let encrypted = arrayBufferToString(encryptedBuf);

SPKI import and encryption:

  let pubKey = await crypto.subtle.importKey(
    "spki",
    stringToArrayBuffer(atob(pubKeyBase64)),
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: "SHA-256" }
    },
    false,
    ["encrypt"]
  );
  console.log("pubKey imported");

  let encryptedBuf = await crypto.subtle.encrypt(
    {
      name: "RSA-OAEP"
    },
    pubKey,
    stringToArrayBuffer(content)
  );
  let encrypted = arrayBufferToString(encryptedBuf);

Ruby public key generation and decryption:

rsa = OpenSSL::PKey::RSA.new(pem_private_key)
js_encrypted = Base64.decode64(js_encrypted_base64)
js_decrypted = rsa.private_decrypt(js_encrypted, OpenSSL::PKey::RSA::NO_PADDING)

see complete reproducable examples here:

https://repl.it/@senid231/Web-Crypto-API-encrypt-with-imported-rsa-pubkey-as-JWK#script.js

https://repl.it/@senid231/Web-Crypto-API-encrypt-with-imported-rsa-pubkey-as-SPKI#script.js

https://repl.it/@senid231/Ruby-RSA-decrypt-data-encrypted-by-JS#main.rb

1

There are 1 answers

0
Senid On

Thanks to Topaco comment, I've managed find out how to decrypt message on ruby side.

ruby OpenSSL standard library does not implement modern RSA-OAEP, but there is a gem, that do can add this functionality.

Message was encrypted by WEb Crypto API with public key imported in SPKI format.

let pubKey = await crypto.subtle.importKey(
    "spki",
    stringToArrayBuffer(atob(pubKeyBase64)),
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: "SHA-256" }
    },
    false,
    ["encrypt"]
  );
  console.log("pubKey imported");

  let encryptedBuf = await crypto.subtle.encrypt(
    {
      name: "RSA-OAEP"
    },
    pubKey,
    stringToArrayBuffer(content)
  );
  let encrypted = arrayBufferToString(encryptedBuf);

https://github.com/terashi58/openssl-oaep

$ gem install openssl-oaep
require "openssl"
require "openssl/oaep"
require "base64"

rsa = OpenSSL::PKey::RSA.new(pem_private_key)
js_encrypted = Base64.decode64(js_encrypted_base64)
js_decrypted = rsa.private_decrypt_oaep(js_encrypted, '', OpenSSL::Digest::SHA256)