GitHub の公開鍵で暗号化する ghcrypt の処理内容

の続き。甘い物のことは忘れて、もうちょっとちゃんと書きます。

いくつかバージョンアップを行いました。暗号化はAESで行い、AES のパスフレーズを公開鍵で暗号化するようにしました。現在のバージョンは 0.5 です。

ghcrypt ファイル名 githubユーザ名(受信者)
ghcrypt ファイル名.enc.tar githubユーザ名(送信者)

で、暗号化・復号化を行います。

動作環境

OpenSSH の 5.6 以降が必須です。OpenSSL は 0.9.8 あたりで動作確認をしてます。

OpenSSH 形式の公開鍵を OpenSSL で使える PKCS#8 にエクスポートするために、 OpenSSH 5.6 以降が必須となっています。手元の OSX Lion には入ってましたが、Debian Squeeze は、まだ 5.5 のようでした。

この時点で簡単に使えるとは言えないかも……。

$ ssh -V
OpenSSH_5.6p1, OpenSSL 0.9.8r 8 Feb 2011

* Allow ssh-keygen(1) to import (-i) and export (-e) of PEM and PKCS#8
keys in addition to RFC4716 (SSH.COM) encodings via a new -m option
(bz#1749)

Python の変換ツールを作ってる方がいました。そんな難しくなさそうなので、自前で変換してもいいかもしれません。

インストール

特にインストールで必要な作業はありません。bin/ghcyrpt をパスの通ったディレクトリに置いてください。

処理内容

暗号化処理では、以下の処理を行っています。

alice が bob に sample.txt を暗号化して送ろうとしている場合。

1. ディレクトリを作る

f:id:koseki2:20130217211142p:plain

カレントディレクトリに sample.txt.enc というディレクトリを作成します。

既存のファイルやディレクトリが存在する場合はエラーメッセージを出して終了します。

2. 受信者 (bob) の公開鍵をダウンロード、変換

f:id:koseki2:20130217211143p:plain

GitHub から公開鍵を wget します。ユーザ名が間違っていた場合にエラーにするため、先にダウンロードします。

ダウンロードした公開鍵を sample.txt.enc/bob.keys に保存します。この鍵を ssh-keygen -m でPKCS#8 に変換し、 bob.keys.pem として保存します。

ssh-keygen -f bob.keys -e -m PKCS8 > bob.keys.pem || exit $?

3. AES 用のパスフレーズを生成、ファイルを暗号化

f:id:koseki2:20130217211146p:plain

以下のコマンドで sample.txt.enc/sample.txt.key にランダムなパスフレーズを書き込みます。長さは 117 バイト。

cat /dev/urandom | LANG=C tr -dc '[:graph:]' | head -c 117 > sample.txt.key || exit $?

生成したパスフレーズを使い、sample.txt を AES 256 CBC で暗号化します。

openssl aes-256-cbc -e -in sample.txt -out sample.txt.enc/sample.txt.enc -pass file:sample.txt.enc/sample.txt.key || exit $?

pass で指定するファイルは1行目しか使われないので要注意です。

openssl rand コマンドで生成したバイナリを直接パスフレーズとして使うことはできません。ランダムなバイト列を使うと、一文字目に改行が含まれていた場合に、パスフレーズが空になってしまいます(おそろしい)。

パスフレーズから、AES の鍵とIV が生成されます。aes 256 で 256 bit の長さを持つのは鍵であって、パスフレーズは好きな長さに設定できます。

パスフレーズが、どのくらい長いとベストなのかはわかりませんでした。パスフレーズが鍵と同じ 32 bytes だと短いかもしれません (ビット列と異なり、使える文字が限られているため)。117 bytes ほど長い必要はないかもしれません。

117 bytes は、1024 bit の公開鍵で暗号化できる最大サイズです。どんだけあてになるかわかりませんが、昔書いた記事を見つけました (117 bytes 以上は暗号化できないことは確認しています)。

4. AES パスフレーズを公開鍵で暗号化

f:id:koseki2:20130217211147p:plain

rsautl コマンドで sample.txt.key を暗号化します。

openssl rsautl -encrypt -in sample.txt.key -out sample.txt.key.enc -inkey bob.keys.pem -pubin || exit $?

最初から公開鍵で sample.txt を暗号化しないのは、上で書いたとおり、公開鍵で暗号化できるサイズに限度があるためです。

5. 秘密鍵を使って、暗号化したファイルに署名

f:id:koseki2:20130217211148p:plain

以下のコマンドで sample.txt.enc に署名します。

openssl sha1 -sign /home/alice/.ssh/id_rsa sample.txt.enc > sample.txt.enc.sig || exit $?

6. 不要なファイルを消して tar 生成

f:id:koseki2:20130217211201p:plain

  • sample.txt.key.enc RSA暗号化済みのAES鍵
  • sample.txt.enc AES暗号化済みの入力ファイル
  • sample.txt.enc.sig 署名

を格納した tar ファイルが生成されます。

7. 復号化

暗号化とほぼ同じです。

  • tar を展開
  • alice の公開鍵をダウンロード、PKCS#8 変換
  • 署名を検証。ほんとに alice から来たのか。
    • openssl sha1 -verify alice.keys.pem -signature sample.txt.enc.sig sample.txt.enc
  • AES 鍵を bob の秘密鍵で復号
    • openssl rsautl -decrypt -in sample.txt.key.enc -out sample.txt.key -inkey /home/bob/.ssh/id_rsa
  • AES 鍵で元のファイルを復号
    • openssl aes-256-cbc -d -in sample.txt.enc -out sample.txt -pass file:sample.txt.key
  • 後片付け。tarで展開したディレクトリを削除。

FAQ

Q. 安全ですか?

A. わかりません。

… 単に OpenSSL のコマンドを順に実行してるだけなので、そんな無茶はしてないと思うのですが、多くの人の目に触れないと安全ではないと思います。