我想存储一个包裹在String对象中的字节数组。这是场景
这是一段代码,描述了我在说什么。
String s = "test123";
byte[] a = s.getBytes();
byte[] b = env.encrypt(a);
String t = new String(b);
byte[] c = t.getBytes();
byte[] d = env.decrypt(c);
env.encrypt()和env.decrypt()执行加密和解密。我遇到的问题是b数组的长度为8,c数组的长度为16.我认为它们是相等的。这里发生了什么?我试图修改下面的代码
String s = "test123";
Charset charset = Charset.getDefaultCharset();
byte[] a = s.getBytes(charset);
byte[] b = env.encrypt(a);
String t = new String(b, charset);
byte[] c = t.getBytes(charset);
byte[] d = env.decrypt(c);
但这没有帮助。
有什么想法吗?
答案 0 :(得分:16)
将二进制数据存储在String对象中并不是一个好主意。你最好使用类似Base64编码的东西,它旨在将二进制数据转换成可打印的字符串,并且是完全可逆的。
事实上,我刚刚找到了一个用于Java的公共域base64编码器:http://iharder.sourceforge.net/current/java/base64/
答案 1 :(得分:11)
有些人指出这不是String(byte[])
构造函数的正确用法。重要的是要记住,在Java中,String
由字符组成,恰好是16位,而不是8位,如字节所示。你也忘记了字符编码。请记住,角色通常不是一个字节。
让我们一点一点地分解它:
String s = "test123";
byte[] a = s.getBytes();
此时,如果系统的默认字符编码为Windows-1252
或iso-8859-1
或UTF-8
,则字节数组很可能包含8个字节。
byte[] b = env.encrypt(a);
现在b
包含一些看似随机的数据,具体取决于您的加密,甚至不能保证一定的长度。许多加密引擎填充输入数据,以便输出匹配特定的块大小。
String t = new String(b);
这是取随机字节并要求Java将它们解释为字符数据。这些字符可能显示为乱码,并且某些位序列对于每个编码都不是有效字符。 Java尽职尽责并创建一系列16位字符。
byte[] c = t.getBytes();
这可能会也可能不会提供与b
相同的字节数组,具体取决于编码。您在问题描述中声明您看到c
长度为16个字节;这可能是因为t中的垃圾在默认字符编码中转换不好。
byte[] d = env.decrypt(c);
这不起作用,因为c
不是您期望的数据,而是腐败。
解决方案:
获取字节数组数据并使用Base64或十六进制数字对其进行编码并存储该字符串:
byte[] cypherBytes = env.encrypt(getBytes(plainText));
StringBuffer cypherText = new StringBuffer(cypherBytes.length * 2);
for (byte b : cypherBytes) {
String hex = String.format("%02X", b); //$NON-NLS-1$
cypherText.append(hex);
}
return cypherText.toString();
字符编码:
用户的密码可能不是ASCII,因此您的系统容易出问题,因为您没有指定编码。
比较
String s = "tést123";
byte[] a = s.getBytes();
byte[] b = env.encrypt(a);
与
String s = "tést123";
byte[] a = s.getBytes("UTF-8");
byte[] b = env.encrypt(a);
字节数组a
与UTF-8
编码的值不同于系统默认值(除非您的系统默认值为UTF-8
)。只要A)你是一致的并且B)你的编码可以代表你的数据的所有允许字符,你使用什么编码并不重要。您可能无法以系统默认编码存储中文文本。如果您的应用程序曾部署在多台计算机上,并且其中一台计算机具有不同的系统默认编码,则在一个系统上加密的密码将在另一台系统上变得乱码。
故事的道德: 字符不是字节,字节不是字符。你必须记住你正在处理的是什么,以及如何在它们之间来回转换。
答案 2 :(得分:4)
在这两种情况下,您使用的是操作系统默认的非Unicode字符集(取决于区域设置)。如果您将字符串从一个系统传递到另一个系统,则它们可能具有不同的语言环境,因此具有不同的默认字符集。您需要使用一个明确定义的字符集来执行您要执行的操作;例如ISO-8859-1。
更好的是,不要进行转换,直接传递byte[]
数组。
答案 3 :(得分:3)
这有点滥用String(byte [])构造函数和相关方法。
这适用于某些编码,并且与其他编码失败。据推测,您平台的默认编码是其失败的编码之一。
您应该使用类似Commons Code c的内容将这些字节转换为十六进制或base64。
另外,为什么要加密密码而不是用盐来加密密码?
答案 4 :(得分:2)
实现一个StringWrapper类,其构造函数接受一个String arg并将其转换为byte []。使用“ISO-8859-1”编码来确保每个字符只有8位而不是16位。显然,您可以使用编码/解码方法来操作这些字节。
答案 5 :(得分:1)
这不能正常工作。将字节存储为字符串只能用于ascii集(以及其他一些集合)。如果您需要将加密结果存储为String,那么将字节转换为十六进制然后将其放入String中。那会有用。
我建议您将密码保留为字节。没有理由将它存储为字符串(除非您想查看人们的密码是什么)。
答案 6 :(得分:0)
我没有给你一个确定的答案,但如果我正在研究这个问题,我会在每一步打印出字符串或字节,然后比较它们以查看发生了什么。另外,b包含来自env.encrypt的返回值,但c是来自.getBytes的返回值,因此在某种程度上,您将苹果与橙子进行比较。