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

Angular 2新手求助:C#加密图片后TypeScript解密实现方案

当然可行!不过这里有几个关键细节要注意,比如RSA加密的长度限制、密钥格式的兼容,还有前后端的编码一致性。另外,直接用RSA加密图片这类大文件效率很低,更推荐结合对称加密来做,下面一步步帮你梳理:

一、先理清加密解密的正确逻辑

首先要明确RSA的核心特性:公钥加密,私钥解密(用于保密传输);私钥签名,公钥验签(用于身份验证)。如果你的需求是前端解密图片,那前端需要持有私钥,但私钥暴露在前端会有严重安全风险。因此更合理的方案是:

用AES(对称加密)加密图片(效率高,无长度限制),再用RSA加密AES的密钥,前端拿到加密的AES密钥和加密的图片后,先用RSA解密得到AES密钥,再用AES解密图片。

二、完善你的C#加密代码

下面是完整的C#实现,包含RSA密钥生成、AES密钥生成、图片加密和AES密钥加密:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Xml.Serialization;

public class ImageEncryptionHelper
{
    // 生成RSA密钥对(公钥给前端,私钥后端安全存储)
    public (string PublicKeyXml, string PrivateKeyPkcs8Base64) GenerateRsaKeyPair()
    {
        using var rsa = new RSACryptoServiceProvider(2048);
        
        // 导出XML格式公钥(方便前端转换)
        var pubParams = rsa.ExportParameters(false);
        var pubWriter = new StringWriter();
        new XmlSerializer(typeof(RSAParameters)).Serialize(pubWriter, pubParams);
        string pubKeyXml = pubWriter.ToString();

        // 导出PKCS#8格式私钥(Base64编码,方便前端导入Web Crypto)
        string privKeyPkcs8 = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey());

        return (pubKeyXml, privKeyPkcs8);
    }

    // 生成AES密钥和IV(128/256位都可以,这里用256位)
    public (byte[] AesKey, byte[] AesIv) GenerateAesKey()
    {
        using var aes = Aes.Create();
        aes.KeySize = 256;
        aes.GenerateKey();
        aes.GenerateIV();
        return (aes.Key, aes.IV);
    }

    // AES加密图片字节
    public byte[] EncryptImageWithAes(byte[] imageBytes, byte[] aesKey, byte[] aesIv)
    {
        using var aes = Aes.Create();
        aes.Key = aesKey;
        aes.IV = aesIv;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
        using var ms = new MemoryStream();
        using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
        
        cs.Write(imageBytes, 0, imageBytes.Length);
        cs.FlushFinalBlock();
        
        return ms.ToArray();
    }

    // RSA加密AES密钥(用公钥加密,确保只有持有私钥的前端能解密)
    public byte[] EncryptAesKeyWithRsa(byte[] aesKey, string rsaPublicKeyXml)
    {
        using var rsa = new RSACryptoServiceProvider(2048);
        
        // 导入XML格式公钥
        var pubReader = new StringReader(rsaPublicKeyXml);
        var pubParams = (RSAParameters)new XmlSerializer(typeof(RSAParameters)).Deserialize(pubReader);
        rsa.ImportParameters(pubParams);

        // 用PKCS#1填充(和前端Web Crypto的RSA-PKCS1-v1_5匹配)
        return rsa.Encrypt(aesKey, false);
    }
}
三、Angular/TypeScript解密实现

