HackerzLab.博多 Vol.21 RSA公開鍵暗号化方式の実装ハンズオン

この記事は、

[**2019/05/05(Sun) HackerzLab.博多 Vol.21
『RSA公開鍵暗号化方式の実装ハンズオン』**](https://hackerzlabhkt.connpass.com/event/129256/)

の資料です。


# 1. 作業用プロジェクトのダウンロード

ハンズオン開始用プロジェクト（ファイル群）をダウンロードします。

gitがインストールされていない場合は、各自でインストールするか、

もしくはハンズオン当日の共有フォルダから開始用プロジェクト（フォルダ）を
コピーしてください（詳細は当日説明します）。

```
$ git clone https://github.com/NobuyukiInoue/Example_RSA_Work/
```

作業ディレクトリに移動します。

```
$ cd Example_RSA_Work
```

カレントディレクトリのパスを確認しておきます。

```
s19@ubuntu1804:~/Example_RSA$ pwd
/home/s??/Example_RSA
```

# 2. ディレクトリ構成

ディレクトリ構成は以下の通りです。
必要なファイルはすべて用意してあります。

入力が未完全なプログラムがあるので、完成例を見ながら、
各自で足りない部分を入力して完成させていきましょう。

入力が必要な行には、目印として行頭に**#**の文字を配置しています。

**3-1～3-5** のファイルは、検証用スクリプトの実行時に必要となりますので、

入力漏れや文字の間違いがないように、
じっくりと完成後のソースファイルと見比べながら、正確に入力するようにしてください。


```sh:ディレクトリ構成
Example_RSA
├─rsa_test.py  ..................... 3-0
├─rsa_main_mode_txt.py  ............ 3-1
├─rsa_main_mode_bin.py  ............ 3-2
├─print_FileHash.py ................ 3-3
├─rsa_trial_division.py  ........... 4-4
│
├─my_modules
│  ├─getch.py  ..................... 3-4
│  └─rsa.py  ....................... 3-5
│
├─TestScripts_mode_txt  ............ 4-1
│  ├─bash  ......................... 4-1-A
│  │ ├─original_utf8.txt
│  │ ├─repeated_main_mode_txt.sh
│  │ └─test_mode_txt.sh
│  │
│  └─ps1  .......................... 4-1-B
│    ├─original_sjis.txt
│    ├─repeated_main_mode_txt.ps1
│    └─test_mode_txt.ps1
│
└─TestScripts_mode_bin  ............ 4-2
   ├─bash  ......................... 4-2-A
   │ ├─original.txt
   │ ├─original.jpg
   │ ├─repeated_main_mode_bin.sh
   │ └─test_mode_bin.sh
   │
   └─ps1  .......................... 4-2-B
     ├─original.txt
     ├─original.jpg
     ├─repeated_main_mode_bin.ps1
     └─test_mode_bin.ps1

```


# 3. プログラムの作成（入力）


* テキスト形式のみに対応した [rsa_main_mode_txt.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_txt.py)

* バイナリ形式に対応した [rsa_main_mode_bin.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_bin.py)


２つのメインプログラムがあります。

## 3-0. [rsa_test.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_test.py)

鍵ペアの生成（ファイルには出力しない）、元テキストの暗号化、復号のみを行うプログラムです。
**3-5. my_modules/rsa.py** の入力が終わったら、動作テストとして実行してみることにします。


## 3-1. [rsa_main_mode_txt.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_txt.py)

テキスト形式のみに対応した暗号化／復号処理プログラムです。
暗号化後の結果、復号後の結果は標準出力に表示されます。

結果をファイルに保存したい場合は、リダイレクトを利用してください。

今回のハンズオンでは、**rsa_main_mode_txt.py** の説明は割愛いたします。

## 3-2. [rsa_main_mode_bin.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_bin.py)

テキストファイルとバイナリファイルの両方に対応した暗号化／復号処理プログラムです。
暗号化後の結果、復号後の結果は、引数で指定したファイルに書き出されます。

## 3-3. [print_FileHash.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/print_FileHash.py)

ファイルのハッシュ値を算出するプログラムです。
暗号化前のファイルとと暗号化／復号後のファイルの内容が、一致しているか調べる場合に使用します。

## 3-4. [my_modules/getch.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/my_modules/getch.py)

キーボードからの文字の読み込みを行う処理をまとめたライブラリです。
ファイルの上書き保存の確認時などに使用します。

すでに完成済みを配布していますので、編集は不要です。

## 3-5. [my_modules/rsa.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/my_modules/rsa.py)

素数の生成、RSA公開鍵暗号化方式による鍵ペアの生成、暗号化、復号などの処理を関数化したライブラリです。

鍵の生成、暗号化、復号の関数は、下記の記事を参考にさせていただいております。

**Python で公開鍵暗号アルゴリズム RSA を実装してみる**
https://qiita.com/QUANON/items/e7b181dd08f2f0b4fdbe


# 4. 動作テスト

入力が終わったら、動作テストとして **3-0.**で作成した **[rsa_test.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_test.py)** を実行してみます。

**python** で **rsa_test.py** を実行してみましょう。

```
$ python rsa_test.py
p = 2551, q = 2207
秘密鍵:(E = 7, N = 5630057)
公開鍵:(D = 2410843, N = 5630057)
平文:この文字列を暗号化／復号化するよ♪
暗号文:004aeb7c00172f07001bf76d0011d53a0034dd090042153a00430f3200084a7b00435a8b001df9180016383700084a7b00435a8b0009e53500195b20002641cd002a999e
平文:この文字列を暗号化／復号化するよ♪
Success.
```

２つの平文の内容が一致しており、実行結果として**Success.**と表示されました。
暗号化／復号に成功しているようです。


## 4-1. 検証用スクリプトによる動作テスト([rsa_main_mode_txt.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_txt.py)用)

**[rsa_main_mode_txt.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_txt.py)** の検証用スクリプト群です。
**bash版**と**PowerShell版**の２つがあります。

**macOS/Linux**を使用している方は**bash版**を、
**Windows**を利用している方は**PowerShell版**を使用してください。

* **4-1-A. TestScripts_mode_txt/bash ... bash版**
* **4-1-B. TestScripts_mode_txt/ps1 ..... PowerShell版**

今回のハンズオンでは、**4-1.**のスクリプト群による検証は割愛します。

## 4-2. 検証用スクリプトによる動作テスト([rsa_main_mode_bin.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_bin.py)用)

**[rsa_main_mode_bin.py](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_bin.py)** の検証用スクリプト群です。
bash版とPowerShell版の２つがあります。

**macOS/Linux**を使用している方は**bash版**を、
**Windows**を利用している方は**PowerShell版**を使用してください。

* **4-2-A. TestScripts_mode_bin/bash ... bash版**
* **4-2-B. TestScripts_mode_bin/ps1 ..... PowerShell版**



### 4-2-A. [TestScripts_mode_bin/bash](https://github.com/NobuyukiInoue/Example_RSA/tree/master/TestScripts_mode_bin/bash)

* 鍵ペアの生成、**公開鍵**による暗号化、**秘密鍵**による復号処理、元ファイルとの比較
* 鍵ペアの生成、**秘密鍵**による暗号化、**公開鍵**による復号処理、元ファイルとの比較

をそれぞれ10回づつ実行する検証用スクリプト群(bash版)です。

* カレントディレクトリを変更

```
$ cd TestScripts_mode_bin/bash
```

* カレントディレクトリのパスを確認

```
$ pwd
/home/s??/Example_RSA_Work/TestScripts_mode_bin/bash
```

* ファイル一覧の確認

```
$ ls -al
合計 68
drwxr-xr-x 2 sxx students  4096  4月 23 20:54 .
drwxr-xr-x 4 sxx students  4096  4月 23 20:54 ..
-rw-r--r-- 1 sxx students 46948  4月 23 20:54 original.jpg
-rw-r--r-- 1 sxx students    32  4月 23 20:54 original.txt
-rw-r--r-- 1 sxx students   488  4月 23 20:54 repeated_main_mode_bin.sh
-rw-r--r-- 1 sxx students  3654  4月 23 20:54 test_mode_bin.sh
```

* 検証用スクリプトの実行

```
$ ./repeated_main_mode_bin.sh
...
...
args = ./test_mode_bin.sh public.key private.key ./original.jpg ./test.bin ./test.jpg 0
Execute: python ../../rsa_main_mode_bin.py create_key
Execute: python ../../rsa_main_mode_bin.py encrypt ./original.jpg ./test.bin private.key
Execute: python ../../rsa_main_mode_bin.py decrypt ./test.bin ./test.jpg public.key
public.key          :3,4512649
private.key         :751361,4512649
./original.jpg      :MD5 : c9dc1e2b3135d0f76203de5820b840f5
./test.jpg          :MD5 : c9dc1e2b3135d0f76203de5820b840f5
<<Success>>
```

確認が終わったら、カレントディレクトリを**Example_RSA_Workディレクトリ**に戻しておきます。

```
$ cd ../..
$ pwd
/home/s??/Example_RSA_Work
```



### 4-2-B. [TestScripts_mode_bin/ps1](https://github.com/NobuyukiInoue/Example_RSA/tree/master/TestScripts_mode_bin/ps1)

* 鍵ペアの生成、公開鍵による暗号化、秘密鍵による復号処理、元ファイルとの比較
* 鍵ペアの生成、秘密鍵による暗号化、公開鍵による復号処理、元ファイルとの比較

をそれぞれ10回づつ実行する検証用スクリプト群(PowerShell版)です。


* カレントディレクトリを変更

```
> cd TestScripts_mode_bin/ps1
```

* ファイル一覧の確認

```
> Get-ChildItem
    ディレクトリ: C:\....\Example_RSA_Work\TestScripts_mode_bin\ps1

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/07/02     22:02          46948 original.jpg
-ar---       2019/04/14      0:51             34 original.txt
-a----       2019/04/21     20:55            571 repeated_main_mode_bin.ps1
-a----       2019/04/21     20:52           4309 test_mode_bin.ps1
```

* 検証用スクリプトの実行

```
> ./repeated_main_mode_bin.ps1
...
...
args = test_mode_bin.ps1 ./public.key ./private.key ./original.jpg ./test.bin ./test.jpg False
Execute: python ../../rsa_main_mode_bin.py create_key
Execute: python ../../rsa_main_mode_bin.py encrypt ./original.jpg ./test.bin ./private.key
Execute: python ../../rsa_main_mode_bin.py decrypt ./test.bin ./test.jpg ./public.key
./public.key         : 7,4755749
./private.key        : 678763,4755749
./original.jpg       MD5 : 351efe5e4d33d7ca16c86b3137c78011
./test.jpg           MD5 : 351efe5e4d33d7ca16c86b3137c78011
<<Sucess>>
```

確認が終わったら、カレントディレクトリを**Example_RSA_Workディレクトリ**に戻しておきます。

```
> cd ../..
> pwd
Path
----
C:\Users\.....\Example_RSA_Work
```


## 4-3. 公開鍵と秘密鍵の生成と暗号化／復号の仕組み

公開鍵と秘密鍵の生成と暗号化／復号の仕組みを少しだけ解説します。

4-2-B. の検証用スクリプトの実行結果の例を見ると、
public.keyとprivate.keyの値が以下のように出力されていました。

```
./public.key         : 7,4755749
./private.key        : 678763,4755749
```

**public.key** の最初の値 **7** は、公開鍵を使った**べき乗計算**で使用する**変数E**を表しています。

次に続く **4755749** は、**とある２つの素数(pとq)の積N**です。

（公開鍵の変数Eは、φ(n)=(p-1)(q-1))未満で互いに素な正の整数です。一般的には2^16 + 1 = 65537の固定された値がよく使われますが、今回は毎回異なる値を生成しています。）

