跨语言RSA加密解密实战:JS与C#/Java兼容性解决方案
2026/7/30 14:27:10 网站建设 项目流程

1. 项目概述:跨平台RSA加密解密的真实挑战

最近在做一个前后端分离的项目,前端是Vue,后端是C#写的Web API,中间还涉及到和另一个Java服务的交互。数据安全是甲方的硬性要求,特别是登录密码和一些敏感信息,明文传输肯定不行。最开始图省事,前端用了个AES加密,后端解密,但很快发现一个问题:AES是对称加密,密钥怎么安全地给前端?把密钥硬编码在JS里,等于把钥匙挂在门上,毫无安全性可言。

这时候,非对称加密RSA就成了必然选择。它的核心思想是用一对密钥:公钥加密,私钥解密。前端可以放心大胆地用公钥加密数据,因为公钥本身就是公开的,不怕泄露;而后端用绝对保密的私钥来解密。这样,密钥分发这个老大难问题就迎刃而解了。听起来很美好,对吧?但坑马上就来了:前端用JavaScript(通常是CryptoJS或类似库)进行RSA加密,后端用C#或Java解密,十有八九会报错。最常见的错误就是“不正确的数据”、“填充错误”或者“RSA公钥未找到”。网上搜到的代码片段往往只告诉你“这么写就行”,但一旦环境稍有不同(比如密钥格式、填充方式),就直接掉坑里。

这个项目,就是把我从踩坑到填坑的全过程记录下来,聚焦于“JS加密,C#/Java解密”这个在Web开发中极其常见的场景。我会拆解每一步的技术细节,告诉你为什么别人的代码到你这就跑不通,以及如何构建一个健壮的、跨语言兼容的RSA加密解密方案。无论你是前端还是后端开发,只要遇到类似的需求,这篇内容都能给你一套可复现、可调试的解决方案。

2. 核心原理与跨语言兼容性拆解

2.1 RSA非对称加密到底在做什么?

