Skip to content

Instantly share code, notes, and snippets.

@QingpingMeng
Last active February 26, 2024 06:13
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save QingpingMeng/f51902e2629fc061c6b9fc9bb0f3f57b to your computer and use it in GitHub Desktop.
Save QingpingMeng/f51902e2629fc061c6b9fc9bb0f3f57b to your computer and use it in GitHub Desktop.
Encrypt data using JavaScript in browser with RSA public key generated in C# without library

This is an example to demo how you generate the RSA key pair from server side(.NetCore 3.1) and pass the public key to the client(Browser) for encrypting the data that <= 245 bytes.

RSA-2048 can only support to encrypt up to 245 bytes data.

Generate RSA key pair in C# (.Net Core 3.1):

using var rsaProvider = new RSACng();
// spki is used for browser side encryption
var spki = Convert.ToBase64String(rsaProvider.ExportSubjectPublicKeyInfo());
var encodedPrivateKey = Convert.ToBase64String(rsaProvider.ExportPkcs8PrivateKey());

Encrypt data using browser SubtleCrypto API:

function base64ToArrayBuffer(base64) {
  var binary_string = window.atob(base64);
  var len = binary_string.length;
  var bytes = new Uint8Array(len);
  for (var i = 0; i < len; i++) {
    bytes[i] = binary_string.charCodeAt(i);
  }
  return bytes.buffer;
}

function arrayBufferToBase64(buffer) {
  var binary = '';
  var bytes = new Uint8Array(buffer);
  var len = bytes.byteLength;
  for (var i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
}

async function importPublicKey(spki) {
  const binaryDer = base64ToArrayBuffer(spki);
  var cryptoKey = await window.crypto.subtle
    .importKey(
      "spki",
      binaryDer, {
        name: 'RSA-OAEP',
        modulusLength: 256,
        hash: {
          name: 'sha-256'
        }
      },
      false,
      ["encrypt"]
    );
  return cryptoKey;
}

async function encryptData(message, publicKey) {
  let enc = new TextEncoder();
  let encodedMessage = enc.encode(message);
  var encryptedData = await window.crypto.subtle.encrypt({
      name: "RSA-OAEP"
    },
    publicKey,
    encodedMessage
  );
  var encodedData = arrayBufferToBase64(encryptedData);
  return encodedData;
}

const start = async function() {
  // this is spki from C# code.
  var spki = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyu9pSV8qF/VUG0MP2ekdlftLS5Ad/wwrF0WCKIfIrSYO6T4vblyuGweQbXKscEuvS2Z8wP4TFU3JSPhRaq7RnFWs0c/UBaQrKSeYEH9iXjJlyGT4N9Shz4lAMLHiMTfxG5fDe\u002B/jUbbEEMXAdcE13lVqUtNLNnEVNq3M1nD5a5MlqCDkdphAaVPlCYcwPZ4v2BEa4ITdejMdJKRiT5jdhFYKUYp0ELtTKm1vU6vSAn6sJvkWJGW5vufOT\u002BJPgxI7o\u002BZfIXP6ln7TSB71KjeY4FrFctPCI/L43wla0LAyjlfgSTQNf9E0XxZnaVCcQe741jhDYS27jHvfAoo/SNLk6QIDAQAB";
  var key = await importPublicKey(spki);
  var encryptedData = await encryptData("hello world", key);
  console.log(encryptedData)
}

start();

Decrypt the data in C# that encrypted in browser:

    using var importedRsa = new RSACng();
    var span = (ReadOnlySpan<byte>)Convert.FromBase64String("<EncodedExportedPkcs8PrivateKey>");
    importedRsa.ImportPkcs8PrivateKey(span, out int _);
    var data = Console.ReadLine();
    var bytes = Convert.FromBase64String(data);
    var decryptedBytes = importedRsa.Decrypt(bytes, RSAEncryptionPadding.OaepSHA256);
    var decryptedData = Encoding.UTF8.GetString(decryptedBytes);
@duongphuhiep
Copy link

Thank you, this gist helped me! Here is a more up to date codes (.NET 6)

https://github.com/duongphuhiep/AsymmetricEncryptionWebDemo

@QingpingMeng
Copy link
Author

Thank you, this gist helped me! Here is a more up to date codes (.NET 6)

https://github.com/duongphuhiep/AsymmetricEncryptionWebDemo

Glad this helps. Thanks for the updated codes!

@Zubaloo
Copy link

Zubaloo commented Feb 16, 2024

There are some typos in the code, which make it fail to run:

@@ -53,1 +53,1 @@
-async function encyptData(message, cryptoKey){
+async function encryptData(message, publicKey){
@@ -81,1 +81,1 @@
-    var decryptedData = Encoding.UTF8.GetString(decryptedBytes));
+    var decryptedData = Encoding.UTF8.GetString(decryptedBytes);

@QingpingMeng
Copy link
Author

@Zubaloo thanks for pointing out, fixed!

@Zubaloo
Copy link

Zubaloo commented Feb 26, 2024

Not really fixed - you'd better run your code instead of assuming it works :)

The encryptData function defines the cryptoKey argument, but an undefined publicKey is used in its body instead - this fix was in the diff I provided.

@QingpingMeng
Copy link
Author

You are right. Truely fixed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment