C#实现JSON数据AES加密解密:原理、代码与最佳实践

📅 2026/7/4 14:31:54
C#实现JSON数据AES加密解密:原理、代码与最佳实践
1. 项目概述与核心价值最近在做一个需要处理敏感数据的项目比如用户的个人资料、订单信息这些数据在传输和存储时如果明文摆在那儿心里总是不踏实。安全无小事尤其是在数据合规要求越来越严格的今天加密成了开发中的标配操作。我选择了AES高级加密标准这个对称加密算法它速度快、安全性高是目前应用最广泛的加密标准之一。而JSON作为数据交换的“世界语”几乎所有的前后端接口都在用它。所以把这两者结合起来——用C#对JSON数据进行AES加密和解密就成了一个非常实际且高频的需求场景。这个实例要解决的就是如何安全地封装一段JSON数据确保它在网络传输中或落盘存储时即使被截获也无法被直接解读。整个过程不仅仅是调用一两个API那么简单它涉及到密钥管理、加密模式选择、初始化向量IV的使用、以及如何将加密后的二进制数据安全地转换为字符串比如Base64以便于JSON传输。我会带你从原理到代码完整走一遍这个流程分享我在实际项目中趟过的坑和总结的最佳实践。无论你是需要为API增加一层安全防护还是要在本地安全地保存配置文件这套方案都能直接拿来用。2. 核心原理与方案选型在动手写代码之前我们得先搞清楚几个关键概念这决定了我们加密方案的健壮性和安全性。盲目调用Aes.Create()然后加密很可能埋下隐患。2.1 为什么是AES-CBC-PKCS7AES是一种分组加密算法它把数据分成固定大小的块128位进行处理。但我们的JSON数据长度是随机的不可能总是128位的整数倍这就需要用到“模式”和“填充”。加密模式Cipher Mode我选择CBC密码分组链接模式。这是目前推荐使用的模式之一。它的核心思想是每一块明文在加密前会先与前一块的密文进行异或操作。第一块没有前一块密文怎么办这就引入了初始化向量IV。IV是一个随机生成的、长度与分组相同的值用来确保即使加密相同的明文每次产生的密文也是不同的。这有效抵御了“密文模式分析”攻击。ECB模式电子密码本是绝对要避免的因为它相同的明文块会产生相同的密文块安全性很差。填充Padding当最后一块数据不足128位16字节时就需要填充。我选择PKCS7填充方式。这是.NET中Aes类的默认填充也是行业标准。它会明确地填充需要填充的字节数解密时能准确移除填充非常可靠。所以完整的算法标识是AES-256-CBC-PKCS7。其中256指的是密钥长度256位安全性更高。128位和192位也是可选的但256位是目前对抗暴力破解的推荐长度。2.2 密钥Key与初始化向量IV的管理心法这是安全的核心也是容易出错的地方。密钥Key用于加密和解密的同一把钥匙。对于AES-256密钥必须是32字节256位的二进制数据。绝对不要使用一个简单的字符串如“mySuperSecretKey”直接作为密钥因为它的字节长度和随机性都不够。正确的做法是使用密码学安全的随机数生成器CSPRNG生成或者从一个密码通过PBKDF2等密钥派生函数衍生出来。初始化向量IV必须是16字节128位并且每次加密都应该使用一个全新的、随机的IV。IV不需要保密它可以和密文一起传输或存储。但至关重要的一点是解密时必须使用加密时生成的同一个IV。一个常见的做法是将IV预置在密文数据的前面解密时先取出前16字节作为IV剩下的部分才是真正的密文。重要提示密钥是最高机密必须妥善保管如使用密钥管理系统、环境变量、或硬件安全模块。IV可以公开但必须随机且唯一。2.3 数据转换字节、Base64与JSON加密操作处理的是字节数组byte[]而JSON是文本字符串。我们需要桥梁加密后将得到的密文字节数组转换为Base64字符串。Base64是一种用可打印字符表示二进制数据的方法非常适合在JSON、URL等文本协议中安全传输。解密前将接收到的Base64字符串转换回字节数组再进行解密。原始数据我们需要将JSON字符串或对象序列化后的字符串转换为byte[]进行加密解密后再转换回字符串。3. 完整代码实现与分步解析下面我将用一个完整的AesJsonCryptoHelper类来演示并逐行解释关键点。using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Text.Json; // 使用System.Text.Json性能更好 namespace SecurityDemo { /// summary /// AES加密解密JSON数据的辅助类 /// /summary public class AesJsonCryptoHelper { // 密钥长度AES-256 需要 32 字节 private const int KeySize 32; // 256 bit // 初始化向量长度AES分组大小是16字节 private const int IvSize 16; // 128 bit private readonly byte[] _key; /// summary /// 构造函数接收Base64编码的密钥字符串 /// /summary /// param namebase64KeyBase64格式的32字节密钥/param /// exception crefArgumentException密钥长度不正确/exception public AesJsonCryptoHelper(string base64Key) { if (string.IsNullOrWhiteSpace(base64Key)) throw new ArgumentException(密钥不能为空, nameof(base64Key)); _key Convert.FromBase64String(base64Key); if (_key.Length ! KeySize) throw new ArgumentException($密钥必须是{KeySize}字节Base64解码后当前是{_key.Length}字节, nameof(base64Key)); } /// summary /// 加密一个对象为加密后的Base64字符串 /// /summary /// typeparam nameT待加密对象的类型/typeparam /// param namedata待加密的对象/param /// returns包含IV和密文的Base64字符串/returns public string EncryptT(T data) { // 1. 将对象序列化为JSON字符串再转为字节数组 string jsonString JsonSerializer.Serialize(data); byte[] plainBytes Encoding.UTF8.GetBytes(jsonString); using (Aes aesAlg Aes.Create()) { // 2. 配置AES算法 aesAlg.Key _key; aesAlg.Mode CipherMode.CBC; // 显式指定CBC模式 aesAlg.Padding PaddingMode.PKCS7; // 显式指定PKCS7填充 // IV由Aes实例自动生成随机且安全 // 3. 创建加密器 ICryptoTransform encryptor aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); // 4. 执行加密 byte[] cipherBytes; using (MemoryStream msEncrypt new MemoryStream()) { // 先将IV写入流的最前面 msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); using (CryptoStream csEncrypt new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { csEncrypt.Write(plainBytes, 0, plainBytes.Length); // 非常重要必须调用FlushFinalBlock否则最后一块可能不会被处理。 csEncrypt.FlushFinalBlock(); } cipherBytes msEncrypt.ToArray(); } // 5. 将整个字节数组IV密文转换为Base64字符串 return Convert.ToBase64String(cipherBytes); } } /// summary /// 解密Base64字符串还原为对象 /// /summary /// typeparam nameT目标对象类型/typeparam /// param nameencryptedBase64加密后的Base64字符串/param /// returns解密后的对象/returns public T DecryptT(string encryptedBase64) { if (string.IsNullOrWhiteSpace(encryptedBase64)) throw new ArgumentException(加密字符串不能为空, nameof(encryptedBase64)); // 1. 将Base64字符串转换回字节数组 byte[] fullCipherBytes Convert.FromBase64String(encryptedBase64); // 2. 分离IV和真正的密文 if (fullCipherBytes.Length IvSize) throw new CryptographicException(密文数据太短无法提取IV); byte[] iv new byte[IvSize]; byte[] cipherBytes new byte[fullCipherBytes.Length - IvSize]; Buffer.BlockCopy(fullCipherBytes, 0, iv, 0, IvSize); // 前16字节是IV Buffer.BlockCopy(fullCipherBytes, IvSize, cipherBytes, 0, cipherBytes.Length); // 剩余的是密文 using (Aes aesAlg Aes.Create()) { // 3. 配置AES算法必须与加密时一致 aesAlg.Key _key; aesAlg.IV iv; // 使用提取出来的IV aesAlg.Mode CipherMode.CBC; aesAlg.Padding PaddingMode.PKCS7; // 4. 创建解密器 ICryptoTransform decryptor aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); // 5. 执行解密 byte[] plainBytes; using (MemoryStream msDecrypt new MemoryStream(cipherBytes)) { using (CryptoStream csDecrypt new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (MemoryStream msPlain new MemoryStream()) { csDecrypt.CopyTo(msPlain); plainBytes msPlain.ToArray(); } } } // 6. 将解密后的字节数组转为JSON字符串再反序列化为对象 string jsonString Encoding.UTF8.GetString(plainBytes); return JsonSerializer.DeserializeT(jsonString); } } /// summary /// 生成一个随机的、安全的AES-256密钥Base64格式 /// /summary public static string GenerateRandomKeyBase64() { using (var rng RandomNumberGenerator.Create()) { byte[] key new byte[KeySize]; rng.GetBytes(key); // 使用密码学安全的随机数生成器 return Convert.ToBase64String(key); } } } }关键步骤解析密钥初始化AesJsonCryptoHelper类在构造时接收一个Base64格式的密钥。这假设你已经通过安全的方式如GenerateRandomKeyBase64方法生成了密钥并进行了存储。在真实项目中这个密钥可能来自配置文件需加密、环境变量或密钥管理服务。加密流程aesAlg.CreateEncryptor()会自动生成一个随机IV。我们将IV写入内存流的最前端然后再写入加密后的数据。这样IV就和密文绑定在了一起。务必调用FlushFinalBlock()它负责处理最后的数据块并应用填充。忘记调用会导致解密时出现“填充无效无法移除”的错误。最终将整个IV密文的字节数组转换为Base64字符串输出。解密流程先将Base64字符串还原为字节数组。利用Buffer.BlockCopy高效地分离出前16字节IV和后面的密文。使用相同的密钥和提取出的IV配置AES算法。通过CryptoStream读取解密后的数据。最后将解密得到的字节数组即原始的JSON字符串字节反序列化为目标对象。4. 实战应用与单元测试光有工具类不够我们得知道怎么用并且验证它是对的。下面是一个模拟用户数据加密的示例和配套的单元测试使用xUnit。示例加密用户敏感信息// 假设这是我们的数据模型 public class UserProfile { public int UserId { get; set; } public string Username { get; set; } public string Email { get; set; } public string PhoneNumber { get; set; } // 敏感信息 public DateTime BirthDate { get; set; } } class Program { static void Main(string[] args) { // 1. 生成并保存好密钥此操作仅需一次 string secretKeyBase64 AesJsonCryptoHelper.GenerateRandomKeyBase64(); Console.WriteLine($请妥善保管此密钥例如放入环境变量: {secretKeyBase64.Substring(0, 16)}...); // 模拟从环境变量读取 // string secretKeyBase64 Environment.GetEnvironmentVariable(AES_SECRET_KEY); // 2. 初始化加密助手 var cryptoHelper new AesJsonCryptoHelper(secretKeyBase64); // 3. 准备要加密的数据 var userProfile new UserProfile { UserId 1001, Username 张三, Email zhangsanexample.com, PhoneNumber 13800138000, BirthDate new DateTime(1990, 5, 20) }; // 4. 加密 string encryptedData cryptoHelper.Encrypt(userProfile); Console.WriteLine($加密后的数据Base64:\n{encryptedData}); // 这个encryptedData可以安全地存入数据库或通过网络发送 // 5. 解密 try { UserProfile decryptedProfile cryptoHelper.DecryptUserProfile(encryptedData); Console.WriteLine($\n解密成功); Console.WriteLine($用户名: {decryptedProfile.Username}, 电话: {decryptedProfile.PhoneNumber}); } catch (CryptographicException ex) { Console.WriteLine($解密失败: {ex.Message}); } } }单元测试确保可靠性using Xunit; using System; using System.Text.Json; namespace SecurityDemo.Tests { public class AesJsonCryptoHelperTests { private readonly string _testKeyBase64; public AesJsonCryptoHelperTests() { // 使用固定的密钥进行测试确保结果可重现 // 实际这是通过AesJsonCryptoHelper.GenerateRandomKeyBase64()生成的 _testKeyBase64 k3F7eP8mNqRtSvWxYzAdBcDfHgJkLpO0; // 32字符Base64对应32字节 } [Fact] public void EncryptAndDecrypt_SimpleObject_ShouldReturnOriginal() { // Arrange var helper new AesJsonCryptoHelper(_testKeyBase64); var original new { Name Test, Value 123, Active true }; // Act string encrypted helper.Encrypt(original); var decrypted helper.Decryptdynamic(encrypted); // 使用dynamic简化测试 // Assert string decryptedJson JsonSerializer.Serialize(decrypted); string originalJson JsonSerializer.Serialize(original); Assert.Equal(originalJson, decryptedJson); } [Fact] public void Encrypt_SameDataTwice_ShouldProduceDifferentCipherText() { // Arrange var helper new AesJsonCryptoHelper(_testKeyBase64); var data This is a secret message.; // Act string cipherText1 helper.Encrypt(data); string cipherText2 helper.Encrypt(data); // 再次加密相同数据 // Assert: 由于IV随机密文应该不同 Assert.NotEqual(cipherText1, cipherText2); } [Fact] public void Decrypt_WithWrongKey_ShouldThrowCryptographicException() { // Arrange var helper1 new AesJsonCryptoHelper(_testKeyBase64); var helper2 new AesJsonCryptoHelper(AesJsonCryptoHelper.GenerateRandomKeyBase64()); // 另一个随机密钥 var original SensitiveData; string encrypted helper1.Encrypt(original); // Act Assert Assert.ThrowsCryptographicException(() helper2.Decryptstring(encrypted)); } [Fact] public void Decrypt_TamperedCipherText_ShouldThrowCryptographicException() { // Arrange var helper new AesJsonCryptoHelper(_testKeyBase64); var original Important; string encrypted helper.Encrypt(original); // 模拟密文在传输中被篡改修改一个字符 char[] chars encrypted.ToCharArray(); chars[chars.Length / 2] chars[chars.Length / 2] A ? B : A; string tampered new string(chars); // Act Assert // 可能会抛出 CryptographicException 或 FormatException (Base64无效) Assert.ThrowsAnyException(() helper.Decryptstring(tampered)); } } }5. 进阶话题与性能优化当数据量变大或者在高并发场景下基础的用法可能会遇到性能瓶颈。这里分享几个优化思路。5.1 使用SpanT和Pipelines减少分配对于非常大的JSON字符串频繁的字节数组分配和复制会产生GC压力。.NET Core以后我们可以使用Spanbyte和System.IO.Pipelines来优化。// 示例使用Span优化Encrypt方法中的字节操作部分代码 public string EncryptWithSpanT(T data) { string jsonString JsonSerializer.Serialize(data); byte[] plainBytes Encoding.UTF8.GetBytes(jsonString); // 这里仍有分配 using (Aes aesAlg Aes.Create()) { aesAlg.Key _key; aesAlg.Mode CipherMode.CBC; aesAlg.Padding PaddingMode.PKCS7; ICryptoTransform encryptor aesAlg.CreateEncryptor(); byte[] iv aesAlg.IV; // 计算输出缓冲区大小: IV长度 加密后数据长度可能需要填充 int outputSize iv.Length encryptor.OutputBlockSize * ((plainBytes.Length / encryptor.InputBlockSize) 1); byte[] outputBuffer new byte[outputSize]; // 使用Span进行内存操作 Spanbyte outputSpan outputBuffer.AsSpan(); iv.CopyTo(outputSpan.Slice(0, iv.Length)); // 复制IV到开头 int bytesWritten iv.Length; // 注意TransformBlock/TransformFinalBlock 是底层方法需小心使用 // 更优的方案是使用 CryptoStream 与 ArrayPool 或 Pipe 结合此处为简化示例 bytesWritten encryptor.TransformBlock(plainBytes, 0, plainBytes.Length, outputBuffer, bytesWritten); bytesWritten encryptor.TransformFinalBlock(Array.Emptybyte(), 0, 0, outputBuffer, bytesWritten); // 截取实际使用的部分并转换 return Convert.ToBase64String(outputBuffer.AsSpan(0, bytesWritten)); } }注意直接使用TransformBlock和TransformFinalBlock需要对加密流程有更深理解。对于大多数应用使用CryptoStream配合MemoryStream已经足够高效且更安全。优化应在性能 profiling 确认此处是热点后再进行。5.2 密钥的安全存储与轮换存储切勿将密钥硬编码在代码中。应使用如Azure Key Vault、AWS KMS、HashiCorp Vault等专业的密钥管理服务。在开发环境可以使用用户机密User Secrets或受保护的环境变量。轮换定期更换密钥是安全最佳实践。这意味着新旧密钥会有一段时间共存。你的系统需要能识别数据是用哪个版本密钥加密的可以在密文中添加一个密钥版本头并使用对应的密钥解密。解密失败时尝试用旧密钥解密。5.3 加密大量数据或流式数据如果要加密整个文件或网络流应使用流式处理避免将全部数据加载到内存。public async Task EncryptStreamAsync(Stream inputPlainStream, Stream outputCipherStream, CancellationToken ct default) { using (Aes aesAlg Aes.Create()) { aesAlg.Key _key; aesAlg.Mode CipherMode.CBC; aesAlg.Padding PaddingMode.PKCS7; // 将IV写入输出流开头 await outputCipherStream.WriteAsync(aesAlg.IV, 0, aesAlg.IV.Length, ct); using (ICryptoTransform encryptor aesAlg.CreateEncryptor()) using (CryptoStream cryptoStream new CryptoStream(outputCipherStream, encryptor, CryptoStreamMode.Write, leaveOpen: true)) { await inputPlainStream.CopyToAsync(cryptoStream, 81920, ct); // 使用缓冲区异步复制 await cryptoStream.FlushFinalBlockAsync(ct); } } // 注意outputCipherStream在此方法外由调用者负责关闭 }6. 常见问题、异常排查与安全加固在实际使用中你肯定会遇到各种异常和问题。下面这个表格整理了我踩过的坑和解决方案。异常信息或问题现象可能原因排查步骤与解决方案CryptographicException: Padding is invalid and cannot be removed.这是最常见的异常。1.密钥错误解密用的密钥与加密时不同。2.IV不匹配解密时使用的IV与加密时生成的不一致。3.密文被篡改传输或存储过程中密文损坏。4.加密/解密模式或填充方式不匹配一端是CBC另一端是ECB。1. 确认双方密钥完全一致Base64字符串比对。2. 确认IV的提取和拼接逻辑正确。确保加密时将IV放在密文前解密时准确取出前16字节。3. 检查数据完整性。4. 确保CipherMode和PaddingMode在加密解密两端设置完全相同。FormatException: The input is not a valid Base-64 string.在Convert.FromBase64String时抛出。密文Base64字符串格式错误可能含有非法字符如空格、换行、长度不正确、或URL传输后未正确解码变空格/变_。1. 检查密文字符串是否在传输中被意外修改如日志截断、URL编码/解码问题。2. 如果密文用于URL需使用UrlSafeBase64将替换为-/替换为_并去除填充接收方需反向处理。ArgumentException: 指定的密钥对此算法无效。密钥长度不符合算法要求。AES-256需要32字节密钥你可能传了一个长度不对的字符串。检查生成和加载密钥的代码。确保_key字节数组的长度是32。使用提供的GenerateRandomKeyBase64方法生成。解密后得到乱码或JSON反序列化失败解密本身成功了没有抛加密异常但得到的明文字节不是有效的UTF-8 JSON。可能原因1. 加密前的数据不是UTF-8编码。2. 加密/解密过程中数据流处理有误如忘记FlushFinalBlock。1. 在解密后将plainBytes用Encoding.UTF8.GetString转换后打印出来看是否是预期的JSON字符串。2. 确保加密时源数据序列化与解密后反序列化使用相同的JSON序列化器设置如日期格式、命名策略。性能问题加密大量数据时慢1. 使用JsonSerializer.Serialize/Deserialize处理超大对象。2. 频繁创建和销毁Aes对象和加密器。1. 对于大数据考虑流式加密见5.3节。2. 对于Web应用等高并发场景Aes对象和ICryptoTransform不是线程安全的不要将其设为单例。但密钥byte[]可以安全共享。每个加密操作应创建新的Aes实例。性能瓶颈通常不在此处应先进行性能分析。如何选择密钥长度128, 192, 还是256安全性256位 192位 128位。性能128位通常最快。对于绝大多数现代应用推荐使用AES-256。它提供了更高的安全边际且在现代CPU上性能损失很小。除非在极端性能敏感且安全要求不高的嵌入式场景才考虑AES-128。安全加固建议使用认证加密AEAD上述CBC模式提供了机密性但缺乏完整性校验。攻击者可能篡改IV或密文导致解密出错误但看似合理的数据。更现代、更推荐的方式是使用AES-GCM模式它在加密的同时会生成一个认证标签Tag用于验证密文和IV的完整性。.NET中可以通过AesGcm类需要引用System.Security.CryptographyNuGet包来实现。这是目前的最佳实践。密钥派生如果你的密钥来源于一个用户提供的密码切勿直接使用密码的字节。务必使用PBKDF2、Argon2等密钥派生函数并加入随机盐Salt以抵御彩虹表攻击。避免“魔数”不要在你的代码里写死加密算法参数如“AES/CBC/PKCS7Padding”。应该将其作为可配置项方便未来算法升级或替换。7. 在真实项目中的集成示例最后我们看一个在ASP.NET Core Web API中集成加密功能的简化示例用于加密传输中的特定敏感字段。假设我们有一个接收用户注册信息的API我们希望入库前加密手机号字段。1. 模型与DTOpublic class UserRegistrationDto { public string Username { get; set; } public string Email { get; set; } [SensitiveData] // 自定义一个属性来标记需要加密的字段 public string PhoneNumber { get; set; } public string Password { get; set; } } public class UserEntity { public int Id { get; set; } public string Username { get; set; } public string Email { get; set; } public string EncryptedPhoneNumber { get; set; } // 数据库存储加密后的Base64字符串 public string PasswordHash { get; set; } }2. 使用ActionFilter进行自动加密/解密// 一个简单的、用于Controller或Action的过滤器 public class EncryptSensitiveDataAttribute : ActionFilterAttribute { private readonly AesJsonCryptoHelper _cryptoHelper; // 可以通过依赖注入获取配置好的Helper public EncryptSensitiveDataAttribute(IConfiguration configuration) { var key configuration[Encryption:Key]; _cryptoHelper new AesJsonCryptoHelper(key); } public override void OnActionExecuting(ActionExecutingContext context) { // 在方法执行前解密传入的加密数据如果需要 // 此例简化假设前端传明文后端加密后存。 base.OnActionExecuting(context); } public override void OnActionExecuted(ActionExecutedContext context) { // 在方法执行后加密返回的敏感数据如果需要 if (context.Result is ObjectResult objectResult objectResult.Value ! null) { // 这里可以使用反射查找带有[SensitiveData]标记的属性并进行加密 // 或者使用JSON序列化器中的Converter来实现更优雅。 } base.OnActionExecuted(context); } } // 更优雅的方式使用自定义的JsonConverter public class SensitiveDataConverter : JsonConverterstring { private readonly AesJsonCryptoHelper _cryptoHelper; public SensitiveDataConverter(string key) _cryptoHelper new AesJsonCryptoHelper(key); public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 反序列化时假设从数据库/前端读来的是加密字符串这里直接返回解密由业务层处理。 // 或者如果前端传密文这里可以解密。 return reader.GetString(); } public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) { // 序列化时如果是手机号等敏感字段则加密后写入 string encryptedValue _cryptoHelper.Encrypt(value); writer.WriteStringValue(encryptedValue); } }3. 在Service层进行加解密操作public class UserService { private readonly AesJsonCryptoHelper _cryptoHelper; private readonly AppDbContext _context; public UserService(IConfiguration configuration, AppDbContext context) { var key configuration[Encryption:Key]; _cryptoHelper new AesJsonCryptoHelper(key); _context context; } public async Taskint RegisterUserAsync(UserRegistrationDto dto) { var entity new UserEntity { Username dto.Username, Email dto.Email, // 在入库前加密敏感字段 EncryptedPhoneNumber _cryptoHelper.Encrypt(dto.PhoneNumber), PasswordHash HashPassword(dto.Password) // 密码应单独哈希处理 }; _context.Users.Add(entity); await _context.SaveChangesAsync(); return entity.Id; } public async TaskUserProfileDto GetUserProfileAsync(int userId) { var entity await _context.Users.FindAsync(userId); if (entity null) return null; return new UserProfileDto { Username entity.Username, Email entity.Email, // 在返回给前端前可以不解密或者根据权限决定是否解密。 // 如果前端需要显示则解密 PhoneNumber _cryptoHelper.Decryptstring(entity.EncryptedPhoneNumber) }; } }这个实例从最基础的AES-CBC加解密原理讲起逐步深入到安全的密钥管理、性能优化、异常处理以及真实的项目集成方案。记住加密只是安全链条中的一环密钥的安全存储、传输通道的安全HTTPS、最小权限原则等同样重要。希望这份详尽的指南能让你在下次需要加密JSON数据时信心十足游刃有余。