**public.key** には、
```E = 7, N = 4755749```
という値が保存されていたことになります。


**private.key** の最初の値 **678763** は、秘密鍵を使った**べき乗計算**で使用する**変数D**を表しています。

次に続く **4755749** は、**とある２つの素数(pとq)の積N**であり、公開鍵の**N**と同じ値を使っています。

（秘密鍵の変数Dは E*D ≡ 1 (mod N) の式を変形した E*D - N*x = gcd(E,N) = 1を、拡張されたユークリッドの互除法で解いて求めた値です。）

**private.key** には、
```D = 678763, N = 4755749```
という値が保存されていたことになります。

ちなみに、**とある２つの素数(pとq)**ですが、どんな値なのでしょうか？

この値がバレてしまうと、簡単に何度でも（公開鍵と）秘密鍵を再生成することができるので、
今回のプログラムでは、乱数を使って２つの素数を選んだあと、鍵ペアの生成後に**保存せずに破棄**しています。

ですので、プログラムを書いた私も解らなくなっています。

興味があれば**N**を素因数分解して求めてみてください（笑)

今回のプログラムでは公開鍵の**E**および**N**は小さめの値になっているので、
強度は高くありません。（実はかなり低い）

詳しくは、[**4-4. 鍵の強度**](https://qiita.com/gx3n-inue/items/d8c33761957ba8c64312#4-4-%E9%8D%B5%E3%81%AE%E5%BC%B7%E5%BA%A6)で取り扱います。


**とある２つの素数(p, q)**の話はここまでにするとして、、、、

それでは、pythonをインタプリタ形式で起動して、これらの値を使って暗号化と復号の実験をしてみましょう。

```
$ python
Python 3.6.5 (default, May  3 2018, 10:08:28)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

pythonが起動したら、変数Nに、とある２つの素数の積である```4755749```を代入しておきます。

```Python

>>> N = 4755749
```

公開鍵で使用する変数Eに```7```を代入します。

```Python
>>> E = 7
```

公開鍵で使用する変数Dに```678763```を代入します。

```
>>> D = 678763
```

暗号化は、```元の値をE乗したあと、Nで割って余りを得る```という計算で行うことができます。
pythonだと、```pow(元の値, E, N)```という式になります。

暗号化したい**元の値**を***999***として、公開鍵で暗号化する演算を行ってみましょう。

```Python
>>> pow(999, E, N)
1878484
```

**1878484**という結果が返されました。
この数値が暗号化後の値です。

次に、秘密鍵による復号を行ってみます。

復号は、```暗号化後の値をD乗したあと、Nで割って余りを得る```という計算で行うことができます。
pythonだと、```pow(暗号化後の値, D, N)```という式になります。

**暗号化後の値**を、さきほどの計算結果である***1878484***として、秘密鍵で復号する演算を行ってみましょう。

```Python
>>> pow(1878484, D, N)
999
```

あら不思議！！！
元の値である**999**が返されました。

暗号化と復号の処理が成功したことになります。

では、今度は逆に、秘密鍵での暗号化、公開鍵での復号を行ってみましょう。

先に、秘密鍵での暗号化を行ってみます。

```Python
>>> pow(999, D, N)
3065016
```

続いて、公開鍵での復号を行ってみます。

```Python
>>> pow(3065016, E, N)
999
```

こちらの処理でも、元の値が返された様子が解ります。

公開鍵と秘密鍵はペアとして使用する不思議な値（笑）の組み合わせで、
この組み合わせを使うと、

* **公開鍵で暗号化したあと、秘密鍵で復号**
* **秘密鍵で暗号化したあと、公開鍵で復号**

のどちらの計算でも、元の値を復元することができるのです。

### 補足
起動していたpythonですが、

Windows ........ CTRL + Z
macOS/Linux .... CTRL + D

を入力して終了させておいてください。

## 4-4. 鍵の強度

**4-3. 公開鍵と秘密鍵の生成と暗号化／復号の仕組み** にて、
**とある２つの素数の組み合わせの積N**の話題を出しましたが、

実際にプログラムを組んで素因数分解を行うと、どれくらいの時間がかかるのでしょうか？

与えられた数値を素因数分解し、
２つの素数が見つかった場合には鍵ペアを生成する
プログラムも用意しています。

* [素因数分解および鍵生成プログラム（rsa_trial_division.py）](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_trial_division.py)

実行方法は以下の通りです。

```python rsa_trial_division.py 素因数分解したい値```

ちょっと実験してみましょう。

```
$ python rsa_trial_division.py 4755749
4755749 < pow(2, 23)
[1879, 2531]

Do you want to create a key? (Y/N):
Public key filename [rsa_public.key]:recalc_public.key      <--- 再生成する公開鍵のファイル名
Private key filename [rsa_private.key]:recalc_private.key   <--- 再生成する秘密鍵のファイル名
```

一瞬で計算が終わりました。
素因数分解の結果として、２つの数値 **[1879, 2531]** が出力されました。

**4755749 < pow(2, 23)**という結果も表示されていますが、
これは **4755749** が **2の23乗** 未満だったことを表しています。
（すなわち、**鍵長は23ビット**だった）

この２つの値が、**とある２つの素数(pとq)** なのでしょうか？

電卓もしくはPythonなどで **1879*2531** を再計算して確かめると、
**4755749**になります。

ちなみに、**1879** や **2531** を **rsa_trial_division.py** の引数で与えて
素因数分解してみると、

それ以上は素因数分解できず、２つの値とも素数だということが解ります。

```
$ python rsa_trial_division.py 1879
1879 < pow(2, 11)
1879 is a prime number.
```

```
$ python rsa_trial_division.py 2531
2531 < pow(2, 12)
2531 is a prime number.
```

プログラムは、素因数分解の結果が２つの値だけだった場合には、
鍵ペアも生成してくれています。

再計算して生成した鍵ファイルを確認してみましょう。
ますは **ls -alコマンド** の実行結果から。

```
$ ls -al
...
...
-rw-r--r-- 1 s19 students   14  4月 23 21:42 recalc_private.key <--- 4-4. 再計算して生成した秘密鍵ファイル
-rw-r--r-- 1 s19 students    9  4月 23 21:42 recalc_public.key  <--- 4-4. 再計算して生成した公開鍵ファイル
```

**公開鍵ファイル**、**秘密鍵ファイル**ともに生成されているようです。
しかも、ファイルのサイズも見覚えがありますね。

catコマンドでファイルの内容を表示してみます。

```
$ cat recalc_public.key
7,4755749
```

```
$ cat recalc_private.key
678763,4755749
```

**「おぉぉぉ。。。。！！！」**

[4-3. 公開鍵と秘密鍵の生成と暗号化／復号の仕組み](https://qiita.com/gx3n-inue/items/d8c33761957ba8c64312#4-3-%E5%85%AC%E9%96%8B%E9%8D%B5%E3%81%A8%E7%A7%98%E5%AF%86%E9%8D%B5%E3%81%AE%E7%94%9F%E6%88%90%E3%81%A8%E6%9A%97%E5%8F%B7%E5%8C%96%E5%BE%A9%E5%8F%B7%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF) で調べた **E, D, N** と同じ値が表示されました。

公開鍵の変数Nから、あっというまに公開鍵と秘密鍵が再生成できてしまった...ｗｗ

**「どうしよう♪♪♪♪」**
**「HackerzLab.博多 ハンズオン用 Example_RSA の脆弱性を発見してしまった...!!」**

というやつでしょうか(笑)

ちょっとだけハッカーになってしまった気分ですね。

それはさておき、

さきほどの素因数分解用プログラム **rsa_trial_division.py** に与える数値を大きくしていくと、
素因数分解にかかる時間も増えていきます。

一桁ずつ増やしていくと、どれくらい処理時間も増えていくのか、体験してみてください。

### 推奨される鍵長

今回のハンズオン用のプログラムでは、セキュリティ強度よりもプログラムの簡潔さや理解のしやすさ、処理の軽快さを重視しているため、鍵長は21～23ビット程度に抑えています。

2004年の時点でも、インターネットで公募した数多くのPCを用いると512ビット程度の数なら素因数分解できていました。

現在では、RSA暗号に使用する **とある２つの素数の積N** は**1024-4096ビット（10進数で300-1000桁程度）**にすることが推奨されています。

**鍵長の推奨値**は、PCの処理速度向上に伴い、今後も**大きくなり続ける**ことは間違いありません。

### **Example_RSA** で鍵長を大きくするには？

**Example_RSA**でも、プログラムを修正すれば鍵長を大きくすることができます。

* [**rsa_main_mode_txt.py**](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_txt.py)
* [**rsa_main_mode_bin.py**](https://github.com/NobuyukiInoue/Example_RSA/blob/master/rsa_main_mode_bin.py)

* [**my_modules/rsa.py**](https://github.com/NobuyukiInoue/Example_RSA/blob/master/my_modules/rsa.py)

のどこを修正すれば良いのか、プログラムをよく読んで考えてみましょう。
 
### PEM/DERファイルについて

暗号化 – .pem、.cer、および.derの違いは何ですか？
https://codeday.me/jp/qa/20190130/213979.html

RSA暗号とPEM/DERの構造
https://www.sambaiz.net/article/135/


# 5. 暗号化データの送付とディジタル署名による改ざん防止の実験

完成したプログラムを使って、暗号化データの送付とディジタル署名による検証の実験を行ってみます。

公開鍵暗号化方式による暗号化データの送信や、ディジタル署名による検証の概念をまとめると、以下の図のようになります。


<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262326/646b9bf2-5512-c42b-8fd1-9fe22b6a9d37.png>

今回のハンズオンでは、以下の流れで作業を進めていきます。

* 事前準備
5-1. 実験用鍵ペアの生成
5-2. 公開鍵の配布（共有フォルダへの配置）

* 送信者側の作業
5-3. 暗号化前の元ファイルの作成
5-4. 送りたいファイルを受信者の公開鍵で暗号化する
5-5. 暗号化したファイルをハッシュ化し、メッセージダイジェストを生成する
5-6. 得られたメッセージダイジェストを送信者の秘密鍵で暗号化する
5-7. 暗号化したファイルとディジタル署名を受信者側に送る

* 受信者側の作業
5-8. 暗号化されたファイルとディジタル署名を受け取る
5-9. 受け取ったディジタル署名を送信者の公開鍵で復号し、暗号化されたファイルをハッシュ化して得られるメッセージダイジェストと比較する
5-10. 暗号化されたファイルを受信者の秘密鍵で復号し、元のデータを得る


## 5-1. 実験用鍵ペアの生成

カレントディレクトリを **Example_RSA_Work** を展開したディレクトリに変更します。

```
$ cd ~/Example_RSA_Work
```

鍵ペアを生成します。
ただし鍵のファイル名は、ほかの参加者とのファイル名の重複を避けるため、

* 公開鍵ファイル：　**s??_public.key**
* 秘密鍵ファイル：　**s??_private.key**

としてください。 **s??** は各自に割り当てられた番号です。

```
s19@ubuntu1804:~/Example_RSA$ python rsa_main_mode_bin.py create_key
Public key filename [rsa_public.key]:s19_public.key    <--- 自分用の公開鍵のファイル名を入力
Private key filename [rsa_private.key]:s19_private.key <--- 自分用の秘密鍵のファイル名を入力
Create Keys done.
```

作成された鍵ファイルを**lsコマンド**で確認します。

```
s19@ubuntu1804:~/Example_RSA$ ls -al
合計 80
drwxr-xr-x 9 s19 students 4096  4月 23 21:42 .
drwxr-xr-x 6 s19 students 4096  4月 23 21:08 ..
drwxr-xr-x 8 s19 students 4096  4月 23 21:08 .git
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 .vscode
-rw-r--r-- 1 s19 students 8525  4月 23 21:08 README.md
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_bin
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_txt
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 base64_work
drwxr-xr-x 3 s19 students 4096  4月 23 21:08 my_modules
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 org
-rw-r--r-- 1 s19 students  958  4月 23 21:08 print_FileHash.py
-rw-r--r-- 1 s19 students 4395  4月 23 21:08 rsa_main_mode_bin.py
-rw-r--r-- 1 s19 students 4868  4月 23 21:08 rsa_main_mode_txt.py
-rw-r--r-- 1 s19 students 1394  4月 23 21:08 rsa_test.py
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key <--- 5-1. 自分用の秘密鍵ファイル
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key  <--- 5-1. 自分用の公開鍵ファイル
```

## 5-2. 公開鍵の配布（共有フォルダへの配置）

参加者の公開鍵および電子メールアドレスをメンバー間で共有（公開）するため、
講師側が指示した**公開鍵配置用共有フォルダ**に公開鍵を配置します。

☆公開鍵配置用共有フォルダ案
\\\ubuntu1804\share

配置が終わったら、暗号化データの送信相手の公開鍵を
Example_RSA_Workの作業ディレクトリ(~/Example_RSA)にコピーしておきます。

作業の詳細は、当日、会場にて説明いたします。

**s19**さんが**s00**さんの公開鍵を作業ディレクトリ(**~/Example_RSA_Work**)にコピーして配置した場合、
下記のようなファイル配置になります。

```
s19@ubuntu1804:~/Example_RSA$ ls -al
合計 80
drwxr-xr-x 9 s19 students 4096  4月 23 21:42 .
drwxr-xr-x 6 s19 students 4096  4月 23 21:08 ..
drwxr-xr-x 8 s19 students 4096  4月 23 21:08 .git
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 .vscode
-rw-r--r-- 1 s19 students 8525  4月 23 21:08 README.md
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_bin
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_txt
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 base64_work
drwxr-xr-x 3 s19 students 4096  4月 23 21:08 my_modules
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 org
-rw-r--r-- 1 s19 students  958  4月 23 21:08 print_FileHash.py
-rw-r--r-- 1 s19 students 4395  4月 23 21:08 rsa_main_mode_bin.py
-rw-r--r-- 1 s19 students 4868  4月 23 21:08 rsa_main_mode_txt.py
-rw-r--r-- 1 s19 students 1394  4月 23 21:08 rsa_test.py
-rw-r--r-- 1 s19 students    9  4月 23 21:40 s00_public.key  <--- 5-2. 送信相手の公開鍵ファイル
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key <--- 5-1. 自分用の秘密鍵ファイル
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key  <--- 5-1. 自分用の公開鍵ファイル
```


## 5-3. 暗号化前の元ファイルの作成

**暗号化データ**の送付と**ディジタル署名（メッセージダイジェスト）**の確認の実験として
**暗号化前の元ファイル**を作成しておきます。

WindowsやmacOSなどのGUI操作で作成・入力してかまいません。

ファイル名は、**s??_data.txt** とします。
(s?? は各自に割り当てられた番号です。）

今回の環境ではWindows/macOS/Linuxユーザーが混在しているため、
文字コードを(macOS/Linuxのデフォルト値に）統一しておくことにします。

一般的なテキストエディタでは、ファイルに保存する際に、文字コードと改行コードを指定する機能があります。
各自で調べてみてください。

|文字コード|改行コード|
|---|---|
|UTF8|LFのみ|


```sh:s??_data.txt
お世話になっております。
s??（○○）です。

メッセージを暗号化して送ります。
読めるかなぁぁ？？

```

lsコマンドでファイルの一覧を確認しておきます。

```
s19@ubuntu1804:~/Example_RSA_Work$ ls -al
合計 84
drwxr-xr-x 9 s19 students 4096  4月 23 21:49 .
drwxr-xr-x 6 s19 students 4096  4月 23 21:08 ..
drwxr-xr-x 8 s19 students 4096  4月 23 21:08 .git
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 .vscode
-rw-r--r-- 1 s19 students 8525  4月 23 21:08 README.md
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_bin
drwxr-xr-x 4 s19 students 4096  4月 23 21:08 TestScripts_mode_txt
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 base64_work
drwxr-xr-x 3 s19 students 4096  4月 23 21:08 my_modules
drwxr-xr-x 2 s19 students 4096  4月 23 21:08 org
-rw-r--r-- 1 s19 students  958  4月 23 21:08 print_FileHash.py
-rw-r--r-- 1 s19 students 4395  4月 23 21:08 rsa_main_mode_bin.py
-rw-r--r-- 1 s19 students 4868  4月 23 21:08 rsa_main_mode_txt.py
-rw-r--r-- 1 s19 students 1394  4月 23 21:08 rsa_test.py
-rw-r--r-- 1 s19 students    9  4月 23 21:40 s00_public.key  <--- 5-2. 送信相手の公開鍵ファイル
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key
-rw-r--r-- 1 s19 students  140  4月 23 21:52 s19_data.txt    <--- 5-3. 暗号化前元ファイル
```

## 5-4. 送りたいファイルを受信者の公開鍵で暗号化する

5-2.で取得した **送信相手の公開鍵** で、**元ファイル(s??_data.txt)**を暗号化します。

暗号化後のファイル名は、（送信相手毎に異なる公開鍵を使用するため）、
**s??_data_to_sXX.bin** とします。
（**s??は送信者（自分）**の番号、**sXXは送信相手**の番号です。）

**rsa_main_mode.py** の引数は以下のようになります。

```python rsa_main_mode_bin.py encrypt 元ファイル名 暗号化後のファイル名 送信相手の公開鍵ファイル名```

公開鍵ファイルは、送信相手の公開鍵を指定してください。
**s19**さんが**s00**さん宛に送信する場合、以下のようになります。

```
s19@ubuntu1804:~/Example_RSA_Work$ python rsa_main_mode_bin.py encrypt s19_data.txt s19_data_to_s00.bin s00_public.key
```

暗号化が終わったら、暗号化後のファイルが生成されているか確認しておきます。

```
s19@ubuntu1804:~/Example_RSA_Work$ ls -al
...
...
-rw-r--r-- 1 s19 students    9  4月 23 21:40 s00_public.key       <--- 5-2. 送信相手の公開鍵ファイル
-rw-r--r-- 1 s19 students  140  4月 23 21:52 s19_data.txt         <--- 5-3. 暗号化前元ファイル
-rw-r--r-- 1 s19 students 1120  4月 23 21:53 s19_data_to_s00.bin  <--- 5-4. s00の公開鍵で暗号化したファイル
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key
```


## 5-5. メッセージダイジェスト（送信する暗号化データのハッシュ値）の生成

**送信データ（暗号化後のファイル）**の**メッセージダイジェスト（送信する暗号化データのハッシュ値）**を出力します。

**print_FileHash.py** の引数として、**送信データ（暗号化後のファイル）**と**MD5**を指定して実行します。
出力されるハッシュ値は、暗号化されたデータ毎に違う結果になります。

```
s19@ubuntu1804:~/Example_RSA_Work$ python print_FileHash.py s19_data_to_s00.bin MD5
MD5 : ce6596060ff8e49bd971c0f13191e2c4
```

出力結果は、下記のように標準出力を、ファイルにリダイレクトするか、またはOut-Fileコマンドレットでファイルに書き出すと良いでしょう。

```
> python .\print_FileHash.py .\s19_data_to_s00.bin MD5 | Out-File -Encoding Ascii  s19_data_to_s00.MD5.unicode
```

または、

```
> python .\print_FileHash.py .\s19_data_to_s00.bin MD5 > s19_data_to_s00.MD5.unicode
```

Windowsの**PowerShell**では、リダイレクトで出力した場合、出力後のファイルの文字コードが**UNICODE**になります。
（Windowsのコマンド プロンプト上でのリダイレクトは**SJIS**です。）

送信者側と受信者側で文字コード（改行コード）が一致していないと、
復号後の結果が変わってしまいますので、

Windowsユーザーの方は、この後の作業で、文字コードの変換を行います。

```
s19@ubuntu1804:~/Example_RSA_Work$ python print_FileHash.py s19_data_to_s00.bin MD5 > s19_data_to_s00.MD5
```

正しくファイルに出力されているか、**テキストエディタ**または**catコマンド**で確認します。

Windowsユーザーの方は、**テキストエディタ**で開いたあと、
**UTF8 & LFのみ** を指定して上書き保存しておいてください。


```
s19@ubuntu1804:~/Example_RSA_Work$ cat s19_data_to_s00.MD5
MD5 : ce6596060ff8e49bd971c0f13191e2c4
```

```
s19@ubuntu1804:~/Example_RSA_Work$ ls -al
...
...
-rw-r--r-- 1 s19 students    9  4月 23 21:40 s00_public.key       <--- 5-2. 送信相手の公開鍵ファイル
-rw-r--r-- 1 s19 students  140  4月 23 21:52 s19_data.txt         <--- 5-3. 暗号化前元ファイル
-rw-r--r-- 1 s19 students   39  4月 23 21:54 s19_data_to_s00.MD5  <--- 5-5. メッセージダイジェスト
-rw-r--r-- 1 s19 students 1120  4月 23 21:53 s19_data_to_s00.bin  <--- 5-4. s00の公開鍵で暗号化したファイル
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key

```

### 補足）PowerShellのリダイレクト結果(Unicode & CRLF)をコマンドでUTF8 & LFに変換する

PowerShellのリダイレクト結果(Unicode & CRLF)を、PowerShellのコマンドでコード変換することもできます。

手順は以下の通りです。

①．ハッシュ出力をリダイレクトで作業ファイル(s19_data_to_s00.MD5.unicode)に出力します。

```
> python .\print_FileHash.py .\s19_data_to_s00.bin MD5 > s19_data_to_s00.MD5.unicode
```

②．Get-ContentコマンドおよびSet-Contentコマンドでコードを変換し、ファイルに再出力します。

```
> (Get-Content -Path "s19_data_to_s00.MD5.unicode" -Encoding Unicode) -join "`n" `
    | % { [Text.Encoding]::UTF8.GetBytes($_) } `
    | Set-Content -Path "s19_data_to_s00.MD5" -Encoding Byte
```

③．MD5ファイルが作成されているか確認します。
（念のため、テキストエディタでも保存されているコードを確認してください。）

```
> Get-Content .\s19_data_to_s00.MD5
```

④．作業ファイル(s19_data_to_s00.MD5.unicode)を削除します。

```
> rm s19_data_to_s00.MD5.unicode
```


## 5-6. ディジタル署名の作成

**5-5.**で作成した**ハッシュ値**の出力結果(**s??_data_to_sXX.MD5**)から、
**送信者（自分）の秘密鍵**で暗号化した**ディジタル署名**を作成します。

生成する**ディジタル署名**の**ファイル名**は、 **s??_data_to_sXX.sig** としてください。
（**s??は送信者（自分）**の番号、**sXXは送信相手**の番号です。）

```
s19@ubuntu1804:~/Example_RSA_Work$ python rsa_main_mode_bin.py encrypt s19_data_to_s00.MD5 s19_data_to_s00.sig s19_private.key

```

```
s19@ubuntu1804:~/Example_RSA_Work$ ls -al
...
...
-rw-r--r-- 1 s19 students    9  4月 23 21:40 s00_public.key       <--- 5-2. 送信相手の公開鍵ファイル
-rw-r--r-- 1 s19 students  140  4月 23 21:52 s19_data.txt         <--- 5-3. 暗号化前元ファイル
-rw-r--r-- 1 s19 students   39  4月 23 21:54 s19_data_to_s00.MD5  <--- 5-5. メッセージダイジェスト
-rw-r--r-- 1 s19 students 1120  4月 23 21:53 s19_data_to_s00.bin  <--- 5-4. s00の公開鍵で暗号化したファイル
-rw-r--r-- 1 s19 students  312  4月 23 21:55 s19_data_to_s00.sig  <--- 5-6. ディジタル署名
-rw-r--r-- 1 s19 students   14  4月 23 21:42 s19_private.key
-rw-r--r-- 1 s19 students    9  4月 23 21:42 s19_public.key

```

## 5-7. 暗号化ファイルおよびディジタル署名のメール送信

各自のメールソフトを使って、送信相手宛のインターネットメールを作成します。
送信するメールには、以下の２つのファイルを添付してください。

**5-4. で作成した暗号化後のファイル(s??_data_to_sXX.bin)**
**5-6. で作成したディジタル署名(s??_data_to_sXX.sig)**

（s??は送信者（自分）の番号、sXXは送信相手の番号です。）

メールの作成が終わったら、送信してみましょう。

## 5-8. メールの受信の添付ファイルの配置

電子メールを受け取った方は、
メール内の２つの添付ファイルをExample_RSA_Workの作業ディレクトリに配置してください。

添付ファイルには、

**ディジタル署名(sYY_data_to_s??.sig)（5-9.の作業にて、相手の公開鍵で復号）**
**暗号化データ(sYY_data_to_s??.bin)を（5-10.の作業にて、自分の秘密鍵で復号）**

（**sYYは送信元者**の番号、**s??は受信者（自分）**の番号です。）

の２つのファイルがあるはずです。

送信元者がファイル名を間違って付与していた場合、
自分が作ったファイルに上書きされる恐れがありますので、
ご注意ください。


## 5-9. ディジタル署名の復号

暗号化ファイルの復号およびディジタル署名の確認

受け取った暗号化ファイルとディジタル署名（メッセージダイジェスト）が一致しているか確認します。

**rsa_main_mode_bin.py** の引数は以下のようになります。

```python rsa_main_mode_bin.py decrypt 受け取ったディジタル署名（sigファイル） 復号後のメッセージダイジェストファイル名 相手の公開鍵ファイル名```


ディジタル署名（メッセージダイジェスト）の復号の作業例

```
s00@ubuntu1804:~/Example_RSA_Work$ python rsa_main_mode_bin.py decrypt s19_data_to_s00.sig s19_data_to_s00.MD5 s19_public.key
```

復号されたメッセージダイジェストを確認します。

```
s00@ubuntu1804:~/Example_RSA_Work$ cat s19_data_to_s00.MD5
MD5 : ce6596060ff8e49bd971c0f13191e2c4
```


受け取った**暗号化データ(sYY_data_to_s??.bin)**から、**メッセージダイジェスト**を再計算します。

```
s00@ubuntu1804:~/Example_RSA_Work$ python print_FileHash.py s19_data_to_s00.bin MD5
MD5 : ce6596060ff8e49bd971c0f13191e2c4
```

一致していますね。

一致していれば、受け取った**暗号化データ(sYY_data_to_s??.bin)**は、
間違いなく**sYYの秘密鍵の所有者本人**からの送信データであり、
（sYYの秘密鍵が盗聴されたり、解読されていないという前提であれば）

途中で第三者による改ざんが行われていないことが確認できたことになります。



## 5-10. 暗号化データの復号

次に、

* **s??_data.bin（暗号化後のファイル）**を**自分の秘密鍵**で復号

します。

**rsa_main_mode_bin.py** の引数は以下のようになります。

```python rsa_main_mode_bin.py decrypt 受け取った暗号化データファイル名 復号後のファイル名 自分の秘密鍵```


```
s00@ubuntu1804:~/Example_RSA_Work$ python rsa_main_mode_bin.py decrypt s19_data_to_s00.bin s19_data.txt s00_private.key
```

復号が終わったら、テキストエディタまたはcatコマンドでファイルの内容を確認します。

```
s00@ubuntu1804:~/Example_RSA_Work$ cat s19_data.txt
お世話になっております。
s??（○○）です。

メッセージを暗号化して送ります。
読めるかなぁぁ？？
```

正しく、復号できたようです。

（みんなうまくいって、よかった...よかった...めでたしめでたし..となっていることを願う）


# 6. 公開鍵暗号化方式の代表的な暗号化ツール

RSA暗号化方式を実装したツールとしては、PGP, OpenSSLなどがあります。

* [Pretty Good Privacy（出典: フリー百科事典『ウィキペディア（Wikipedia）』）](https://ja.wikipedia.org/wiki/Pretty_Good_Privacy)
* [OpenSSL（出典: フリー百科事典『ウィキペディア（Wikipedia）』）](https://ja.wikipedia.org/wiki/OpenSSL)

これらのツールでも、Example_RSAと同様の考え方で、
鍵ペアの作成や、ディジタル署名やファイルの暗号化／復号の操作ができます。

各自で使い方を調べてみましょう。

そのほか、Python3で鍵の生成、暗号化／復号／電子署名の処理を作成したいのであれば、下記のツールが便利です。

* pycryptodome

https://www.pycryptodome.org/en/latest/

Example_RSAと同等の操作方法をPyCryptodomeで置き換えたプロジェクトも用意しています。

* PyCryptodomeを利用したファイルの暗号化／復号処理および電子署名の生成／照合処理の作成例

https://github.com/NobuyukiInoue/Example_RSA_by_pycryptodome