ファイルをパスワードで暗号化して保管したい、暗号化したファイルを元に戻したいときは、openssl encを使う。

$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out plain.txt.enc
enter AES-256-CBC encryption password:
Verifying - enter AES-256-CBC encryption password:

復号するときは-dを付ける。

$ openssl enc -d -aes-256-cbc -pbkdf2 -in plain.txt.enc -out plain.txt
enter AES-256-CBC decryption password:

パスワードは対話的に入力する。-pbkdf2は必ず付ける。理由は後述する。暗号化と復号でアルゴリズムと-pbkdf2などのオプションを揃える必要がある。

実行例はUbuntu 24.04のOpenSSL 3.0.13のもの。

パスワードの渡し方を選ぶ

スクリプトから実行する場合は、対話入力の代わりに-passでパスワードの取得元を指定する。

指定パスワードの取得元
-pass pass:[パスワード]コマンドラインに直接書く
-pass env:[環境変数名]環境変数から読む
-pass file:[ファイル]ファイルの1行目から読む
$ export PW=secret
$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out plain.txt.enc -pass env:PW
$ echo 'secret' > pw.txt
$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out plain.txt.enc -pass file:pw.txt

pass:はコマンドラインに書くため、psの出力やシェルの履歴にパスワードが残る。共有環境ではenv:かfile:を使う。file:を使う場合は、パスワードのファイルのパーミッションを600にする。

標準入力が端末でない環境で-passを指定しないと、パスワードを読み込めずに失敗する。

$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -out x.enc </dev/null
enter AES-256-CBC encryption password:
bad password read

パスワードに使う文字列は、openssl randで生成できる。

参考: 【openssl】ランダムな文字列を生成する (rand)

-pbkdf2を付ける理由

パスワードから暗号鍵を作る処理を鍵導出と呼ぶ。-pbkdf2を付けないと、OpenSSLは古い鍵導出(ソルト付きパスワードのハッシュを1回だけ計算する方式)を使う。総当たりでパスワードを試されやすいため、警告が表示される。

$ openssl enc -aes-256-cbc -in plain.txt -out plain.txt.enc -pass pass:pw
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.

-pbkdf2を付けると、パスワードを何千回も繰り返してハッシュ化する鍵導出(PBKDF2)を使う。警告も表示されなくなる。

暗号化したときと異なる鍵導出で復号すると、復号に失敗する。

$ openssl enc -d -aes-256-cbc -in plain.txt.enc -pass pass:pw
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
bad decrypt

-pbkdf2を付けて暗号化したファイルは、-pbkdf2を付けて復号する。

繰り返し回数を増やす

-iterでPBKDF2の繰り返し回数を指定できる。デフォルトは10000回である。回数を増やすほど、総当たりに時間がかかる。

$ openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -in plain.txt -out plain.txt.enc -pass env:PW

-iterの値は保存されない。復号時にも同じ値を指定する。

$ openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -in plain.txt.enc -pass env:PW
secret-data
$ openssl enc -d -aes-256-cbc -pbkdf2 -in plain.txt.enc -pass env:PW
bad decrypt

暗号化したファイルの中身

暗号化したファイルは、先頭8バイトがSalted__、続く8バイトがソルト(ランダムな値)である。

$ head -c 8 plain.txt.enc | od -c | head -1
0000000   S   a   l   t   e   d   _   _

ソルトは暗号化ごとにランダムな値が選ばれる。入力とパスワードが同じでも、出力は毎回異なる。

$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -pass pass:pw | md5sum
e0c3bcba638cd9d28dc35ef590befab4  -
$ openssl enc -aes-256-cbc -pbkdf2 -in plain.txt -pass pass:pw | md5sum
b67f226b2a2cbd46d1ae7f61ee0d03ed  -

ソルトを付けたくない場合は-nosaltを指定する。同じ入力から同じ出力が得られるため、ソルトなしで運用するのは避ける。

暗号化結果をテキストで扱う

-aを付けると、暗号化結果をBase64で出力する。メールやチャットへ貼り付ける場合に使う。復号時も-aを付ける。

$ openssl enc -aes-256-cbc -pbkdf2 -a -in plain.txt -pass env:PW
U2FsdGVkX18PwgUW9Ogx00Qu/SRs1VkgVIgaNXaCWAI=
$ openssl enc -d -aes-256-cbc -pbkdf2 -a -in plain.b64 -pass env:PW
secret-data

ディレクトリを暗号化する

encはファイル単位の処理である。ディレクトリはtarでまとめてからパイプで渡す。

$ tar czf - [ディレクトリ] | openssl enc -aes-256-cbc -pbkdf2 -pass env:PW -out dir.tgz.enc
$ openssl enc -d -aes-256-cbc -pbkdf2 -pass env:PW -in dir.tgz.enc | tar tzf -
d/
d/a

復号に失敗する場合

パスワードが違う場合や、暗号化時と異なるアルゴリズム・オプションを指定した場合は、bad decryptで失敗する。

$ openssl enc -d -aes-256-cbc -pbkdf2 -in plain.txt.enc -pass pass:bad
bad decrypt

誤ったパスワードでも、偶然パディングの形式が正しくなると、エラーなしで壊れたデータが出力される。復号結果が正しいかどうかは、ハッシュ値の比較など別の手段で確認する。

参考: 【openssl】ファイルのハッシュ値を計算する (dgst)

使えるアルゴリズム

openssl enc -listで、使えるアルゴリズムを一覧表示できる。-aes-256-cbc以外に、-aes-128-cbcや-aes-256-ctrなどがある。

-aes-256-gcmのような認証付き暗号(AEAD)は、encでは使えない。

$ openssl enc -aes-256-gcm -pbkdf2 -in plain.txt -pass pass:pw
enc: AEAD ciphers not supported

encで使うCBCは、データが改ざんされても検知できない。改ざんの検知が必要な用途では、GnuPGなどほかのツールを検討する。

バージョンによる違い

警告の有無はバージョンで異なる。OpenSSL 3.0、3.5、3.6では、-pbkdf2なしで警告が表示される。macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)では、警告は表示されない。

-pbkdf2を付けて暗号化したファイルは、OpenSSL 3.0、3.6、LibreSSLの間で相互に復号できる。

参考

openssl-enc - OpenSSL Documentation