java、python 和 javascript 应用程序之间的 Base64 UTF-16 编码
Base64 UTF-16 encoding between java, python and javascript applications
作为示例,我有以下字符串,我认为它采用 utf-16 编码:"hühühüh"。
在python中编码
时得到以下结果
>>> base64.b64encode("hühühüh".encode("utf-16"))
b'//5oAPwAaAD8AGgA/ABoAA=='
在java中:
>>> String test = "hühühüh";
>>> byte[] encodedBytes = Base64.getEncoder().encode(test.getBytes(StandardCharsets.UTF_16));
>>> String testBase64Encoded = new String(encodedBytes, StandardCharsets.US_ASCII);
>>> System.out.println(testBase64Encoded);
/v8AaAD8AGgA/ABoAPwAaA==
在java脚本中,我根据Mozilla dev guideline定义了一个二进制编码函数,然后对相同的字符串进行编码。
>> function toBinary(string) {
const codeUnits = new Uint16Array(string.length);
for (let i = 0; i < codeUnits.length; i++) {
codeUnits[i] = string.charCodeAt(i);
}
return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
}
>> atob(toBinary("hühühüh"))
aAD8AGgA/ABoAPwAaAA=
如您所见,每个编码器都创建了一个不同的 base64 字符串。因此,让我们再次反转编码。
在 Python 中,所有生成的字符串再次解码正常:
>>> base64.b64decode("//5oAPwAaAD8AGgA/ABoAA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("/v8AaAD8AGgA/ABoAPwAaA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("aAD8AGgA/ABoAPwAaAA=").decode("utf-16")
'hühühüh'
在 java 脚本中再次根据 Mozilla dev guideline:
使用 fromBinary 函数
>>> function fromBinary(binary) {
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
console.log(...bytes)
return String.fromCharCode(...new Uint16Array(bytes.buffer));
}
>>> fromBinary(window.atob("//5oAPwAaAD8AGgA/ABoAA=="))
"\ufeffhühühüh"
>>> fromBinary(window.atob("/v8AaAD8AGgA/ABoAPwAaA=="))
"\ufffe栀ﰀ栀ﰀ栀ﰀ栀"
>>> fromBinary(window.atob("aAD8AGgA/ABoAPwAaAA="))
"hühühüh"
最后在 Java:
>>> String base64Encoded = "//5oAPwAaAD8AGgA/ABoAA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "/v8AaAD8AGgA/ABoAPwAaA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "aAD8AGgA/ABoAPwAaAA=";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println("Decoded" + base64Decoded);
hühühüh
我们可以看到 python 的 base64 解码器能够为其他两个解析器编码和解码消息。但是 Java 和 Java 脚本解析器之间的定义似乎彼此不兼容。我不明白这是为什么。
这是 Java 和 Java 脚本中 base64 库的问题吗?如果是,是否有其他工具或路由让我们在 Java 和 Java脚本应用?如何使用尽可能接近核心语言功能的工具确保 Java 和 Javscript 应用程序之间安全的 base64 字符串传输?
编辑:
正如接受的答案中所说,问题是不同的 utf16 编码。 Java 和 Javascript 之间的兼容性问题可以通过在 Javascript 中以相反的顺序生成 utf16 字节来解决,或者接受编码字符串作为 StandardCharsets.UTF_16LE
.
问题是 UTF-16
有 4 个变体。
此字符编码每个代码单元使用两个字节。这两个字节中的哪一个应该先出现?这会创建两个变体:
- UTF-16BE 首先存储最高有效字节。
- UTF-16LE 首先存储最低有效字节。
为了区分这两者,在文本的开头有一个可选的 "byte order mark" (BOM) 字符 U+FEFF。因此,带 BOM 的 UTF-16BE 以字节 fe ff
开头,而带 BOM 的 UTF-16LE 以 ff fe
开头。由于 BOM 是可选的,它的存在使可能的编码数量加倍。
看起来您正在使用 4 种可能编码中的 3 种:
- Python 使用带 BOM 的 UTF-16LE
- Java 使用带 BOM 的 UTF-16BE
- Java脚本使用无 BOM 的 UTF-16LE
人们更喜欢 UTF-8 而不是 UTF-16 的原因之一是为了避免这种混淆。
作为示例,我有以下字符串,我认为它采用 utf-16 编码:"hühühüh"。
在python中编码
时得到以下结果>>> base64.b64encode("hühühüh".encode("utf-16"))
b'//5oAPwAaAD8AGgA/ABoAA=='
在java中:
>>> String test = "hühühüh";
>>> byte[] encodedBytes = Base64.getEncoder().encode(test.getBytes(StandardCharsets.UTF_16));
>>> String testBase64Encoded = new String(encodedBytes, StandardCharsets.US_ASCII);
>>> System.out.println(testBase64Encoded);
/v8AaAD8AGgA/ABoAPwAaA==
在java脚本中,我根据Mozilla dev guideline定义了一个二进制编码函数,然后对相同的字符串进行编码。
>> function toBinary(string) {
const codeUnits = new Uint16Array(string.length);
for (let i = 0; i < codeUnits.length; i++) {
codeUnits[i] = string.charCodeAt(i);
}
return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
}
>> atob(toBinary("hühühüh"))
aAD8AGgA/ABoAPwAaAA=
如您所见,每个编码器都创建了一个不同的 base64 字符串。因此,让我们再次反转编码。
在 Python 中,所有生成的字符串再次解码正常:
>>> base64.b64decode("//5oAPwAaAD8AGgA/ABoAA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("/v8AaAD8AGgA/ABoAPwAaA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("aAD8AGgA/ABoAPwAaAA=").decode("utf-16")
'hühühüh'
在 java 脚本中再次根据 Mozilla dev guideline:
使用 fromBinary 函数>>> function fromBinary(binary) {
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
console.log(...bytes)
return String.fromCharCode(...new Uint16Array(bytes.buffer));
}
>>> fromBinary(window.atob("//5oAPwAaAD8AGgA/ABoAA=="))
"\ufeffhühühüh"
>>> fromBinary(window.atob("/v8AaAD8AGgA/ABoAPwAaA=="))
"\ufffe栀ﰀ栀ﰀ栀ﰀ栀"
>>> fromBinary(window.atob("aAD8AGgA/ABoAPwAaAA="))
"hühühüh"
最后在 Java:
>>> String base64Encoded = "//5oAPwAaAD8AGgA/ABoAA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "/v8AaAD8AGgA/ABoAPwAaA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "aAD8AGgA/ABoAPwAaAA=";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println("Decoded" + base64Decoded);
hühühüh
我们可以看到 python 的 base64 解码器能够为其他两个解析器编码和解码消息。但是 Java 和 Java 脚本解析器之间的定义似乎彼此不兼容。我不明白这是为什么。 这是 Java 和 Java 脚本中 base64 库的问题吗?如果是,是否有其他工具或路由让我们在 Java 和 Java脚本应用?如何使用尽可能接近核心语言功能的工具确保 Java 和 Javscript 应用程序之间安全的 base64 字符串传输?
编辑:
正如接受的答案中所说,问题是不同的 utf16 编码。 Java 和 Javascript 之间的兼容性问题可以通过在 Javascript 中以相反的顺序生成 utf16 字节来解决,或者接受编码字符串作为 StandardCharsets.UTF_16LE
.
问题是 UTF-16
有 4 个变体。
此字符编码每个代码单元使用两个字节。这两个字节中的哪一个应该先出现?这会创建两个变体:
- UTF-16BE 首先存储最高有效字节。
- UTF-16LE 首先存储最低有效字节。
为了区分这两者,在文本的开头有一个可选的 "byte order mark" (BOM) 字符 U+FEFF。因此,带 BOM 的 UTF-16BE 以字节 fe ff
开头,而带 BOM 的 UTF-16LE 以 ff fe
开头。由于 BOM 是可选的,它的存在使可能的编码数量加倍。
看起来您正在使用 4 种可能编码中的 3 种:
- Python 使用带 BOM 的 UTF-16LE
- Java 使用带 BOM 的 UTF-16BE
- Java脚本使用无 BOM 的 UTF-16LE
人们更喜欢 UTF-8 而不是 UTF-16 的原因之一是为了避免这种混淆。