Web Crypto API: how to import RSA private key

1.2k views Asked by At

I have 2 different web software. At one of them I want to encrypt user's data in browser using public key from the server and store encrypted user's data at database. After that I want to have possibility to decrypt those data on server side or browser.

I tried to create proof of concept - It works with keys that were generated by Web Cypto API itself but fail with keys from another source.

How to reproduce:

I've create RSA keys using OpenSSL Ruby standard library

require 'openssl'

rsa = OpenSSL::PKey::RSA.new(2048)
puts rsa.to_s
puts rsa.public_key.to_s

I'm trying to use them to encrypt/decrypt messages via Web Crypto API. However I've encountered and error while importing private key. In DevTools I see it as Error without a backtrace.

Could someone advice How can I achieve by goal?

function arrayBufferToString(buf) {
  let bytes = new Uint8Array(buf);
  return bytes.reduce((str, byte) => str + String.fromCharCode(byte), "");
}

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

function base64ToBinary(str) {
  return atob(str);
}

function binaryToBase64(str) {
  return btoa(str);
}

(async function main() {
  console.clear();
  var content = "hello world!"; //+ Math.random();
  console.log("content: ", content);

  let algorithmOptions = {
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  };

  let rsaPrivate =
    "-----BEGIN RSA PRIVATE KEY-----\n" +
    "MIIEogIBAAKCAQEAqnXo4TWhiUaDpiQBArFq5B22JB2ebKlAvQMbDrOakK8Tactj\n" +
    "zg64ZTXwZhadVcfPwFvoS45O6LkIZL+o7F9N7WnlRiT5SXgwkPzQ75l2EcGqZKgK\n" +
    "lyCl5THuD2RRMOTJI1bzmx8DO/f7LMHr3xUGtdHXzqICxIUwa5B0RTvHG/j6Wirk\n" +
    "Z51ArwQa5ZrtyGXJoupgws0LZRuadJBkbIRUaaujv1pA25Dmzu7pODr3S2XWqIYy\n" +
    "cfJqxgIGvNJ7LHTEPLBWh1xCc0MNf3C9cZNEsVmDgGCYS4oeKuBbUoqF6eirQHC6\n" +
    "rkW+4KwE/Rvo6LZVrtl1vh6vI+rVt6wvoWydYQIDAQABAoIBAB+xZogg6ZTVaHrG\n" +
    "bO2sQPyCza+vVhpL6b56ylgUaqOF+a0M5NSWBhDDU5wXjk85pFXWgL0zi1ZXuMjK\n" +
    "ncS8/4cpzjgZfcP8NcNvTgWOWdZ5VI38dGOe7VlMzD9OXo4hq4gHjamEvZwzwh6T\n" +
    "O6CxjxrVFjPUCYGyZctKA2Qv9hgsB9Nq7EV5z/VErN33OI1edHeMIdZ0tvahQNrY\n" +
    "2It26hKsxSKAowm7H+NG0fJsVUmN0m1Ou0od9tqzX6XwOgycUvvdr5iHrECHFMDe\n" +
    "be5iZ19evzai8gdxr4ZkWdF5P7+VltI7vlKpHKUvx/I/mO/XWq4yo4efEoLYWjfe\n" +
    "Vz8OG7ECgYEA2yAesiDs3buYAQ8nEVje6sn/jr2PgtXXo6TXHbd3NNCnvbt71LWw\n" +
    "5P8dUqqiIwKskjargEU0HphQKmUubnSdYdl+rLpwcCsRn/0kBX1+89vv7XWnn20i\n" +
    "j07IUtsHev40xK3joJcJ1eHsHjkljJaMsRvGilo8V7eh5p8WkfDA4jUCgYEAxyVR\n" +
    "DQSc0U/O9++euv9LVUxqISaSG866/k9HP++IKhcN3Gh3gcJJcMETgtH0sEa5ut9K\n" +
    "AzC61OTZqpB+t1bVOpdzj2h4K0YVy2FVAB/wpZcoslrD4hC6Zh7tcUlfrdo3Pj+i\n" +
    "1cJzqaqZDVv0Rtxwz9ekWkBi9V+pPWohntZas/0CgYBRTS2WcdjwvDW9zt1z9kFf\n" +
    "Y+tKDtM8fBMySGr4P6YfFnvmTbW4SmGD1ZQPo/fcfZWB+n7PbN3VrDWyRTBhEyuB\n" +
    "rqztcY9eTtyPO+EtmE6ONEBlHo4+/MMh4N06wMGZxM/XWZ1nbCLeFKEC5bkk3Ib+\n" +
    "/4s+shRJh3yukMBTDbzDMQKBgH7Rtc7LfC4TW+MqdnPxNgEo+4EG9g69VPm0dNQz\n" +
    "bwwWyF3vLQO2PVyPqCQsHl3PfGGT5qcndiMzZaYoBHou0vVQE2hlB/nO1PxCjIXa\n" +
    "0T4yh9kk0g95xapY0a7OIh8tkvaSQdlMzqlimbsXLvWdVj4VvnU3AY3vEHCq0KQ9\n" +
    "L1/lAoGAbRAVMic9JDVqjpxfjAeL1E9w30m59vDCIEjJdMGX5BHOea7iVbQshilb\n" +
    "yo+1s+QfWVNT0jOTzo4VPRomRNbdEIMb5W/M3zv+cUAmnCJymHUBiBP0N+ZO8+g8\n" +
    "lEB3MJBdfsiKxMiX69jcoU1fB8tAwwE3hWSmkNKuvvzjOgxhtgQ=\n" +
    "-----END RSA PRIVATE KEY-----";
  let rsaPrivateBase64String = rsaPrivate.split("\n").slice(1, -1).join("");
  // console.log("rsaPrivateString", rsaPrivateBase64String);

  let rsaPublic =
    "-----BEGIN PUBLIC KEY-----\n" +
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqnXo4TWhiUaDpiQBArFq\n" +
    "5B22JB2ebKlAvQMbDrOakK8Tactjzg64ZTXwZhadVcfPwFvoS45O6LkIZL+o7F9N\n" +
    "7WnlRiT5SXgwkPzQ75l2EcGqZKgKlyCl5THuD2RRMOTJI1bzmx8DO/f7LMHr3xUG\n" +
    "tdHXzqICxIUwa5B0RTvHG/j6WirkZ51ArwQa5ZrtyGXJoupgws0LZRuadJBkbIRU\n" +
    "aaujv1pA25Dmzu7pODr3S2XWqIYycfJqxgIGvNJ7LHTEPLBWh1xCc0MNf3C9cZNE\n" +
    "sVmDgGCYS4oeKuBbUoqF6eirQHC6rkW+4KwE/Rvo6LZVrtl1vh6vI+rVt6wvoWyd\n" +
    "YQIDAQAB\n" +
    "-----END PUBLIC KEY-----";
  let rsaPublicBase64String = rsaPublic.split("\n").slice(1, -1).join("");
  // console.log("rsaPublicBase64String", rsaPublicBase64String);

  let pubKey = await crypto.subtle.importKey(
    "spki",
    stringToArrayBuffer(base64ToBinary(rsaPublicBase64String)),
    algorithmOptions,
    false, ["encrypt"]
  );
  console.log("pubKey", pubKey);

  // >> ERROR IS ON STATEMENT BELOW
  let privKey = await crypto.subtle.importKey(
    "pkcs8",
    stringToArrayBuffer(base64ToBinary(rsaPrivateBase64String)),
    algorithmOptions,
    false, ["decrypt"]
  );
  console.log("privKey", privKey);

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

  let decryptedBuf = await crypto.subtle.decrypt({
      name: "RSA-OAEP"
    },
    privKey,
    stringToArrayBuffer(encrypted)
  );
  let decrypted = arrayBufferToString(decryptedBuf);
  console.log("decrypted", decrypted);
})().catch(error => console.error(error));

Here's code sandbox link

0

There are 0 answers