「自分には安全にデータを送ってほしい」人向けおてがる暗号化ツールを作ってみた

# ブラウザでオフラインにRSA暗号を使う

公開鍵暗号は便利ですが、相手に概念を説明して、公開鍵を送ったり、ツールをインストールしてもらう…といった準備が大変でした。

そこで最近、ブラウザ共通の暗号技術実装 [WebCrypto API](https://developer.mozilla.org/ja/docs/Web/API/Web_Crypto_API) を知りまして、GitHubって公開鍵データベースみたいな側面あるし、組み合わせたら面白いんじゃないかと思い、作ってみたのがこちらです。

https://rsa.bio/

ブラウザで指定URLのページを開くだけで、公開鍵暗号がすぐ使えます。平文をネットワークに送ることなく、オフラインで暗号化できるのも特徴です。

なお私、暗号技術は手探りです。そもそもこのRSAの使い方はどうか？みたいな指摘もコメントにお寄せください！

# 暗号化フォーム

GitHubにRSA公開鍵が登録されていればすぐに暗号化できます。今どきRSA使ってないよーという方も追加登録を😅

筆者宛の暗号化フォーム (パス末尾をご自身のIDに変更してください)
https://rsa.bio/github/miyanaga

このフォームでは、`RSA-OAEP SHA256`により、テキスト文書と

![RSA_BIO___ブラウザでできるオフラインRSA暗号.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35178/9e9de071-4e92-cfa6-65be-8f444efd3422.png)

ファイルの暗号化ができます。ファイルの方も[File API](https://developer.mozilla.org/ja/docs/Web/API/File)を使ってオフラインで処理します。

![RSA_BIO___ブラウザでできるオフラインRSA暗号.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35178/0b405ea7-49de-475f-7031-8e26bce26d34.png)

ファイルをドラッグ&ドロップすると、拡張子`.rsa`が付加された暗号化済みファイルがダウンロードされます(オフラインなのでダウンロードではないですが、そのような挙動の意味)。

どちらか試してみてください。ただ、あまり大きなファイルはブラウザが固まるかもです…

# 復号化

自分宛の暗号をもとに戻す時ですが… **とりあえず私自身が使いたいと思ったツールなので、macOSでの手順しか考えていません。**

Windowsではこうやったとか、ツール実装してみたよーみたいなコメントをいただけるといいなぁと期待してます。そして私も使いたい！

## 1. 秘密鍵のPEM化

最近の`ssh-keygen`は秘密鍵を`OpenSSH`独自のフォーマットで出力するらしく、私の場合は変換が必要でした。`openssl genrsa`で生成した人は大丈夫なのかな。

ひとまず、`~/.ssh/id_rsa`を`~/.ssh/id_rsa.pem`にコピーし、形式を確認します。

```sh
cp ~/.ssh/id_rsa ~/.ssh/id_rsa.pem
head -n 1 ~/.ssh/id_rsa.pem
```

この結果が、`-----BEGIN OPENSSH PRIVATE KEY-----`だとそのOpenSSH形式です。

以下のコマンドで一般的なPEM(?)に変換します。

```sh
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa.pem
head -n 1 ~/.ssh/id_rsa.pem
```

出力が`-----BEGIN RSA PRIVATE KEY-----`になればOKです(もともとそうであれば変換は不要です)。

## 2. openssl@3のインストール

WebCrypt APIのRSA-OAEPはハッシュ関数がSHA256固定のようなんですが、macOS(当方Monterey)バンドルのOpenSSLはSHA1固定のようで、そのまま使えませんでした。

そこで`openssl@3`をインストールします。

```sh
brew install openssl@3
```

## 3. 復号化スクリプト

ちょっと手間がかかりましたが、これでやっと復号化スクリプトに入れます。

`~/dec-rsa.sh`など任意の名称でシェルスクリプトを作成します。私のはこんな感じですが、ぜひアレンジしてみてください。

```bash
#!/bin/bash

set -e

private_key="$HOME/.ssh/id_rsa.pem"
openssl="/opt/homebrew/opt/openssl@3/bin/openssl"

input_file="$1"

# 第一引数にファイルがなければ一時ファイルにクリップボードのBase64をデコード
if [ -z "$input_file" ]; then
  input_file=$(mktemp)
  pbpaste | base64 -d > $input_file
fi

# 鍵長と暗号文単位サイズ
bits=$(openssl rsa -in "$private_key" -noout -text | egrep 'Private-Key' | sed -E 's/^[^0-9]+([0-9]+).+$/\1/')
chunk_size=$(( ($bits + 7) / 8 ))

# 一時ディレクトリに暗号文単位でファイルとして分割
# FIXME: ほんとはreadコマンドでやりたいが最後のチャンクをうまく処理できないので妥協
chunk_dir=$(mktemp -d)
split -a 8 -b $chunk_size "$input_file" "$chunk_dir/"

# 復号
for chunk in `find "$chunk_dir" -type f | sort`; do
  $openssl pkeyutl -decrypt -inkey "$private_key" -in "$chunk" -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 > "$chunk.raw"
  rm -rf "$chunk"
done

# 出力
for chunk in `find "$chunk_dir" -type f | sort`; do
  cat "$chunk"
done

# 一時ディレクトリの削除
rm -rf "$chunk_dir"
```

実行権限を付与します。

```sh
chmod +x ./dec-rsa.sh
```

## テキストを復号してみる

例えば私宛の場合、`こんにちは！`は

```txt
iZkLueyY2jZ7NGZz60GRTGyw2M3eGtEbdAY4wftuNqVIHV1dXr16BqFX+rs5zzYD
nQQ3zSnVl14n9rCtfDR/4xTZEr1MgdresyMaiVHZuxjHHd5QNiKrdwTWC8GbP8Tx
bYPHBvG8cJ6c5+fD6CNnvIuI7XCc0xD9vVYd9N4T6OUe/S2DtxfQhYNymd+wCFQ/
K4yA+eFQMUPOyzXLyb0xYY0Z8l4xLGLISAcI+qvsX4li8/z2kZqibRQZyOFphylO
TziMHTZsIHJ0wS3cmbt42J4hSqUBWWfN3FJeUX5MNn1tBKjocYa1MVwgRHZAX/g5
dzKDySRh0JhlonaXMMuBqNZy52/JbTQfyvIGaeag8j+j9s4Y6XrCCH124LKDFQic
G0ahnOu1jcg16JJdxrdu2lfrozlRhRn2r+0Y9HB6haQ9BK74i1lkkxsXgJqrwC14
kCvtIEAb38sJIHGc2SrB67dbuC8MTPj1UVcy1nFkUkJL9TY8Ed8bhnn7sSW44Yy2
QYeuyDScXpMOecopaqc3Qe2Vyv8OLVRzdUDRRDM9skgBI8oR9wxtKrHLwhq4lOLV
ZvWwWq+wvYNV2DGu/lOKS9IZCssvDRlWhDsfVcTO40/fpQQEzXLb7MRSSciW2U3g
EgHDlmQ3swJnBedgqFl4Cnzq3UEMuioJ0zJHb3jL92E=
```

になります。これをクリップボードにコピーし、先ほどのスクリプトを実行すると…

```sh
./dec-rsa.sh
こんにちは！
```

復号できました！

`pbcopy`を組み合わせると、復号化された平文をクリップボードに書き戻すことができます。

```sh
./dec-rsa.sh | pbcopy
```

## ファイルを復号してみる

ファイルを復号化するときは、シェルスクリプトの第1引数にパスを指定します。

```sh
./dec-rsa.sh ~/Downloads/my-file.txt.rsa > ./my-file.txt
```

これで暗号化前の`my-file.txt`に戻すことができるはずです。

# 終わりに

Qiitaをやっているみなさんだと、パスワード類を預かって作業を代行する…みたいな場面があるのではないでしょうか。

そんなときパスワードが平文でくるとドキッとするし、PPAPで渡されてもモヤっとします。それを自分だけでも回避できないかと思って作ってみました。

もちろん「このツールでRSA暗号にすればメールでデータを共有しても安心！これがベスト！」というつもりはまったくありません。

社外とのやりとりだと、お互いのベストが一致するとは限らず、いつもの連絡経路でやり取りすることになっちゃう場面は多いです。その際に、このツールがあると一歩前進すると思いますが、どうでしょうか。