Загрузить файл
  1. base64hub
  2. Статьи о Base64
  3. Стандарты и RFC Base64
Справочник

Стандарты Base64: от PEM и MIME до RFC 4648

У Base64 нет единого автора и единой спецификации. Кодировка появилась в 1987 году в защищённой почте, перешла в MIME и только в 2003 году получила собственный стандарт. Ниже собраны документы, которые её определяют, и варианты, которые они породили.

Хронология

ГодДокументЧто он добавил
1987RFC 989 (PEM, часть I)64-символьная «печатная кодировка» с дополнением =, плюс * для незашифрованных фрагментов
1988, 1989RFC 1040, RFC 1113Пересмотренные черновики PEM
1992RFC 1341 (MIME)Base64 как Content-Transfer-Encoding для почты
1993RFC 1421 (PEM), RFC 1521 (MIME)Строки PEM ровно по 64 символа, обновлённый MIME
1996RFC 2045 (MIME)Определение MIME, которое действует до сих пор: строки не длиннее 76 символов
1997RFC 2152 (UTF-7)Base64 внутри текстовой кодировки для Unicode
2003RFC 3501 (IMAP4rev1)Модифицированный Base64 с , для имён почтовых ящиков
2003RFC 3548Первая самостоятельная спецификация: base64, base64url, base32, base16
2006RFC 4648Заменяет RFC 3548, добавляет base32hex и тестовые векторы
2007RFC 4880 (OpenPGP)Броня Radix-64 с контрольной суммой CRC-24
2015RFC 7468Строгие правила для файлов сертификатов и ключей в стиле PEM
2015RFC 7515 (JWS)Base64URL без дополнения для токенов
2024RFC 9580 (OpenPGP)Заменяет RFC 4880, объявляет контрольную сумму брони устаревшей

PEM: с чего начался Base64

Алфавит, которым мы пользуемся сегодня, впервые появился в RFC 989 «Privacy Enhancement for Internet Electronic Mail», опубликованном в феврале 1987 года. Зашифрованная почта шла через шлюзы, которые пропускали только печатный текст, поэтому PEM нужен был способ записать двоичный шифротекст символами, которые переживут любую почтовую систему. Он брал по 6 бит и сопоставлял их символам A-Z, a-z, 0-9, + и /, а для дополнения использовал =. Таблица в RFC 989 совпадает с таблицей алфавита на этом сайте. Ещё там был зарезервирован * для пометки незашифрованных фрагментов, но эта возможность не прижилась.

PEM прошёл через RFC 1040 и RFC 1113 и окончательно оформился в RFC 1421 (февраль 1993 года), где была закреплена длина строки: каждая строка, кроме последней, содержит ровно 64 печатных символа, а последняя не длиннее 64. В качестве причины назывались печать и лимит SMTP в 1000 символов на строку. Как система защищённой почты PEM так и не взлетел, а вот его формат файлов остался. Каждый блок -----BEGIN CERTIFICATE----- происходит от него, и RFC 7468 (2015) теперь прямо прописывает правила: генераторы обязаны переносить строки ровно через 64 символа, парсеры могут принимать и другую ширину. Такие файлы удобно разбирать в декодере сертификатов.

MIME: Base64 для каждого почтового вложения

MIME (RFC 1341 в 1992 году, RFC 1521 в 1993-м, RFC 2045 в 1996-м) понадобился способ вкладывать в письма картинки и документы, и он позаимствовал кодировку у PEM. RFC 2045 до сих пор остаётся основным документом для электронной почты и отличается от PEM в двух местах:

  • Закодированные строки не должны быть длиннее 76 символов и разделяются CRLF.
  • Любые символы вне алфавита base64 в закодированных данных должны игнорироваться. Декодеры пропускают переносы строк, пробелы и всё остальное, что не входит в алфавит.

Именно из-за второго правила так много декодеров молча выбрасывают недопустимые символы. Вывод в стиле MIME делает кодировщик MIME Base64, а как устроены вложения, рассказано в статье Base64 в электронной почте.

RFC 3548 и RFC 4648: один документ для всех

До 2003 года каждый протокол, использовавший Base64, ссылался на MIME или PEM и вместе с ними получал их правила о длине строк и снисходительном декодировании, нужны они были ему или нет. RFC 3548 (июль 2003 года) определил base64, base64url, base32 и base16 как самостоятельные кодировки. RFC 4648 (октябрь 2006 года) заменил его с небольшими изменениями: добавил base32hex, который сохраняет порядок сортировки закодированных данных, и набор тестовых векторов:

BASE64("")       = ""
BASE64("f")      = "Zg=="
BASE64("fo")     = "Zm8="
BASE64("foo")    = "Zm9v"
BASE64("foobar") = "Zm9vYmFy"