很多人对RSA的理解停留在“公钥加密,私钥解密”这八个字,但真要实现跨语言操作,必须再往下深挖一层。RSA算法本质上是基于大数分解的难题。这里我们不推导数学公式,但需要理解几个直接影响编码结果的核心概念:

  1. 密钥对生成:首先会生成两个大质数p和q,计算它们的乘积n(这就是模数,Modulus)。然后根据欧拉函数等计算得到公钥指数e(通常固定为65537)和私钥指数d。公钥就是(n, e),私钥则是(n, d)以及p, q等更多信息。不同的密钥格式(如PKCS#1, PKCS#8)本质上就是用不同的方式包装这对(n, e)(n, d)

  2. 加密过程:加密时,并不是直接对原始字符串操作。程序会先将你的字符串(比如“hello123”)转换成一个大整数m(编码过程),然后计算密文c = m^e mod n。这个密文c也是一个很大的整数。

  3. 解密过程:解密方拿到密文c后,用自己的私钥计算m = c^d mod n,得到原始的大整数m,再将其解码回字符串。

跨语言问题的根源就藏在这些“编码”和“解码”的细节里。JS和C#/Java对于“如何将字符串变成整数m”、“加密后的整数c如何表示”以及“使用哪种填充方案”往往有着不同的默认实现。

2.2 为什么JS加密,C#解密经常失败?

失败的原因主要集中在以下三个方面,它们环环相扣:

2.2.1 密钥格式不匹配这是头号杀手。RSA密钥有多种格式标准:

  • PKCS#1:传统格式,公钥以-----BEGIN RSA PUBLIC KEY-----开头,私钥以-----BEGIN RSA PRIVATE KEY-----开头。它明确包含了RSA特有的参数。
  • PKCS#8:更通用的格式,公钥以-----BEGIN PUBLIC KEY-----开头,私钥以-----BEGIN PRIVATE KEY-----开头。它用一个统一的结构封装了不同算法的密钥。
  • XML格式:.NET Framework时代常用,是一段包含<Modulus>,<Exponent>,<D>等标签的XML字符串。
  • DER/PEM:这是编码方式。DER是二进制格式,PEM则是将DER进行Base64编码后,加上头尾标记的文本格式。

很多JS库(尤其是较老的或轻量级的)默认生成或只支持PKCS#1格式的PEM密钥。而C#的RSACryptoServiceProvider(旧版)或Java的RSA类,默认可能期望的是另一种格式。直接拿JS生成的PEM密钥去初始化C#的RSA对象,大概率会抛出“找不到公钥”或“无效的密钥格式”异常。

2.2.2 填充方案不一致RSA加密明文前,需要先进行填充(Padding),以达到安全性和兼容性要求。最常见的两种是:

  • PKCS#1 v1.5 Padding:这是老标准,应用广泛,但在某些情况下可能存在潜在风险。
  • OAEP Padding(最优非对称加密填充):更安全,是现代推荐的默认选择。

问题在于,JS库的默认填充可能是PKCS#1,而C#的RSA.Encrypt方法默认可能是OAEP(取决于版本和参数)。一个用PKCS#1填充加密的数据,用OAEP模式去解密,必然失败。

2.2.3 数据编码与转换问题即使密钥和填充都对上了,还有最后一道坎:数据是如何传递的?

  1. JS加密输出:JS加密后通常得到一个Base64字符串。这个Base64是加密后的二进制数据(那个大整数c的字节表示)的编码。
  2. C#/Java解密输入:C#解密方法RSA.Decrypt需要接收一个字节数组(byte[])。你需要将前端传过来的Base64字符串准确无误地转换回字节数组。
  3. 字符集问题:在将原始字符串转换为字节数组进行加密时,如果JS用的是UTF-8编码,而C#解密后默认用ASCIIGB2312去解码,对于中文等字符就会出现乱码。

实操心得:我曾遇到一个诡异的问题,前端加密英文数字都正常,一加密中文后端就解密失败。折腾半天才发现,前端JS用的是TextEncoder(UTF-8),而后端C#在将解密后的字节数组转字符串时,默认用了Encoding.Default(在中文系统上是GB2312)。解决方案就是前后端统一强制使用UTF-8进行编解码。

3. 实战:构建JS前端加密方案

3.1 库的选择与考量

前端JS进行RSA加密,主要有以下几个选择:

  1. jsencrypt:这是最流行、最易用的库之一。它封装得很好,API简单,默认使用PKCS#1 v1.5填充,非常适合快速上手。但这也意味着它的自定义灵活性较低,如果你想用OAEP填充,可能需要修改源码或寻找其他方案。
  2. node-rsa:如果你是在Node.js环境下,这个库功能更强大,支持PKCS#1和OAEP填充,支持多种密钥格式。但在纯浏览器端使用可能稍显笨重。
  3. Web Crypto API:这是现代浏览器原生的加密API,标准、安全、性能好。但它原生不支持直接导入PEM格式的密钥,需要先将PEM转换成CryptoKey对象,步骤稍繁琐,且IE兼容性差。
  4. crypto-js+ 自定义RSAcrypto-js主要擅长对称加密和哈希,其RSA支持有限,不推荐用于生产环境的非对称加密。

对于大多数Web应用,我推荐使用jsencrypt。它的简单性掩盖了其健壮性,社区活跃,遇到的问题基本都能搜到解决方案。下面我们就以它为例。

3.2 使用jsencrypt进行加密的完整流程

首先,你需要准备一个PEM格式的公钥。这个公钥应由后端(C#或Java)生成并提供给前端,通常通过一个安全的接口(如/api/getPublicKey)动态获取,而不是硬编码在JS里。

假设后端给你的公钥字符串是这样的(PKCS#1格式):

-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1...(省略)...AwIDAQAB -----END PUBLIC KEY-----

前端的加密代码如下:

<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/jsencrypt@3.3.2/bin/jsencrypt.min.js"></script> </head> <body> <script> // 1. 实例化JSEncrypt对象 const encryptor = new JSEncrypt(); // 2. 设置公钥 (这里假设从接口获取到了公钥字符串publicKeyPem) const publicKeyPem = `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1...AwIDAQAB -----END PUBLIC KEY-----`; encryptor.setPublicKey(publicKeyPem); // 3. 要加密的数据 const plainText = 'MySecretPassword123!@#'; // 4. 执行加密 const encryptedData = encryptor.encrypt(plainText); // 5. 输出结果 (是一个Base64字符串) console.log('加密后的数据:', encryptedData); // 在实际项目中,你将这个encryptedData通过Ajax/Fetch发送到后端 // fetch('/api/login', { method: 'POST', body: JSON.stringify({ encryptedData }) }) </script> </body> </html>

关键点与注意事项

  • jsencrypt.encrypt()方法内部已经帮你处理了填充(默认PKCS#1 v1.5)和输出格式(Base64),你只需要传入明文字符串。
  • 加密后的数据长度受密钥长度限制。一个2048位的RSA密钥,最多只能加密245字节(≈245个ASCII字符)的原始数据。如果你的数据更长,需要采用“RSA加密AES密钥,AES加密实际数据”的混合加密模式。这是另一个重要话题,本文聚焦于短数据(如密码、令牌)的加密。
  • 务必确保你设置的公钥字符串格式正确,头尾的-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----以及中间的换行符都不能少,否则setPublicKey会失败。

4. 实战:C#后端解密方案

4.1 .NET中的RSA类演进

在C#中处理RSA,经历了几个阶段:

  • .NET Framework:主要使用RSACryptoServiceProvider。这个类比较老旧,从.NET Framework 1.1就有了,它依赖于Windows的CryptoAPI。
  • .NET Core / .NET 5+:引入了新的RSA抽象基类及其实现RSA.Create(),以及更易用的RSA.Import...RSA.Export...系列方法。这是现代.NET开发的首选,跨平台支持更好。

强烈建议在新项目中使用新的RSA。下面的示例也基于此。

4.2 解密代码实现与逐行解析

假设前端通过POST请求,在Body中传来了一个JSON:{ "data": "前端加密后的Base64字符串" }

以下是ASP.NET Core Web API中的一个控制器方法示例:

using System; using System.Security.Cryptography; using System.Text; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; // 或用System.Text.Json [ApiController] [Route("api/[controller]")] public class DecryptController : ControllerBase { // 假设你的私钥以PEM格式存储在配置或环境变量中 private readonly string _privateKeyPem = @"-----BEGIN PRIVATE KEY----- MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALV...(省略私钥内容)... -----END PRIVATE KEY-----"; [HttpPost("decrypt")] public IActionResult DecryptData([FromBody] JObject payload) { try { // 1. 获取前端传来的加密数据(Base64字符串) string encryptedBase64 = payload["data"]?.ToString(); if (string.IsNullOrEmpty(encryptedBase64)) { return BadRequest("加密数据为空"); } // 2. 将Base64字符串转换为字节数组 // **这是关键一步,必须确保转换正确** byte[] encryptedDataBytes = Convert.FromBase64String(encryptedBase64); // 3. 使用RSA类进行解密 using (RSA rsa = RSA.Create()) { // 4. 导入私钥 // 这里需要根据私钥的格式选择导入方法。 // 如果私钥是PKCS#8格式的PEM(以BEGIN PRIVATE KEY开头),使用以下方法: rsa.ImportFromPem(_privateKeyPem.ToCharArray()); // 如果是PKCS#1格式的PEM(以BEGIN RSA PRIVATE KEY开头), // .NET 5+ 也支持直接导入。如果遇到问题,可以先将PCS#1转换为PKCS#8,或使用以下方法: // rsa.ImportRSAPrivateKey(Convert.FromBase64String(pemContentWithoutHeaders), out _); // 5. 执行解密 // 第一个参数:待解密的字节数组 // 第二个参数:填充模式,必须与前端加密时使用的模式一致! // jsencrypt默认使用PKCS#1 v1.5填充,所以这里也用这个。 // 如果前端用了OAEP,这里就需要用RSAEncryptionPadding.OaepSHA256等。 byte[] decryptedBytes = rsa.Decrypt(encryptedDataBytes, RSAEncryptionPadding.Pkcs1); // 6. 将解密后的字节数组转换为字符串 // **另一个关键点:编码必须与前端加密前的编码一致!** // 假设前端是UTF-8编码的字符串,这里也用UTF-8解码。 string originalText = Encoding.UTF8.GetString(decryptedBytes); // 7. 返回解密结果(实际项目中可能是验证密码等后续操作) return Ok(new { decrypted = originalText }); } } catch (FormatException) { return BadRequest("Base64数据格式无效"); } catch (CryptographicException ex) { // 最常见的异常:填充错误、密钥不匹配 return BadRequest($"解密失败: {ex.Message}"); } catch (Exception ex) { return StatusCode(500, $"服务器内部错误: {ex.Message}"); } } }

代码解析与避坑指南

  1. ImportFromPem方法:这是.NET 5及以上版本提供的便捷方法,可以直接导入PEM格式的密钥字符串。它自动识别PKCS#8和PKCS#1格式,非常省心。如果你的项目是.NET Core 3.1,可能需要使用ImportRSAPrivateKeyImportPkcs8PrivateKey等更底层的方法,并手动处理PEM的头尾标记和Base64解码。
  2. RSAEncryptionPadding.Pkcs1:这是与jsencrypt默认行为匹配的关键参数。如果你在这里错误地使用了OaepSHA1,解密一定会失败,并抛出CryptographicException: The parameter is incorrect
  3. 编码一致性Encoding.UTF8.GetString确保了与前端(通常使用UTF-8)编码一致。如果前端是其他编码(可能性较小),这里也需要相应调整。

4.3 如何生成并提供PEM格式的密钥对?

后端需要生成一对RSA密钥,私钥自己保存用于解密,公钥发给前端用于加密。以下是生成PEM格式密钥对的C#代码:

using System; using System.IO; using System.Security.Cryptography; public class RSAKeyGenerator { public static void GenerateAndSaveKeys(int keySize = 2048) { using (RSA rsa = RSA.Create(keySize)) { // 导出私钥 (PKCS#8格式的PEM) string privateKeyPem = rsa.ExportPkcs8PrivateKeyPem(); // .NET 7+ // 如果是.NET 5/6,可以使用以下方式: // byte[] privateKeyBytes = rsa.ExportPkcs8PrivateKey(); // string privateKeyPem = PemEncoding.WriteString("PRIVATE KEY", privateKeyBytes); // 导出公钥 (SPKI格式的PEM,这是PKCS#8的公钥格式) string publicKeyPem = rsa.ExportSubjectPublicKeyInfoPem(); // .NET 7+ // .NET 5/6: // byte[] publicKeyBytes = rsa.ExportSubjectPublicKeyInfo(); // string publicKeyPem = PemEncoding.WriteString("PUBLIC KEY", publicKeyBytes); // 保存到文件或数据库 File.WriteAllText("private_key.pem", privateKeyPem); File.WriteAllText("public_key.pem", publicKeyPem); Console.WriteLine("私钥已保存到 private_key.pem"); Console.WriteLine("公钥已保存到 public_key.pem"); Console.WriteLine("\n公钥内容(提供给前端):"); Console.WriteLine(publicKeyPem); } } }

注意事项:生成的私钥是最高机密,必须妥善保管(如存储在服务器的安全配置中心、环境变量或硬件安全模块中),绝对不要提交到代码仓库或发送给客户端。

5. 实战:Java后端解密方案

Java生态中,RSA加解密通常使用java.security包下的类。从Java 8到现代的Java 17,核心API变化不大,但使用方式略有不同。

5.1 使用Java进行解密的完整示例

假设你使用Spring Boot框架,以下是一个REST接口的解密示例:

import org.springframework.web.bind.annotation.*; import javax.crypto.Cipher; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; @RestController @RequestMapping("/api") public class DecryptionController { // 你的PEM格式私钥字符串(这里去掉了头尾和换行符,仅保留Base64内容) // 实际项目中应从安全配置中读取 private static final String PRIVATE_KEY_BASE64 = "MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBALV...(省略)..."; @PostMapping("/decrypt") public String decrypt(@RequestBody Map<String, String> request) throws Exception { String encryptedBase64 = request.get("data"); if (encryptedBase64 == null || encryptedBase64.isEmpty()) { throw new IllegalArgumentException("加密数据为空"); } // 1. 将Base64加密字符串解码为字节数组 byte[] encryptedData = Base64.getDecoder().decode(encryptedBase64); // 2. 将PEM私钥的Base64内容解码 byte[] keyBytes = Base64.getDecoder().decode(PRIVATE_KEY_BASE64); // 3. 创建PKCS#8密钥规范 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); // 4. 获取RSA密钥工厂并生成私钥对象 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // 5. 初始化Cipher为解密模式,并指定填充方式 // 与C#和JS对应,使用"RSA/ECB/PKCS1Padding" // 注意:这里的"ECB"是RSA加密的模式,对于非对称加密,ECB是唯一有效的模式,不用担心ECB的安全问题。 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); // 6. 执行解密 byte[] decryptedBytes = cipher.doFinal(encryptedData); // 7. 将解密后的字节数组转换为字符串(UTF-8编码) String originalText = new String(decryptedBytes, "UTF-8"); return "解密成功: " + originalText; } }

5.2 Java实现中的关键细节与差异

  1. 私钥格式处理:Java的PKCS8EncodedKeySpec期望的是PKCS#8格式的DER编码的私钥。这意味着你不能直接把带有-----BEGIN PRIVATE KEY-----的完整PEM字符串传给它。你需要先去掉PEM的头尾标记和换行符,只取中间的Base64部分,然后进行Base64解码,得到DER编码的字节数组。上面的示例中,PRIVATE_KEY_BASE64变量存储的就是这个“干净的”Base64内容。
  2. Cipher转换字符串Cipher.getInstance("RSA/ECB/PKCS1Padding")这个字符串是Java标准命名。
    • RSA:算法。
    • ECB:加密模式。对于分组密码(如AES),ECB是不安全的,但对于RSA这种非对称算法,它本质上是“无模式”,所以ECB在这里只是一个占位符,是标准写法。
    • PKCS1Padding:填充方案。这必须与前端jsencrypt的默认填充保持一致。
  3. 异常处理doFinal方法可能抛出BadPaddingException,这通常意味着填充错误,即你的密钥、填充模式或加密数据三者不匹配。IllegalBlockSizeException则可能表示数据块大小不对(例如,用2048位密钥解密了超过256字节的数据)。

如何将完整的PEM私钥转换为Java可用的格式?你可以写一个简单的工具方法来处理:

private static String cleanPemKey(String pemKey) { // 移除-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----以及所有换行符、空格 return pemKey.replaceAll("-----BEGIN PRIVATE KEY-----", "") .replaceAll("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); // 移除所有空白字符(空格、换行、制表符) } // 使用 String cleanBase64 = cleanPemKey(yourFullPemString); byte[] keyBytes = Base64.getDecoder().decode(cleanBase64);

6. 联调排错与常见问题实录

即使按照上面的步骤一步步来,第一次联调也难免遇到问题。下面是我在多个项目中总结出来的问题排查清单,按照从易到难的顺序检查,能解决95%以上的“JS加密,后端解密失败”问题。

6.1 问题排查四步法

第一步:检查密钥是否匹配

  • 症状:后端初始化RSA对象时直接报错,如“Invalid Key Format”、“找不到公钥”。
  • 排查
    1. 确认前端使用的公钥和后端用来解密的私钥是同一对密钥。重新生成一对密钥,确保前端和后端代码都使用全新的这一对。
    2. 确认密钥格式。如果后端是C# (.NET 5+),使用ImportFromPem,它兼容PKCS#1和PKCS#8的PEM。如果后端是Java,确保你提供给PKCS8EncodedKeySpec的是PKCS#8 DER的Base64(即去掉PEM头尾的纯Base64)。
    3. 实用技巧:用一个在线工具(如https://8gwifi.org/rsafunctions.jsp)做交叉验证。用你的公钥在该网站加密一个字符串,然后用你的私钥在同一网站解密。如果成功,说明密钥本身没问题,问题出在代码;如果失败,说明密钥对或格式有问题。

第二步:检查填充模式是否一致

  • 症状:后端解密时抛出CryptographicException(C#)或BadPaddingException(Java),提示填充错误。
  • 排查
    1. 前端jsencrypt默认使用PKCS#1 v1.5填充。除非你特意改过源码,否则就是这个。
    2. 后端C#RSA.Decrypt的第二个参数必须是RSAEncryptionPadding.Pkcs1
    3. 后端JavaCipher.getInstance必须是"RSA/ECB/PKCS1Padding"
  • 注意:OAEP填充更安全,但jsencrypt默认不支持。如果你想用OAEP,前端需要换库(如node-rsa或Web Crypto API),后端也要相应改为OaepSHA1OaepSHA256

第三步:检查数据传递与编码

  • 症状:解密出来的中文是乱码,或者解密过程不报错但得到一堆乱码。
  • 排查
    1. 网络传输:确保前端发送的加密Base64字符串完整、正确地被后端接收到。用浏览器的开发者工具或抓包工具(如Fiddler)查看实际发送的请求体,确认encryptedData这个字段的值是一个完整的、没有换行或截断的Base64字符串。
    2. Base64解码:在后端,确保使用正确的Base64解码方法。C#用Convert.FromBase64String,Java用Base64.getDecoder().decode。注意URL安全的Base64(-_)可能需要特殊处理,但jsencrypt输出的是标准Base64。
    3. 字符编码:这是乱码的根源。在JS端,字符串到字节的转换由jsencrypt内部处理,它通常使用UTF-8。在后端,解密得到字节数组后,必须用UTF-8解码。C#Encoding.UTF8.GetString(decryptedBytes)Javanew String(decryptedBytes, StandardCharsets.UTF_8)

第四步:检查数据长度限制

  • 症状:加密短文本正常,加密长文本失败。JS端可能无错误,但后端解密时报IllegalBlockSizeException(Java)或解密出空数据。
  • 排查:记住这个公式:RSA能加密的最大数据长度(字节)= 密钥长度(位)/ 8 - 填充开销。对于PKCS#1 v1.5填充,开销是11字节。
    • 2048位密钥:2048/8 - 11 = 256 - 11 = 245字节。
    • 如果你的明文超过245字节(约245个英文字符,中文UTF-8下更少),就需要采用混合加密:用RSA加密一个随机生成的AES密钥,再用这个AES密钥去加密你的长数据。

6.2 常见错误速查表

错误现象 (后端)可能原因解决方案
CryptographicException: Bad Data.(C#)1. 密钥不匹配(不是一对)
2. 填充模式不一致
3. 加密数据被篡改或传输错误
1. 重新生成并同步密钥对
2. 确认前后端填充均为PKCS#1
3. 检查网络传输数据完整性
BadPaddingException(Java)同上,特别是填充模式不一致确认Java Cipher实例化为RSA/ECB/PKCS1Padding
Invalid Key Format1. 密钥格式错误(如将PKCS#1 PEM给了需要PKCS#8的Java)
2. PEM头尾标记不正确或含有非法字符
1. 使用正确的导入方法或转换密钥格式
2. 清理PEM字符串,确保格式正确
解密结果乱码前后端字符串编解码不一致前后端统一使用UTF-8进行编解码
加密长文本失败明文长度超过RSA加密上限采用RSA+AES混合加密方案
JS加密时报错Message too long同上,明文长度超限同上,或在前端进行数据分块(不推荐,混合加密是标准做法)

6.3 一个终极调试技巧:日志记录与对比

当所有常规检查都无效时,可以启用最详细的日志对比。

  1. 在JS端:在调用encrypt之前,将你的明文字符串,用TextEncoder转换成Uint8Array,然后打印或记录这个字节数组的16进制字符串。同时,记录下生成的加密Base64字符串。

    const textEncoder = new TextEncoder(); const plainBytes = textEncoder.encode(plainText); console.log('明文字节(Hex):', Array.from(plainBytes).map(b => b.toString(16).padStart(2, '0')).join(' ')); console.log('加密后(Base64):', encryptedData);
  2. 在后端(以C#为例):在解密前,记录接收到的Base64字符串,并将其解码后的字节数组也以16进制打印出来。解密后,同样打印解密出的字节数组。

    Console.WriteLine($"收到Base64: {encryptedBase64}"); Console.WriteLine($"解密前字节(Hex): {BitConverter.ToString(encryptedDataBytes).Replace("-", " ")}"); // ... 解密操作 ... Console.WriteLine($"解密后字节(Hex): {BitConverter.ToString(decryptedBytes).Replace("-", " ")}");
  3. 对比

    • 对比JS和C#日志中的“明文字节”。如果不一致,说明编码问题。
    • 确保JS输出的加密Base64,与C#收到的完全一致。如果不一致,说明网络传输过程中数据可能被修改。
    • 如果两者都一致,但解密还是失败,那几乎可以肯定是密钥或填充模式的问题了。

通过这种“白盒化”的调试,你可以把问题定位到最小的环节。这套跨语言的RSA加解密方案,核心就在于对细节的掌控。一旦打通,它就会成为你项目中一个稳定可靠的安全基石。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询