前端用浏览器原生的Web Crypto API来解密(无需额外安装库,安全可靠),下面是服务类的实现:

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ImageDecryptionService {
  // 把C#的XML格式公钥转换为Web Crypto支持的格式(如果需要前端加密用,解密的话用私钥)
  async convertRsaXmlPublicKeyToCryptoKey(xmlPublicKey: string): Promise<CryptoKey> {
    // 解析XML公钥,提取Modulus和Exponent
    const modulusMatch = xmlPublicKey.match(/<Modulus>([^<]+)<\/Modulus>/);
    const exponentMatch = xmlPublicKey.match(/<Exponent>([^<]+)<\/Exponent>/);
    
    if (!modulusMatch || !exponentMatch) {
      throw new Error('Invalid RSA XML public key');
    }

    const modulus = this.base64ToArrayBuffer(modulusMatch[1]);
    const exponent = this.base64ToArrayBuffer(exponentMatch[1]);

    // 导入为公钥
    return crypto.subtle.importKey(
      'spki',
      await this.spkiFromRsaComponents(modulus, exponent),
      { name: 'RSA-PKCS1-v1_5', hash: 'SHA-256' },
      false,
      ['encrypt']
    );
  }

  // 用RSA私钥解密AES密钥
  async decryptAesKeyWithRsa(encryptedAesKeyBase64: string, privateKeyPkcs8Base64: string): Promise<CryptoKey> {
    // 解码Base64的加密AES密钥和PKCS#8私钥
    const encryptedAesKey = this.base64ToArrayBuffer(encryptedAesKeyBase64);
    const privateKeyBytes = this.base64ToArrayBuffer(privateKeyPkcs8Base64);

    // 导入私钥
    const privateKey = await crypto.subtle.importKey(
      'pkcs8',
      privateKeyBytes,
      { name: 'RSA-PKCS1-v1_5', hash: 'SHA-256' },
      false,
      ['decrypt']
    );

    // 解密AES密钥
    const aesKeyBytes = await crypto.subtle.decrypt(
      { name: 'RSA-PKCS1-v1_5' },
      privateKey,
      encryptedAesKey
    );

    // 导入AES密钥
    return crypto.subtle.importKey(
      'raw',
      aesKeyBytes,
      { name: 'AES-CBC', length: 256 },
      false,
      ['decrypt']
    );
  }

  // 用AES解密图片,返回Uint8Array(可直接转为Blob显示)
  async decryptImageWithAes(encryptedImageBase64: string, aesKey: CryptoKey, ivBase64: string): Promise<Uint8Array> {
    const encryptedImageBytes = this.base64ToArrayBuffer(encryptedImageBase64);
    const iv = this.base64ToArrayBuffer(ivBase64);

    const decryptedBytes = await crypto.subtle.decrypt(
      { name: 'AES-CBC', iv: iv },
      aesKey,
      encryptedImageBytes
    );

    return new Uint8Array(decryptedBytes);
  }

  // 工具方法:Base64转ArrayBuffer
  private base64ToArrayBuffer(base64: string): ArrayBuffer {
    const binaryString = window.atob(base64);
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
  }

  // 工具方法:从RSA组件生成SPKI格式公钥
  private async spkiFromRsaComponents(modulus: ArrayBuffer, exponent: ArrayBuffer): Promise<ArrayBuffer> {
    // 构建ASN.1结构,这里推荐用`jsrsasign`库简化转换,避免手动写复杂逻辑
    // 示例:可以安装jsrsasign后用以下代码
    // const rsa = new RSAKey();
    // rsa.setPublic(this.arrayBufferToBase64(modulus), this.arrayBufferToBase64(exponent));
    // const spki = rsa.getPublicKeySPKI();
    // return this.base64ToArrayBuffer(spki);
    throw new Error('Implement SPKI conversion or install jsrsasign library');
  }

  // 工具方法:ArrayBuffer转Base64
  private arrayBufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
  }
}
四、关键注意事项
  • 密钥安全:绝对不要把RSA私钥明文传输或存储在前端代码中!可以用浏览器的crypto.subtle.exportKey结合IndexedDB等安全存储机制来保存私钥。
  • 算法匹配:前后端的加密算法、填充方式、哈希算法必须完全一致!比如C#用RSA-PKCS1-v1_5填充,前端也要对应相同的算法配置。
  • 编码一致性:后端把加密后的字节转成Base64字符串传给前端,前端再转成Uint8Array处理,避免出现编码混乱。
  • 大文件处理:如果图片体积很大,建议后端分段加密,前端分段解密后再拼接,避免内存溢出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:44:34