
C# 与 Java 的 Base64 编码结果不同,并非编码逻辑问题,而是 JSON 序列化时对特殊字符(如斜杠 /)的转义策略不同所致:Java 的 JSONObject.toString() 默认转义 / 为 /,而 .NET 的 System.Text.Json.JsonSerializer 默认不转义,导致原始字节流不同,Base64 结果自然不同。
c# 与 java 的 base64 编码结果不同,并非编码逻辑问题,而是 json 序列化时对特殊字符(如斜杠 `/`)的转义策略不同所致:java 的 `jsonobject.tostring()` 默认转义 `/` 为 `/`,而 .net 的 `system.text.json.jsonserializer` 默认不转义,导致原始字节流不同,base64 结果自然不同。
在 JWT、API 签名或跨语言数据交换场景中,确保 Base64 编码一致性至关重要。表面上看,C# 和 Java 都使用 UTF-8 编码 + Base64 编码,但问题根源在于 JSON 字符串生成阶段——即 claimSet.toString() 与 JsonSerializer.Serialize() 的行为差异。
? 根本原因分析
Java(org.json.JSONObject):
toString() 方法会自动对 U+002F (/) 进行转义,输出 {"partnerUrl":"https://test.com/testapply/abc/signup"}。
因此字节数组包含额外的反斜杠 (ASCII 0x5C),导致 Base64 编码结果更长(末尾多一个 = 补位)。C#(System.Text.Json):
默认配置下 JsonSerializer.Serialize() 不对 / 转义,生成 {"partnerUrl":"https://test.com/testapply/abc/signup"},更紧凑、符合 RFC 7159 推荐实践。
验证方式(解码后对比原始 JSON):
# Java 输出解码后(含转义)
echo "eyJwYXJ0bmVyVXJsIjoiaHR0cHM6XC9cL3Rlc3QuY29tXC90ZXN0YXBwbHlcL2FiY1wvc2lnbnVwIn0=" | base64 -d
# → {"partnerUrl":"https://test.com/testapply/abc/signup"}
# C# 输出解码后(无转义)
echo "eyJwYXJ0bmVyVXJsIjoiaHR0cHM6Ly90ZXN0LmNvbS90ZXN0YXBwbHkvYWJjL3NpZ251cCJ9" | base64 -d
# → {"partnerUrl":"https://test.com/testapply/abc/signup"}✅ 解决方案:统一 JSON 序列化行为
方案一:让 C# 兼容 Java 的转义行为(推荐用于对接遗留 Java 系统)
启用 JsonSerializerOptions.EscapeHtml = false 并手动转义 /(注意:EscapeHtml 控制的是 <, >, & 等,不影响 /):
✅ 正确做法:使用 JsonSerializerOptions 配置 Encoder,强制转义 /:
using System.Text.Encodings.Web;
using System.Text.Json;
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(
UnicodeRanges.All, // 包含所有字符
new[] { '/', '\' } // 显式要求转义 / 和
),
WriteIndented = false
};
var claimSets = new Dictionary<string, object>
{
{ "partnerUrl", "https://test.com/testapply/abc/signup" }
};
string claimSetsJson = JsonSerializer.Serialize(claimSets, options);
byte[] bytes = Encoding.UTF8.GetBytes(claimSetsJson);
string base64 = Convert.ToBase64String(bytes);
// 输出:eyJwYXJ0bmVyVXJsIjoiaHR0cHM6XC9cL3Rlc3QuY29tXC90ZXN0YXBwbHlcL2FiY1wvc2lnbnVwIn0=⚠️ 注意:JavaScriptEncoder.Create(...) 中传入 new[] { '/' } 即可,无需 \;.NET 6+ 支持该语法,低版本需自定义 TextEncoder。
方案二:修改 Java 端(更优,符合现代 JSON 规范)
禁用 JSONObject 对 / 的自动转义(需升级到 org.json:json:20231013 或更高版本):
// Java 2023+ 版本支持
JSONObject claimSet = new JSONObject();
claimSet.put("partnerUrl", "https://test.com/testapply/abc/signup");
String claimSetJson = claimSet.toString(0, 0, false); // 第三个参数:escapeForwardSlash = false
// 或使用 JSONObject.toString() 后手动替换(不推荐)或改用 Jackson/Gson 等更可控的库:
ObjectMapper mapper = new ObjectMapper(); mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, false); String json = mapper.writeValueAsString(claimSet); // 默认不转义 /
? 关键总结
- ✅ Base64 编码本身无语言差异,差异永远源于输入字节流是否一致;
- ✅ JSON 序列化器的默认转义策略(尤其是 /)是常见陷阱,务必显式对齐;
- ✅ 生产环境建议:双方均关闭 / 转义(RFC 合规、体积更小、解析更高效);
- ❌ 不要依赖 Encoding.UTF8.GetBytes() 前后字符串“看起来一样”——必须逐字节比对或解码验证。
通过统一序列化配置,即可实现 C# 与 Java 生成完全一致的 Base64 字符串,确保跨语言签名、JWT header/payload 或密钥派生等场景的可靠性。


















