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


概要

Unity(C#)において、共通鍵暗号の代表格であるAES暗号(正確にはAES暗号ではなくRijndael暗号)公開鍵暗号の代表格であるRSA暗号、それぞれ手軽に実装でき、セキュアな(解読されづらい)ものになるようにしたライブラリを公開しました。
UnityCipher
unitypackageをご希望の方はreleasesからダウンロードできますのでこちらからダウンロードしてください UnityCipher Releases
Unity Package Manager(UPM)を利用してダウンロードする場合は 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 を追加することでインストールすることができます。

参考

使い方

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

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

AES暗号を用いる場合

暗号化

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

string encrypted = RijndaelEncryption.Encrypt(planeText, passwordText);

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

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

復号

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

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

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

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

RSA暗号を用いる場合

公開鍵と秘密鍵のペアの作成

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

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

このとき、引数に設定したkeySizeに設定した値の大きさの公開鍵、秘密鍵が生成されます。指定できるkeySizeの値は512~16384の間の8bit単位での数字(8の倍数)になります。
(実際にはkeySizeの値は384以上を作成可能)
詳しくはこちらを参照してください。
RSACryptoServiceProvider.KeySize Property
ここで指定する値が大きくなればなるほど、生成される公開鍵、秘密鍵の長さが大きくなり、よりセキュアなものになりますが、同時に暗号化、復号処理にかかる時間も増大します。また、鍵を作成する時間も大きくなります。
生成された公開鍵、秘密鍵はKeyValuePair<string, string>の形(publicAndPrivateKeyValuePair)で取得でき、それぞれKeyが公開鍵、Valueが秘密鍵となります。

暗号化

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

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

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

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

復号

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

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

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

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

特徴

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

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

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

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

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

なお、RSA暗号はスタンダードなものを使用しているため、今回、詳しい話は省略します。

よくある話

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

仕様

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

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

参考・関連