我有以下Java代码:
byte[] signatureBytes = getSignature();
String signatureString = new String(signatureBytes, "UTF8");
byte[] signatureStringBytes = signatureString.getBytes("UTF8");
System.out.println(signatureBytes.length == signatureStringBytes.length); // prints false
问:我可能误解了这一点,但我认为new String(byte[] bytes, String charset)
和String.getBytes(charset)
是反向操作?
问:作为后续工作,将byte []数组作为字符串传输的安全方法是什么?
答案 0 :(得分:8)
并非每个byte[]
都是有效的UTF-8。默认情况下,无效序列会被固定字符替换,我认为这是长度更改的原因。
尝试使用Latin-1,它不应该发生,因为它是一个简单的编码,每个byte[]
都是有意义的。
Windows-1252都不应该发生。那里有未定义的序列(事实上是未定义的字节),但所有的字符都在一个字节中编码。新byte[]
可能与原始{{1}}不同,但它们的长度必须相同。
答案 1 :(得分:5)
我可能误解了这一点,但我认为新的String(byte [] bytes,String charset)和String.getBytes(charset)是反向操作?
不一定。
如果输入字节数组包含无效UTF-8的序列,则初始转换可能会将它们转换为(例如)问号。第二个操作然后将这些转换为UTF-8编码的'?'
字符....与原始表示不同。
Unicode中的某些字符确实有多种表示形式;例如重音字符可以是单个代码点,也可以是基本字符代码点和重音代码点。但是,在字节数组(包含有效的UTF-8)和String之间来回转换应该保留代码点序列。它没有执行任何"规范化"。
那么将byte []数组作为String传输的安全方法是什么呢?
最安全的替代方法是base64编码字节数组。这样做的另一个好处是,字符串中的字符可以在转换为任何可以表示拉丁字母和数字的字符集/编码后继续使用。
另一种选择是使用Latin-1而不是UTF-8。但是:
答案 2 :(得分:2)
有两种可能性浮现在脑海中。
首先,您的签名不是完全有效的UTF8。你不能只取任意二进制数据和" string"它。并非每一个比特都定义了一个合法的字符。 String构造函数将为二进制数据插入一些默认的替换内容,这些内容实际上并不意味着' UTF8中的任何内容。这不是一个可逆的过程。如果你想要" String"一些任意二进制数据,你需要使用已建立的方法这样做,我建议org.apache.commons.codec.binary.Base64
还有一些角色有多个表示。例如,带有重音符号的东西可以编码为重音字符,或者作为字符加上后面的重音来组合。在编码之间来回移动时,无法保证这是一个可逆的过程。