Base64 и Base64url: различия и конвертация

Base64 — это способ представления бинарных данных в виде ASCII-строки. Он используется там, где требуется передавать или хранить произвольные байты в текстовой форме: в HTTP-заголовках, XML, JSON, JWT, электронных письмах.

Принцип основан на разбиении входных данных на группы по 3 байта (24 бита). Далее эти 24 бита делятся на 4 группы по 6 бит. Каждая 6-битная группа кодируется символом из таблицы Base64 (64 возможных символа).

Алфавит Base64:

  • A–Z (0–25)
  • a–z (26–51)
  • 0–9 (52–61)
  • / (63)

Если количество байт не кратно 3, используется символ заполнения =.

Пример логики:

  • 1 байт → 2 символа + ==
  • 2 байта → 3 символа + =
  • 3 байта → 4 символа без padding

Padding нужен только для выравнивания длины до кратности 4.


Base64url: модификация для безопасной передачи в URL

Base64url — это вариант Base64, адаптированный для использования в URL и HTTP-параметрах.

Основные отличия:

  • + заменяется на -
  • / заменяется на _
  • символ = (padding) часто удаляется

Причина появления Base64url — конфликт символов + и / с URL-энкодингом. В URL эти символы могут интерпретироваться специальным образом, поэтому был создан безопасный вариант.

Сравнение:

Base64 Base64url
A+/9 A-_9
+ / - _
= (обычно убирается)

Padding и его роль в совместимости

Символ = в Base64 выполняет исключительно выравнивающую функцию. Он не несёт данных.

В Base64url часто padding удаляют полностью, но это создаёт важный нюанс:

  • некоторые реализации требуют восстановить padding перед декодированием
  • длина строки должна быть кратна 4 для классического Base64-декодера

Формула восстановления padding:

если длина % 4 = 2 → добавить "=="
если длина % 4 = 3 → добавить "="
если длина % 4 = 0 → ничего не добавлять

Работа с Base64 и Base64url в Jsrsasign

Библиотека Jsrsasign предоставляет набор утилит для кодирования и декодирования в модуле KJUR.crypto.Util.

Основные методы:

  • hextob64(hex) — HEX → Base64
  • b64tohex(b64) — Base64 → HEX
  • hextob64u(hex) — HEX → Base64url
  • b64utohex(b64u) — Base64url → HEX

Также часто используются вспомогательные функции:

  • KJUR.crypto.Util.utf8tob64u(str)
  • KJUR.crypto.Util.b64utoutf8(b64u)

Конвертация Base64 ↔︎ Base64url в Jsrsasign

HEX → Base64 и Base64url

const hex = "48656c6c6f"; // "Hello"

const b64 = KJUR.crypto.Util.hextob64(hex);
const b64url = KJUR.crypto.Util.hextob64u(hex);

console.log(b64);
console.log(b64url);

Base64 → Base64url вручную

Jsrsasign не всегда предоставляет прямую конвертацию Base64 → Base64url как отдельную функцию, но преобразование выполняется заменой символов:

function base64ToBase64url(b64) {
  return b64
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

Base64url → Base64

Обратное преобразование требует восстановления символов и padding:

function base64urlToBase64(b64url) {
  let b64 = b64url
    .replace(/-/g, "+")
    .replace(/_/g, "/");

  while (b64.length % 4 !== 0) {
    b64 += "=";
  }

  return b64;
}

Декодирование Base64url в Jsrsasign

Jsrsasign предоставляет готовую функцию:

const hex = KJUR.crypto.Util.b64utohex("SGVsbG8");
console.log(hex);

Если строка без padding:

const hex = KJUR.crypto.Util.b64utohex("SGVsbG8"); // работает без "="

Кодирование UTF-8 строк в Base64url

Частая задача — преобразование текста в безопасный формат:

const text = "Привет";

const b64url = KJUR.crypto.Util.utf8tob64u(text);
console.log(b64url);

Обратное преобразование:

const decoded = KJUR.crypto.Util.b64utoutf8(b64url);
console.log(decoded);

Base64 и JWT: ключевое применение Base64url

JSON Web Token (JWT) использует исключительно Base64url.

Структура JWT:

header.payload.signature

Каждая часть:

  • JSON → UTF-8
  • UTF-8 → Base64url
  • объединение через точку

Пример:

const header = {
  alg: "HS256",
  typ: "JWT"
};

const payload = {
  sub: "1234567890",
  name: "John",
  iat: 1516239022
};

const sHeader = KJUR.jws.JWS.readSafeJSONString(JSON.stringify(header));
const sPayload = KJUR.jws.JWS.readSafeJSONString(JSON.stringify(payload));

Внутри Jsrsasign кодирование происходит через Base64url автоматически, но понимание механизма важно при ручной сборке токенов.


Типичные ошибки при работе с Base64url

1. Игнорирование padding

Некоторые декодеры требуют строгую длину:

// ошибка
b64utohex("SGVsbG8")

// может потребоваться
b64utohex("SGVsbG8=")

2. Использование Base64 вместо Base64url в JWT

JWT может ломаться из-за символов + и /.


3. Двойное кодирование

Частая ошибка:

btoa(btoa(str)) // неверно

4. Несоответствие UTF-8 и Latin1

Jsrsasign ожидает корректную UTF-8 обработку:

KJUR.crypto.Util.utf8tob64u("текст") // корректно

Отличия поведения в разных окружениях JavaScript

В браузере есть btoa и atob, но они:

  • работают только с Latin1
  • не поддерживают UTF-8 напрямую

В Node.js используются Buffer:

Buffer.from(str).toString("base64")
Buffer.from(str).toString("base64url")

Jsrsasign унифицирует эти различия через собственные функции.


Внутреннее представление и совместимость

Base64url — это не отдельный стандарт кодирования, а соглашение поверх Base64.

Поэтому:

  • данные идентичны
  • отличается только алфавит и padding
  • декодирование всегда возвращает исходный байтовый поток

Практическая схема преобразований в Jsrsasign

HEX:

hex → hextob64 → Base64
hex → hextob64u → Base64url

STRING:

utf8 → utf8tob64u → Base64url
Base64url → b64utoutf8 → UTF-8

Сравнение в контексте криптографии

В криптографических протоколах Base64url используется почти всегда, потому что:

  • безопасен для URL
  • не требует экранирования
  • стабилен при сериализации JSON
  • предсказуем в JWT и JWS

Base64 остаётся актуальным только для:

  • email MIME
  • бинарных файлов в XML/JSON
  • legacy API