Pass information from JSInterop as base64 strings instead of byte arrays to bypass .NET issue (#1307)

This commit is contained in:
Leendert de Borst
2025-10-27 21:47:19 +01:00
committed by Leendert de Borst
parent 3bead0bbfc
commit 975ae9bd74
3 changed files with 29 additions and 28 deletions

View File

@@ -80,12 +80,12 @@ public sealed class EmailService(DbService dbService, JsInteropService jsInterop
try
{
var decryptedSymmetricKey = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
email.Subject = await jsInteropService.SymmetricDecrypt(email.Subject, Convert.ToBase64String(decryptedSymmetricKey));
email.FromDisplay = await jsInteropService.SymmetricDecrypt(email.FromDisplay, Convert.ToBase64String(decryptedSymmetricKey));
email.FromLocal = await jsInteropService.SymmetricDecrypt(email.FromLocal, Convert.ToBase64String(decryptedSymmetricKey));
email.FromDomain = await jsInteropService.SymmetricDecrypt(email.FromDomain, Convert.ToBase64String(decryptedSymmetricKey));
email.MessagePreview = await jsInteropService.SymmetricDecrypt(email.MessagePreview, Convert.ToBase64String(decryptedSymmetricKey));
var decryptedSymmetricKeyBase64 = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
email.Subject = await jsInteropService.SymmetricDecrypt(email.Subject, decryptedSymmetricKeyBase64);
email.FromDisplay = await jsInteropService.SymmetricDecrypt(email.FromDisplay, decryptedSymmetricKeyBase64);
email.FromLocal = await jsInteropService.SymmetricDecrypt(email.FromLocal, decryptedSymmetricKeyBase64);
email.FromDomain = await jsInteropService.SymmetricDecrypt(email.FromDomain, decryptedSymmetricKeyBase64);
email.MessagePreview = await jsInteropService.SymmetricDecrypt(email.MessagePreview, decryptedSymmetricKeyBase64);
}
catch (Exception ex)
{
@@ -110,8 +110,8 @@ public sealed class EmailService(DbService dbService, JsInteropService jsInterop
try
{
var decryptedSymmetricKey = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
var decryptedBase64 = await jsInteropService.SymmetricDecryptBytes(encryptedBytes, Convert.ToBase64String(decryptedSymmetricKey));
var decryptedSymmetricKeyBase64 = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
var decryptedBase64 = await jsInteropService.SymmetricDecryptBytes(encryptedBytes, decryptedSymmetricKeyBase64);
return decryptedBase64;
}
catch (Exception ex)
@@ -133,21 +133,21 @@ public sealed class EmailService(DbService dbService, JsInteropService jsInterop
try
{
var decryptedSymmetricKey = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
email.Subject = await jsInteropService.SymmetricDecrypt(email.Subject, Convert.ToBase64String(decryptedSymmetricKey));
var decryptedSymmetricKeyBase64 = await jsInteropService.DecryptWithPrivateKey(email.EncryptedSymmetricKey, privateKey.PrivateKey);
email.Subject = await jsInteropService.SymmetricDecrypt(email.Subject, decryptedSymmetricKeyBase64);
if (email.MessageHtml is not null)
{
email.MessageHtml = await jsInteropService.SymmetricDecrypt(email.MessageHtml, Convert.ToBase64String(decryptedSymmetricKey));
email.MessageHtml = await jsInteropService.SymmetricDecrypt(email.MessageHtml, decryptedSymmetricKeyBase64);
}
if (email.MessagePlain is not null)
{
email.MessagePlain = await jsInteropService.SymmetricDecrypt(email.MessagePlain, Convert.ToBase64String(decryptedSymmetricKey));
email.MessagePlain = await jsInteropService.SymmetricDecrypt(email.MessagePlain, decryptedSymmetricKeyBase64);
}
email.FromDisplay = await jsInteropService.SymmetricDecrypt(email.FromDisplay, Convert.ToBase64String(decryptedSymmetricKey));
email.FromLocal = await jsInteropService.SymmetricDecrypt(email.FromLocal, Convert.ToBase64String(decryptedSymmetricKey));
email.FromDomain = await jsInteropService.SymmetricDecrypt(email.FromDomain, Convert.ToBase64String(decryptedSymmetricKey));
email.FromDisplay = await jsInteropService.SymmetricDecrypt(email.FromDisplay, decryptedSymmetricKeyBase64);
email.FromLocal = await jsInteropService.SymmetricDecrypt(email.FromLocal, decryptedSymmetricKeyBase64);
email.FromDomain = await jsInteropService.SymmetricDecrypt(email.FromDomain, decryptedSymmetricKeyBase64);
}
catch (Exception ex)
{

View File

@@ -199,13 +199,13 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
/// </summary>
/// <param name="base64Ciphertext">Ciphertext to decrypt.</param>
/// <param name="privateKey">Private key to use for decryption.</param>
/// <returns>Decrypted string.</returns>
public async Task<byte[]> DecryptWithPrivateKey(string base64Ciphertext, string privateKey)
/// <returns>Decrypted bytes as base64 string.</returns>
public async Task<string> DecryptWithPrivateKey(string base64Ciphertext, string privateKey)
{
try
{
// Invoke the JavaScript function and get the result as a byte array
byte[] result = await jsRuntime.InvokeAsync<byte[]>("rsaInterop.decryptWithPrivateKey", base64Ciphertext, privateKey);
// Invoke the JavaScript function and get the result as a base64 string
string result = await jsRuntime.InvokeAsync<string>("rsaInterop.decryptWithPrivateKey", base64Ciphertext, privateKey);
return result;
}
catch (JSException ex)

View File

@@ -28,7 +28,7 @@ function checkCryptoAvailable() {
window.cryptoInterop = {
encrypt: async function (plaintext, base64Key) {
checkCryptoAvailable();
const key = await window.crypto.subtle.importKey(
"raw",
Uint8Array.from(atob(base64Key), c => c.charCodeAt(0)),
@@ -62,7 +62,7 @@ window.cryptoInterop = {
},
decrypt: async function (base64Ciphertext, base64Key) {
checkCryptoAvailable();
const key = await window.crypto.subtle.importKey(
"raw",
Uint8Array.from(atob(base64Key), c => c.charCodeAt(0)),
@@ -89,7 +89,7 @@ window.cryptoInterop = {
},
decryptBytes: async function (base64Ciphertext, base64Key) {
checkCryptoAvailable();
const key = await window.crypto.subtle.importKey(
"raw",
Uint8Array.from(atob(base64Key), c => c.charCodeAt(0)),
@@ -126,7 +126,7 @@ window.rsaInterop = {
*/
generateRsaKeyPair : async function() {
checkCryptoAvailable();
const keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
@@ -154,7 +154,7 @@ window.rsaInterop = {
*/
encryptWithPublicKey : async function(plaintext, publicKey) {
checkCryptoAvailable();
const publicKeyObj = await window.crypto.subtle.importKey(
"jwk",
JSON.parse(publicKey),
@@ -181,11 +181,11 @@ window.rsaInterop = {
* Decrypts a ciphertext string using an RSA private key.
* @param {string} ciphertext - The base64-encoded ciphertext to decrypt.
* @param {string} privateKey - The private key in JWK format.
* @returns {Promise<Uint8Array>} A promise that resolves to the decrypted data as a Uint8Array.
* @returns {Promise<string>} A promise that resolves to the decrypted data as a base64 string.
*/
decryptWithPrivateKey: async function(ciphertext, privateKey) {
checkCryptoAvailable();
try {
// Parse the private key
let parsedPrivateKey = JSON.parse(privateKey);
@@ -215,8 +215,9 @@ window.rsaInterop = {
cipherBuffer
);
// Return the decrypted data as a Uint8Array
return new Uint8Array(plaintextBuffer);
// Convert to base64 string instead of returning Uint8Array to avoid Blazor serialization issues, see https://github.com/dotnet/aspnetcore/issues/59837
const decryptedBytes = new Uint8Array(plaintextBuffer);
return btoa(String.fromCharCode.apply(null, Array.from(decryptedBytes)));
} catch (error) {
throw new Error(`Failed to decrypt: ${error.message}`);
}