Правила RFC 4648 строже, чем у MIME, если ссылающаяся на него спецификация не говорит иного:

  • Переводы строк: реализации не должны их добавлять (раздел 3.1).
  • Дополнение: реализации должны добавлять = (раздел 3.2).
  • Символы вне алфавита: декодеры должны отклонять такие данные (раздел 3.3).
  • Каноническая форма: кодеры должны обнулять неиспользуемые биты, а декодеры могут отклонять ввод, где они не обнулены (раздел 3.5). Без этого правила и SGk/Pg==, и SGk/Ph== декодируются в Hi?>.

Оговорка «если ссылающаяся на этот документ спецификация явно не указывает иное» значит очень многое. Благодаря ей JWS может отказаться от дополнения, а MIME сохранить строки по 76 символов, и при этом оба используют «Base64 по RFC 4648». Две другие кодировки из того же RFC реализуют инструменты Base32 и Base16.

Варианты из других стандартов

JWS и JWT: Base64URL без дополнения

RFC 7515 (май 2015 года) определяет BASE64URL(x) как алфавит из раздела 5, у которого отброшены все завершающие символы =, без переносов строк и пробелов. Так работают JWT, JWE, JWK и большая часть семейства JOSE. Подробности есть в статье Base64 в JWT.

IMAP: modified UTF-7 с запятой

Имена почтовых ящиков в IMAP записываются 7-битными строками, поэтому RFC 3501 (раздел 5.1.3) кодирует имена папок с не-ASCII символами в «modified UTF-7». Последовательности не-ASCII символов переводятся в UTF-16, кодируются в Base64 без дополнения, с , вместо /, и заключаются между & и -. Пример из самого RFC &U,BTFw- кодирует китайское название 台北, а немецкая папка Entwürfe превращается в Entw&APw-rfe. Обычный UTF-7 (RFC 2152) записывает то же имя как Entw+APw-rfe.

OpenPGP: Radix-64 с CRC-24

ASCII-броня OpenPGP устроена как MIME Base64 плюс строка с контрольной суммой. RFC 4880 определяет CRC-24 (порождающий многочлен 0x864CFB, начальное значение 0xB704CE) над двоичными данными; сумма кодируется в Base64 и предваряется символом =. Вот что GnuPG выдаёт для 12 байт Hello world!:

-----BEGIN PGP ARMORED FILE-----
Comment: Use "gpg --dearmor" for unpacking

SGVsbG8gd29ybGQh
=s4Gu
-----END PGP ARMORED FILE-----

RFC 4880 допускает строки до 76 символов, GnuPG пишет по 64. RFC 9580 (июль 2024 года) сохраняет формат, но говорит, что строку CRC-24 генерировать не следует (SHOULD NOT) и что реализации не должны отклонять блок, если она отсутствует или неверна.

Таблица вариантов Base64

ВариантИндексы 62 и 63ДополнениеДлина строкиГде определён
Стандартный Base64+ /ОбязательноБез ограниченийRFC 4648, раздел 4
Base64URL- _По усмотрению ссылающейся спецификацииБез ограниченийRFC 4648, раздел 5
JWS / JWT- _НикогдаБез ограниченийRFC 7515
MIME+ /ОбязательноНе больше 76, CRLFRFC 2045
PEM+ /ОбязательноРовно 64RFC 1421, RFC 7468
Броня OpenPGP+ /ОбязательноНе больше 76 (GnuPG: 64)RFC 4880, RFC 9580
UTF-7+ /НетНеприменимоRFC 2152
Modified UTF-7 в IMAP+ ,НетНеприменимоRFC 3501
bcryptАлфавит ./A-Za-z0-9НетНеприменимоДе-факто (OpenBSD)
Хеши crypt(3)Алфавит ./0-9A-Za-zНетНеприменимоДе-факто (Unix)

Последние две строки переставляют весь алфавит, а не только два последних символа. О них рассказано на странице алфавита. Многие другие форматы ссылаются на одну из строк выше, ничего не меняя: HTTP Basic-аутентификация (RFC 7617), data URI (RFC 2397), base64Binary из XML Schema и хеши Subresource Integrity используют стандартный Base64 с дополнением.

Вопросы и ответы

Какой RFC описывает Base64?

Основным справочным документом служит RFC 4648 (октябрь 2006 года). В нём определены base64, base64url, base32, base32hex и base16. Версия MIME описана в RFC 2045, исходная версия PEM в RFC 1421.

Почему строки в MIME по 76 символов, а в PEM по 64?

Оба ограничения держат строки заметно короче лимита SMTP в 1000 символов. PEM зафиксировал 64 в RFC 1421, MIME в своей спецификации выбрал «не больше 76». RFC 4648 вообще запрещает кодерам вставлять переносы строк, если этого не требует ссылающийся на него стандарт.

Есть ли у Base64URL официальный стандарт?

Да. Это раздел 5 RFC 4648, «Base 64 Encoding with URL and Filename Safe Alphabet», а впервые он появился в RFC 3548. JWS (RFC 7515) использует его без дополнения.

Что за строка =XXXX в конце PGP-блока?

Это контрольная сумма CRC-24 декодированных данных, тоже записанная в Base64. RFC 9580 (2024) рекомендует новым реализациям её не выводить и запрещает отклонять блок из-за неё.

Читайте также