You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WASM通过JS互操作加解密报错:组件渲染未处理异常

AES-GCM解密失败问题排查(Blazor WASM + JS互操作)

问题描述

通过Blazor WASM结合JS互操作实现AES-GCM加解密时,解密操作始终失败,抛出错误:DOM异常:提供的数据过小。已确认加密生成的密文、Key、IV格式均正确,但解密无法完成。

完整代码实现

Blazor页面(Encryption.razor)

<div class="input-container">
    <label for="plainText">Input string</label>
    <div class="input-wrapper">
        <input id="plainText" type="text" @bind="plainText" title="Input string" />
    </div>
</div>

<div class="button-container">
    <button @onclick="Encrypt" class="button-style button-one">Encrypt</button>
    <button @onclick="Decrypt" class="button-style">Decrypt</button>
</div>

<div class="input-container">
    <label for="encryptedMessage">Encrypted/Decrypted Message</label>
    <div class="input-wrapper">
        <input id="encryptedMessage" type="text" @bind="encryptedMessage" />
    </div>
</div>

@code {
    private string plainText = "";
    private string encryptedMessage = "";
    private string keyBase64 = "";
    private string ivBase64 = "";

    // Method to generate a key and IV
    private async Task GenerateKeyAndIV()
    {
        keyBase64 = await JSRuntime.InvokeAsync<string>("generateKey");
        ivBase64 = await JSRuntime.InvokeAsync<string>("generateIV");
    }

    private async Task Encrypt()
    {
        await GenerateKeyAndIV();
        encryptedMessage = await encryptionService.Encrypt(plainText, keyBase64, ivBase64);
    }

    private async Task Decrypt()
    {
        await GenerateKeyAndIV();
        plainText = await encryptionService.Decrypt(encryptedMessage, keyBase64, ivBase64);
    }

    // Call GenerateKeyAndIV when the component is initialized
    protected override async Task OnInitializedAsync()
    {
        await GenerateKeyAndIV();
    }
}

EncryptionService.cs

using Microsoft.JSInterop;
using System.Threading.Tasks;

public class EncryptionService
{
    private readonly IJSRuntime _jsRuntime;

    public EncryptionService(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public async Task<string> Encrypt(string text, string keyBase64, string iv)
    {
        return await _jsRuntime.InvokeAsync<string>("encryptText", text, keyBase64, iv);
    }

    public async Task<string> Decrypt(string encryptedBase64, string keyBase64, string iv)
    {
        return await _jsRuntime.InvokeAsync<string>("decryptText", encryptedBase64, keyBase64, iv);
    }
}

index.html中的JS脚本

<script>
      window.generateKey = async () => {
          const key = await crypto.subtle.generateKey(
              { name: 'AES-GCM', length: 128 },
              true,
              ['encrypt', 'decrypt']
          );
          const keyBytes = await crypto.subtle.exportKey('raw', key);
          return btoa(String.fromCharCode.apply(null, new Uint8Array(keyBytes)));
      };
</script> 

<!-- Function to generate IV -->
<script>
window.generateIV = async () => {
    const iv = crypto.getRandomValues(new Uint8Array(12));
    return btoa(String.fromCharCode.apply(null, iv));
};
</script>

<!-- Function to encrypt text using AES-GCM algorithm -->
<script>
  async function encryptText(text, keyBase64, ivBase64) {
      const iv = new Uint8Array(atob(ivBase64).split('').map(char => char.charCodeAt(0)));
      const keyBytes = new Uint8Array(atob(keyBase64).split('').map(char => char.charCodeAt(0)));
      const encodedText = new TextEncoder().encode(text);
      const encodedKey = await crypto.subtle.importKey("raw", keyBytes, "AES-GCM", false, ["encrypt"]);
      const encryptedBytes = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, encodedKey, encodedText);
      return btoa(String.fromCharCode.apply(null, new Uint8Array(encryptedBytes)));
  }
</script>

<!-- Function to decrypt text using AES-GCM algorithm -->
<script>
  async function decryptText(encryptedBase64, keyBase64, ivBase64) {
      const iv = new Uint8Array(atob(ivBase64).split('').map(char => char.charCodeAt(0)));
      const keyBytes = new Uint8Array(atob(keyBase64).split('').map(char => char.charCodeAt(0)));
      const encryptedBytes = new Uint8Array(atob(encryptedBase64).split('').map(char => char.charCodeAt(0)));
      const importedKey = await crypto.subtle.importKey("raw", keyBytes, "AES-GCM", false, ["decrypt"]);
      const decryptedBytes = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, importedKey, encryptedBytes);
      return new TextDecoder().decode(decryptedBytes);
  }
</script>

问题根源

解密失败的核心原因是每次调用Decrypt方法时都重新生成了新的Key和IV。AES-GCM算法要求解密必须使用与加密时完全相同的Key和IV:

  • 新生成的Key无法匹配加密时的密文,导致解密过程中无法正确解析数据
  • AES-GCM会自动附加认证标签用于校验数据完整性,不匹配的Key/IV会触发校验失败,最终抛出"提供的数据过小"的异常(本质是无法识别有效密文结构)

修复方案

修改Decrypt方法,移除重新生成Key和IV的逻辑,直接使用加密时保存的keyBase64和ivBase64:

修改后的Blazor组件代码(Decrypt方法部分)

private async Task Decrypt()
{
    // 移除GenerateKeyAndIV()调用,复用加密时的Key和IV
    plainText = await encryptionService.Decrypt(encryptedMessage, keyBase64, ivBase64);
}

额外说明

  1. 如果需要持久化密文(比如保存到后端或本地存储),必须同时保存密文、Key(需加密存储)和IV(IV无需保密,但必须与加密时一致)
  2. AES-GCM的IV推荐每次加密都使用随机值,但同一组密文必须对应唯一的Key+IV组合

内容的提问来源于stack exchange,提问作者er280652

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 04:11:03