Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. OpenSSL
Код

Base64 в OpenSSL

openssl base64 есть почти на любой машине с Linux, macOS и BSD и везде ведёт себя одинаково. А ещё при ошибках она молчит. Результаты ниже получены на OpenSSL 3.5.

Закодировать и декодировать строку

printf '%s' 'héllo wörld' | openssl base64        # aMOpbGxvIHfDtnJsZA==
echo 'aMOpbGxvIHfDtnJsZA==' | openssl base64 -d   # héllo wörld

openssl base64 – сокращение для openssl enc -base64; обе формы принимают одни и те же флаги. При кодировании используйте printf '%s', чтобы в данные не пробрался перевод строки, а при декодировании – echo, и ниже объяснено почему.

Ловушка с переводом строки и -A

По умолчанию OpenSSL обрабатывает Base64 как текстовые строки, завершённые переводом строки. Последняя строка без \n при декодировании игнорируется, поэтому эта команда вообще ничего не выведет:

printf 'aGVsbG8K' | openssl base64 -d             # (no output)
printf 'aGVsbG8K\n' | openssl base64 -d           # hello
printf 'aGVsbG8K' | openssl base64 -d -A          # hello

-A говорит OpenSSL считать весь вход одной строкой. Используйте его в обе стороны:

  • Кодирование: без -A вывод переносится по 64 символа в строке. С -A получается одна строка, как раз для HTTP-заголовков, JSON и переменных окружения.
  • Декодирование: с -A вход должен быть одной строкой без переносов. Без -A многострочные данные, например тела PEM, декодируются нормально.
head -c 60 /dev/zero | tr '\0' x | openssl base64      # 64 chars, newline, 16 chars
head -c 60 /dev/zero | tr '\0' x | openssl base64 -A   # 80 chars on one line

Перенос по 64 символа соответствует формату PEM (RFC 7468), а не MIME, где строки бывают до 76 символов. Для вывода в стиле MIME лучше подойдут кодировщик MIME Base64 или GNU base64 (по умолчанию 76).

URL-safe Base64

Режима Base64URL в OpenSSL нет. Заменяйте символы через tr:

printf '%s' '<<???>>' | openssl base64 -A | tr '+/' '-_' | tr -d '='
# PDw_Pz8-Pg

Для декодирования сделайте обратную замену и восстановите дополнение; цикл, который это делает, есть на странице Base64 в Bash. Сам OpenSSL, как видно ниже, молча игнорирует - и _, поэтому никогда не передавайте ему URL-safe строки напрямую. Разница объяснена в статье Base64 и Base64URL.

Кодирование и декодирование файлов

openssl base64 -in photo.jpg -out photo.b64
openssl base64 -d -in photo.b64 -out photo-copy.jpg

OpenSSL работает потоково, так что большие файлы ему не помеха. Случайный файл в 100 КБ пережил кодирование и декодирование байт в байт. Закодированный файл перенесён по 64 символа и заканчивается переводом строки, поэтому его можно декодировать без -A. Если нужен файл в одну строку, добавьте -A и при кодировании, и при декодировании.

Для data URI:

printf 'data:image/jpeg;base64,%s' "$(openssl base64 -A -in photo.jpg)"

В разделе BUGS руководства enc для OpenSSL 3.5 до сих пор упомянуты две связанные проблемы: -A «doesn’t work properly» (работает неправильно) с большими файлами, а при декодировании без -A, если в первых 1024 байтах входа нет перевода строки, первые две строки игнорируются. Большой однострочный файл сначала перенесите через fold -w 64 и декодируйте без -A.

Тихие сбои: проверка результата

OpenSSL не сообщает о некорректном Base64. Он останавливается на плохих данных, выводит то, что успел декодировать, и завершается с кодом 0. Результаты на OpenSSL 3.5:

ВходВыводКод выхода
aGVsbG8K + перевод строкиhello\n0
aGVsbG8K (без перевода строки)ничего0
aGk + перевод строки (без дополнения)ничего0
aGk=!! + перевод строкиничего0
PDw_Pz8-Pg== + перевод строки (URL-safe)ничего0
not base64 at all + перевод строкиничего0

Поэтому скриптам приходится проверять вывод самостоятельно:

n=$(echo "$b64" | openssl base64 -d | wc -c)
[ "$n" -gt 0 ] || echo "decode failed or input was empty" >&2

Так ловятся полные провалы, но не частичные, когда OpenSSL декодировал начало строки, прежде чем наткнуться на плохие данные.

Или перейдите на GNU base64 -d, который пишет base64: invalid input и завершается с кодом 1. Чтобы понять, что не так с конкретной строкой, откройте валидатор Base64: он отметит плохой символ, а причины объяснены в статье частые ошибки Base64.

Хеши, ключи и зашифрованный вывод

Чаще всего Base64 в OpenSSL используют вместе с другими командами:

printf '%s' hi | openssl dgst -sha256 -binary | openssl base64 -A
# j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=

openssl rand -base64 32          # 32 random bytes -> 44 characters

Первая форма даёт дайджест SHA-256 в Base64. Такой формат используют хеши SRI и подписи многих API; в браузере то же делает инструмент SHA-256 в Base64. -binary важен: без него вы закодируете hex-текст хеша, а не сам хеш.

openssl rand -base64 32 – распространённый способ сгенерировать секреты и ключи; генератор ключей Base64 делает то же в браузере.

openssl enc с -a (то же, что -base64) кодирует шифротекст в Base64:

printf '%s' 'secret text' | openssl enc -aes-256-cbc -pbkdf2 -a -pass pass:demo
# U2FsdGVkX1/eMk5T8FT9dX8OyAxLRn/ukvgmovOQOeA=   (salt is random, changes every run)

Префикс U2FsdGVkX1 – это Base64 от Salted__. Если декодировать такую строку без пароля, ничего читаемого не получится. Эта разница между кодированием и шифрованием разобрана в статье кодирование и шифрование.

Файлы PEM

Сертификаты и ключи в формате PEM – это DER в Base64 между строками -----BEGIN ...-----, перенесённый по 64 символа. OpenSSL конвертирует их напрямую, без ручной возни с Base64:

openssl x509 -in cert.pem -outform der -out cert.der
openssl x509 -in cert.der -inform der -outform pem

Чтобы посмотреть блок PEM в браузере, воспользуйтесь декодером сертификатов или инструментом PEM в DER.

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

Почему openssl base64 -d ничего не выводит?

Либо во входных данных нет перевода строки в конце, либо они некорректны. Без -A OpenSSL ждёт строк, завершённых переводом строки, и молча отбрасывает последнюю строку без него. Добавьте -A или выводите строку через echo, а не printf '%s'.

openssl base64 – это то же, что openssl enc -base64?

Да. openssl base64 – сокращение для openssl enc -base64, и обе формы принимают -d, -A, -in и -out.

Как проверить, что openssl base64 -d сработал?

На код выхода полагаться нельзя: на некорректных данных он равен 0, а вывод пустой или неполный. Проверяйте, что вывод не пуст, или используйте GNU base64 -d, который на плохих данных завершается с кодом 1.

Что за U2FsdGVkX1 в начале строки Base64?

Это Base64 от Salted__ – заголовка, который openssl enc пишет перед зашифрованными данными, если используется пароль. Строка – шифротекст, поэтому при декодировании получится двоичный шум, а не исходный текст.

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