Unity(C#) で「正しい」暗号化処理をするライブラリを作成しました

# 概要
Unity(C#)において、<font color="#FF0000">共通鍵暗号</font>の代表格である<font color="#FF0000">[AES暗号](https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard)(正確にはAES暗号ではなくRijndael暗号)</font>と<font color="#ff0000">公開鍵暗号</font>の代表格である[RSA暗号](https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7)、それぞれ手軽に実装でき、セキュアな(解読されづらい)ものになるようにしたライブラリを公開しました。
[UnityCipher](https://github.com/TakuKobayashi/UnityCipher)
unitypackageをご希望の方はreleasesからダウンロードできますのでこちらからダウンロードしてください [UnityCipher Releases](https://github.com/TakuKobayashi/UnityCipher/releases)
[Unity Package Manager(UPM)](https://docs.unity3d.com/ja/2019.4/Manual/Packages.html)を利用してダウンロードする場合は `Packages/manifest.json` に以下の内容を記述

```Packages/manifest.json
{
  "dependencies": {
    "net.taptappun.taku.kobayashi.unitycipher": "https://github.com/TakuKobayashi/UnityCipher.git?path=/Assets/UnityCipher",
    ...
  }
}
```

または `Window` -> `PackageManager` -> `Add package from git URL` にて
`https://github.com/TakuKobayashi/UnityCipher.git?path=/Assets/UnityCipher` を追加することでインストールすることができます。

![windowbar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/9fefb9b1-ac7e-865d-3496-a4892af51f03.png)

![PackageManager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/8ae7074a-45dd-2169-0acb-92b8e359452f.png)

![packageFromGitURL.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/518f6b6a-c107-9e06-3f86-1829bb60cf37.png)

![giturl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72596/d6d518b3-5116-884a-9978-daa73da3b455.png)

### 参考

 * [Unity Package Managerについて](https://qiita.com/k7a/items/cc5d09bc1000551b203f)

# 使い方

詳しい使い方は```UnityCipher/Examples/``` 以下にサンプルがありますので、こちらを確認してください。

また、ライブラリのメソッドを使う場合、```using UnityCipher```を追加することで使用することができます。

## [AES暗号](https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard)を用いる場合
### 暗号化
以下のようなメソッドを呼び出すことで暗号化することができます。

```C#
string encrypted = RijndaelEncryption.Encrypt(planeText, passwordText);
```

こうすることで暗号化されたもの(```encrypted```)を取得することができます。
また、```Encrypt```メソッドの第一引数である、```planeText```は```byte[]```を指定することもでき、この場合、暗号化された```byte[]```を受け取ります。(以下を参考)

```C#
byte[] encrypted = RijndaelEncryption.Encrypt(planeBinary, passwordText);
```

### 復号
以下のようなメソッドを呼び出すことで復号することができます。

```C#
string planeText = RijndaelEncryption.Decrypt(encryptedText, passwordText);
```

暗号化されたものをうまく復号することができれば復号されたもの(```planeText```)を取得することができます。
```Decrypt```メソッドの第一引数である、```encryptedText```には暗号化された```byte[]```を指定することもでき、この場合、うまく復号できれば復号されたされた```byte[]```を受け取ります。(以下を参照)

```C#
byte[] planeBinary = RijndaelEncryption.Decrypt(encryptedBinary, passwordText);
```

## [RSA暗号](https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7)を用いる場合

### 公開鍵と秘密鍵のペアの作成
まずは以下のようなメソッドを呼び出すことで公開鍵と秘密鍵をそれぞれ作成します。

```
KeyValuePair<string, string> publicAndPrivateKeyValuePair = RSAEncryption.GenrateKeyPair(int keySize);
```

このとき、引数に設定したkeySizeに設定した値の大きさの公開鍵、秘密鍵が生成されます。<font color="#FF0000">指定できるkeySizeの値は512~16384の間の8bit単位での数字(8の倍数)になります。</font>
(実際にはkeySizeの値は384以上を作成可能)
詳しくはこちらを参照してください。
[RSACryptoServiceProvider.KeySize Property](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsacryptoserviceprovider.keysize?view=netframework-4.7.2)
<font color="#FF0000">ここで指定する値が大きくなればなるほど、生成される公開鍵、秘密鍵の長さが大きくなり、よりセキュアなものになりますが、同時に暗号化、復号処理にかかる時間も増大します。また、鍵を作成する時間も大きくなります。</font>
生成された公開鍵、秘密鍵は```KeyValuePair<string, string>```の形(```publicAndPrivateKeyValuePair```)で取得でき、それぞれKeyが公開鍵、Valueが秘密鍵となります。

### 暗号化
以下のようなメソッドを呼び出すことで暗号化することができます。

```C#
string encrypted = RSAEncryption.Encrypt(planeText, publicKey);
```

上記で生成した公開鍵(publicKey)を第二引数に指定することでRSA暗号化された暗号文(```encrypted```)を取得することができます。
```Encrypt```の第一引数である、```planeText```は```byte[]```を指定することもでき、この場合、暗号化された```byte[]```を受け取ります。(以下を参照)

```C#
byte[] encrypted = RSAEncryption.Encrypt(planeBinary, publicKey);
```

### 復号
以下のようなメソッドを呼び出すことで暗号化することができます。

```C#
string planeText = RSAEncryption.Decrypt(encryptedText, privateKey);
```

上記で生成した公開鍵(privateKey)を第二引数に指定することで、うまく復号できれば復号されたもの(```planeText```)を取得することができます。
```Decrypt```の第一引数である、```planeText```は```byte[]```を指定することもでき、```byte[]```を指定することもでき、この場合、うまく復号できれば復号されたされた```byte[]```を受け取ります。(以下を参照)

```C#
byte[] planeBinary = RSAEncryption.Decrypt(encryptedBinary, privateKey);
```

# 特徴
このライブラリの特徴として

 * シンプルに使うことができます。
 * 強固なセキュリティで暗号化しています。

以上のような特徴があります。

##### 強固なセキュリティとは?

```UnityCipher``` を使い、共通鍵暗号で暗号化した場合、同じ平文、同じパスワードであっても、暗号化するたびに異なる暗号文が生成されます。また、以下の「仕様」より、暗号化による一定のパターンが観測されるようなこともありません。(暗号化するたびに、Salt、IVを生成しているため)

なお、[RSA暗号](https://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7)はスタンダードなものを使用しているため、今回、詳しい話は省略します。

### よくある話
AES暗号を実装する場合、<font color="#FF0000">平文、パスワード、Salt、IV(初期ベクター)</font>という4種類の要素を設定する必要があります。そして、SaltとIVを定数として設定してしまうことが非常によくあります。しかし、本来、AES暗号の特徴として、<font color="#FF0000">「同じ平文、同じパスワードであっても、暗号化するたびに暗号文が変わる」</font>という特徴があります。
しかし、<font color="#FF0000">IVとソルトを定数であるとこの特徴が全く活きなくなります。</font>(つまり、同じ平文、同じパスワードだと毎回同じ暗号文になる)
実際の運用において、このようなSaltとIVを定数として用いられている場合は非常に多くあります。

### 仕様
今回のライブラリでは、AES暗号において、暗号化するたびにSaltとIVを生成しています。Salt + IV + 暗号文をつなげた```byte[]```を生成し、この```byte[]```を取得できるようにしています。(詳しくはこちら)
これにより、<font color="#FF0000">「同じ平文、同じパスワードであっても、暗号化するたびに暗号文が変わる」</font>ようにしています。

なお、上記の仕様はすでに挙げましたので、よりセキュアに運用したい場合、順番を変えたり、bitのルールを変える、などの独自ルールにすることで対応することもできます。

# 参考・関連
 * [Data_Encryption_Standard](https://ja.wikipedia.org/wiki/Data_Encryption_Standard)
 * [Advanced_Encryption_Standard](https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard)
 * [DES暗号化](http://www.atmarkit.co.jp/ait/articles/1505/21/news030.html)
 * [暗号利用モード](https://ja.wikipedia.org/wiki/%E6%9A%97%E5%8F%B7%E5%88%A9%E7%94%A8%E3%83%A2%E3%83%BC%E3%83%89)
 * [C#（.NET Framework）のAESを使ってファイルを暗号化してみる](https://hibara.org/blog/2011/02/20/c-sharp-net-framework-aes/)
 * [Visual Studio C#でファイルを暗号化してみる](http://qiita.com/hibara/items/c9096376b1d7b5c8e2ae)
 * [プログラマ暗号入門](https://qiita.com/asksaito/items/1793b8d8b3069b0b8d68)