Кодировка Base64 UTF-16 между приложениями java, python и javascript - PullRequest
1 голос
/ 06 апреля 2020

В качестве примера у меня есть следующая строка, которая, как я предполагаю, находится в кодировке 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==

В javascript Я определяю функцию двоичного кодирования согласно директиве разработчика Mozilla и затем кодировать ту же строку.

>> 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'

В javascript снова с использованием функции fromBinary согласно Mozilla dev guideline :

>>> 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

Мы видим, что декодер base64 python способен кодировать и декодировать сообщения для и от двух других парсеров. Но определения между парсерами Java и Javascript, похоже, не совместимы друг с другом. Я не понимаю, почему это так. Это проблема с библиотеками base64 в Java и Javascript и, если да, существуют ли другие инструменты или маршруты, которые позволяют передавать строки utf-16 в кодировке base64 между приложениями Java и Javascript? Как я могу обеспечить безопасный перенос строки base64 между Java и приложениями Javscript, используя инструменты, максимально приближенные к функциональности основного языка?

РЕДАКТИРОВАТЬ: Как сказано в принятом ответе, проблема в различных кодировках utf16. Проблема совместимости между Java и Javascript может быть решена путем генерирования байтов utf16 в Javascript в обратном порядке или принятия кодированной строки как StandardCharsets.UTF_16LE.

1 Ответ

4 голосов
/ 06 апреля 2020

Проблема в том, что существует 4 варианта UTF-16.

Эта кодировка символов использует два байта на единицу кода. Какой из двух байтов должен стоять первым? Это создает два варианта:

  • UTF-16BE сначала сохраняет старший значащий байт.
  • UTF-16LE сначала сохраняет младший значащий байт.

Кому чтобы показать разницу между этими двумя значениями, в начале текста есть необязательный символ «знак порядка байтов» (UOM FEFF), U + FEFF. Таким образом, UTF-16BE с спецификацией начинается с байтов fe ff, а UTF-16LE с спецификацией начинается с ff fe. Поскольку спецификация является необязательной, ее присутствие удваивает число возможных кодировок.

Похоже, вы используете 3 из 4 возможных кодировок:

  • Python использовали UTF-16LE с Спецификация
  • Java используется UTF-16BE с спецификацией
  • JavaScript используется UTF-16LE без спецификации

Одна из причин, по которой люди предпочитают UTF-8 UTF-16, чтобы избежать этой путаницы.

...