文字列のエンコードとデコード
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 のない最後の行は無視されるため、次の 1 行目は何も出力しません。
printf 'aGVsbG8K' | openssl base64 -d # (no output)
printf 'aGVsbG8K\n' | openssl base64 -d # hello
printf 'aGVsbG8K' | openssl base64 -d -A # hello
-A を付けると、OpenSSL は入力全体を 1 行として扱います。両方向で使いましょう。
- エンコード時:
-Aなしでは、出力は 1 行 64 文字で折り返されます。-Aを付ければ 1 行になり、HTTP ヘッダー、JSON、環境変数で必要なのはこちらです。 - デコード時:
-Aを付けると、入力は改行のない 1 行でなければなりません。-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)に合わせたもので、最大 76 文字の MIME とは異なります。MIME 形式の出力には、MIME Base64 エンコーダー か GNU の base64(デフォルト 76 文字)のほうが適しています。
URLセーフな Base64
OpenSSL には Base64URL モードがありません。tr で置き換えます。
printf '%s' '<<???>>' | openssl base64 -A | tr '+/' '-_' | tr -d '='
# PDw_Pz8-Pg
デコードでは対応を逆にし、パディングを戻します。そのためのループは Bash での Base64 にあります。後で示すとおり、OpenSSL 自体は - と _ を黙って無視するので、URLセーフな入力を直接渡してはいけません。違いについては Base64 と Base64URL の違い で説明しています。
ファイルのエンコードとデコード
openssl base64 -in photo.jpg -out photo.b64
openssl base64 -d -in photo.b64 -out photo-copy.jpg
OpenSSL はストリーム処理なので、大きなファイルも問題ありません。100 KB のランダムなファイルは、往復変換後もバイト単位で一致しました。エンコードしたファイルは 64 文字で折り返され、末尾に改行があるので、-A なしでデコードできます。1 行のファイルが必要なら、エンコード時とデコード時の両方で -A を付けてください。
Data URI を作るには次のようにします。
printf 'data:image/jpeg;base64,%s' "$(openssl base64 -A -in photo.jpg)"
OpenSSL 3.5 の enc マニュアルの BUGS セクションには、関連する問題が今も 2 つ記載されています。大きなファイルでは -A が「正しく動作しない」こと、そして -A なしでデコードする際、入力の最初の 1024 バイトに改行が含まれていないと最初の 2 行が無視されることです。大きな 1 行のファイルは、先に fold -w 64 で折り返してから -A なしでデコードしてください。
エラーにならない失敗:結果を検証する
OpenSSL は不正な Base64 を報告しません。不正なデータの手前で止まり、そこまでにデコードした内容を書き出して、ステータス 0 で終了します。OpenSSL 3.5 での結果:
| 入力 | 出力 | 終了コード |
|---|---|---|
aGVsbG8K + 改行 | hello\n | 0 |
aGVsbG8K(改行なし) | なし | 0 |
aGk + 改行(パディングなし) | なし | 0 |
aGk=!! + 改行 | なし | 0 |
PDw_Pz8-Pg== + 改行(URLセーフ) | なし | 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 が不正なデータに当たる前に先頭部分だけをデコードした、部分的な失敗は検出できません。
あるいは、base64: invalid input を表示してステータス 1 で終了する GNU の base64 -d に切り替えてください。特定の文字列のどこがおかしいかは Base64 バリデーター が不正な文字を示し、原因は よくある Base64 のエラー で説明しています。
ハッシュ、鍵、暗号化された出力
OpenSSL で Base64 を使うのは、たいてい他のコマンドとの組み合わせです。
printf '%s' hi | openssl dgst -sha256 -binary | openssl base64 -A
# j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ=
openssl rand -base64 32 # 32 random bytes -> 44 characters
1 つ目の形式は Base64 の SHA-256 ダイジェストを生成します。SRI ハッシュ や多くの API 署名で使われる形式で、ブラウザ版は SHA-256 から Base64 ツールです。-binary が重要で、これがないとハッシュそのものではなく、ハッシュの 16進数テキストをエンコードしてしまいます。
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 は Salted__ を Base64 にしたものです。パスワードなしでデコードしても、読める内容は何も出てきません。エンコードと暗号化のこの違いは エンコードと暗号化の違い で解説しています。
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 を付けるか、printf '%s' ではなく echo で文字列を渡してください。
openssl base64 と openssl enc -base64 は同じですか?
同じです。openssl base64 は openssl enc -base64 の省略形で、どちらも -d、-A、-in、-out を受け付けます。
openssl base64 -d が成功したかどうかを確認するには?
終了コードは当てになりません。不正な入力でも、空または一部だけの出力とともにステータス 0 を返します。出力が空でないことを確認するか、不正な入力でステータス 1 を返す GNU の base64 -d を使ってください。
Base64 文字列の先頭にある U2FsdGVkX1 とは何ですか?
Salted__ を Base64 にしたもので、パスワードを使ったときに openssl enc が暗号化データの前に書き込むヘッダーです。文字列は暗号文なので、デコードしても平文ではなくバイナリの雑音しか得られません。