共通鍵暗号化のプログラムサンプル。.Netのライブラリを使用したもの。
プログラムの概要
まず暗号化のためのパスワードを入力させ、テキストを暗号化する。
その後、復号化のパスワードを入力させ、暗号化したデータを復号化する。
暗号化パスワードと復号化パスワードの不一致で、復号化が失敗したら、メッセージを表示する。
ソルトは、暗号化時に作成し文字列で内部で保存しておき、復号化時には、それを使用して復号化のデータを作成する。
ソルトの作成
RNGCryptoServiceProvider
を使用し作成した情報をHEX化文字列にしたものを保持するようにしている。ただHEX化文字列をByte[]に変換する処理で、-(ハイフン)を考慮していない実装にしているので、HEX化する際に-(ハイフン)を除去する処理を入れている。
AESキーの作成
Rfc2898DeriveBytes
を使用しKeyとIVを作成している。
ただ、KeyとIVの作成方法として、Rfc2898DeriveBytes
を利用している意味が実を言うとよくわかっていない。KeyとIVを作成するコードをググって検索した場合、ほぼすべてが同様な実装を使っていたため、これを使用した。
復号化
復号化のパスワード間違い時の動作も検証したかったので、復号化処理には、例外処理を入れてある。
復号化処理で何らかの間違いがあった場合、CryptographicException
が発生する。
今回のプログラムでの発生個所は、DecryptStringFromBytes
のplaintext = srDecrypt.ReadToEnd();
。
DecryptStringFromBytes
の中で利用されるメソッドでCryptographicException
が発生するとは、MSDNに記載されていなかったので、プログラムを走らせてエラーが出てから例外処理を入れた。
サンプルコード
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace AESTest1
{
class Program
{
static byte[] EncryptStringToBytes(string plainText, ICryptoTransform encryptor)
{
byte[] encrypted;
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
return encrypted;
}
static string DecryptStringFromBytes(byte[] cipherText, ICryptoTransform decryptor)
{
string plaintext = null;
using (MemoryStream msDecrypt = new MemoryStream(cipherText)) {
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) {
using (StreamReader srDecrypt = new StreamReader(csDecrypt)) {
plaintext = srDecrypt.ReadToEnd();
}
}
}
return plaintext;
}
static void Main(string[] args)
{
string plainText = "Here is some data to encrypt!";
// 共通鍵暗号化
// パスワードは、上から与える。
// soltはここで作るが、一度string化したものを再利用する。
// 想定は、設定画面で、パスワードを設定時、soltを自動生成し、それを覚えておく。
// 認証画面では覚えておいたsoltと入力パスワードで復号化する。
using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) {
string soltString; // soltを文字列化したもの
// AESのキー情報を作成する
{
// soltの作成
Byte[] soltByte1 = new byte[8];
using (var rnd = new RNGCryptoServiceProvider()) {
rnd.GetBytes(soltByte1);
}
soltString = BitConverter.ToString(soltByte1).Replace("-", "");
Console.WriteLine("Solt\n{0}\n", soltString);
// キー作成
Console.Write("AES\nEnter crypt password : ");
string password = Console.ReadLine();
var keyCreator = new Rfc2898DeriveBytes(password, soltByte1);
myAes.Key = keyCreator.GetBytes(myAes.KeySize / 8);
myAes.IV = keyCreator.GetBytes(myAes.BlockSize / 8);
}
// 暗号化を行い、その結果をbase64化したテキストで受け取る
string entryptedString = Convert.ToBase64String(EncryptStringToBytes(plainText, myAes.CreateEncryptor()));
Console.WriteLine("Encrypted\n{0}\n", entryptedString);
// AESのキー情報を作成する
{
// soltは文字列化したものを再利用する
var soltByte2 = Enumerable.Range(0, soltString.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(soltString.Substring(x, 2), 16))
.ToArray();
// パスワードの再入力
Console.Write("Enter encrypt password : ");
var password = Console.ReadLine();
var keyCreator = new Rfc2898DeriveBytes(password, soltByte2);
myAes.Key = keyCreator.GetBytes(myAes.KeySize / 8);
myAes.IV = keyCreator.GetBytes(myAes.BlockSize / 8);
}
// 復号化
try {
Console.WriteLine("decrypt text: {0}",
DecryptStringFromBytes(Convert.FromBase64String(entryptedString), myAes.CreateDecryptor()));
}
catch (CryptographicException) {
Console.WriteLine("復号化に失敗しました");
}
}
}
}
}
コメント