Java和.NET:Base64转换混淆

6
我很难将文本转换为Java(Android)和.NET(Visual Basic)中的Base64字符串。 ASCII字符的普通(可读)形式可以成功转换。但是,当涉及特殊字符(代码大于128的字符)时,它们会给我带来麻烦。
例如,我尝试转换ASCII值为65的字符代码(字符“A”)。
我的Java代码如下:
char a = 65;
String c = String.valueOf(a); 
byte bt[] = c.getBytes();               
String result = Base64.encodeToString(bt, Base64.DEFAULT);

我的.NET代码如下:

Dim c As String = Chr(65)
Dim result as String = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(c))

这两个都返回相同的结果:"QQ=="。这很好。但是当我尝试转换一个特殊字符,例如字符代码153时,它会返回不同的结果。

char a = 153;
String c = String.valueOf(a);               
byte bt[] = c.getBytes();               
String result = Base64.encodeToString(bt, Base64.DEFAULT);

这将返回 "wpk=",以下是相同的.NET代码:
Dim c As String = Chr(153) 
Dim result as String = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(c))

这将返回"4oSi"

这很奇怪。这里有什么问题。我在两个平台上都使用了本地的Base64库。我的代码有什么问题吗?


6
C#使用UTF8,而Java使用Unicode。建议在两者中都使用Unicode。请尝试更改编码方式。 - Wug
顺便问一下,你认为字符153代表什么?它在Unicode中是不可打印的。在ISO-8859-1中,它是商标(TM)。 - Guido Simone
我尝试在两个平台上使用Unicode,但仍存在差异。.NET现在返回"IiE=",而Java仍然返回"wpk="。是的,153可能是商标符号。但我的代码只是尝试通过混淆字符代码来执行加密,使它们在0-255之间变化。然后为了安全地通过互联网传输,我需要将其转换为Base64。 - Faraz Azhar
1个回答

11

由于您编码的数据是加密数据,其中任何字节都可以从0到255,并且在其加密状态下没有字符或文本含义,因此您需要将此信息视为“真二进制”数据。Java和.NET都通过各自的字节数组原语对真二进制数据提供完全支持。

正如您所知,Base64编码是将真二进制数据(范围为0到255)转换为略大的二进制数据数组(其中每个字节保证具有与ASCII可打印字符相同的值,介于32和126之间的某个位置)。让我们称这个编码后的二进制数据为“编码二进制数据”。然后,“编码二进制数据”可以安全地转换为文本,因为几乎所有已知的字符集都同意可打印的ASCII字符集(32到126)。

因此,Java和VB.NET代码片段的主要问题是您尝试使用文本原语-char和String在Java中;在VB.NET中使用String存储“真二进制”数据。一旦这样做,就太晚了。没有可靠的方法将其转换回字节数组,因为文本原语不是设计用于安全地存储和检索二进制数据的。有关此原因的更多信息,请阅读《软件开发人员绝对必须知道的有关Unicode和字符集的最绝对最小限度(不要找借口!)》

幸运的是,解决方法很简单。对于Java,请勿使用char和String存储二进制数据。将数据直接放入字节数组中。尝试以下内容:

  byte [] bt = new byte[1];
  bt[0] = (byte) 153;
  String result = Base64.encodeToString(bt, Base64.DEFAULT);

我得到了mQ==

在VB.NET中,概念上的解决方法是相同的。不要使用String类型,而要使用一个字节数组。

    Dim bytes() As Byte = New Byte() {153}
    Dim result As String = Convert.ToBase64String(bytes)

再次强调,答案是mQ==

最后,在编码之后,使用字符串是完全可以的。你的字符在ASCII子集中,任何字符串和字节数组之间的转换都不会损坏数据,因为所有字符集都同意ASCII子集。

请记住,如果反向操作 - 解码,你将解码为字节数组,此时你将回到真正的二进制形式。从这一点开始,直到完成解密过程并恢复原始明文之前,数据绝不能以字符串形式存储。

希望这有所帮助。


System.Text.Encoding.UTF8.GetBytes(c) 在 .NET 中返回一个字节数组。你关于 Java 的描述听起来是正确的。 - faester
@Guido,你的代码似乎可以工作。我不太擅长Java,所以我猜我在将字符串转换为字节再转换为Base64时弄错了。我的目标是,我有一个在XML数据上运行的Android应用程序。所以它都是纯文本数据。然后我设置了一个带有ASP.NET的Web服务器。我的Android应用程序是使用自己的加密代码加密XML文件数据,这就是为什么像153这样的字符也会出现的原因。然后将其转换为Base64并传输。然后在基于.NET的Web服务器上接收它并从Base64解码并解密它。 - Faraz Azhar
@FarazAzhar - 感谢您的澄清。我已根据更好地理解您试图解决的问题更新了答案。 - Guido Simone
@GuidoSimone,这真的澄清了问题。我明白了。我尝试将非可打印二进制数据存储到字符串变量中,所以它们在这些字符串变量中被丢失或损坏,导致笨拙的B64转换。谢谢! 还有谢谢提供的链接! :o) - Faraz Azhar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接