Закодировать и декодировать строку
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\n | 0 |
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 пишет перед зашифрованными данными, если используется пароль. Строка – шифротекст, поэтому при декодировании получится двоичный шум, а не исходный текст.