AES は共通鍵のアルゴリズムで、他の共通鍵の暗号化方式と比べて強力なようです。
今回は、AESS の暗号化・複号化のライブラリの使い方を理解してみたいと思います。
暗号化と復号化のロジック
基本的にとても簡単です。 AesManaged というクラスか、AesCryptoServiceProvider というクラスが存在するので、そのクラスを使って、暗号化、復号化を行います。このライブラリを使う上で重要なコンセプトは、暗号化の Key と、IV (Initialization Vector)というパラメータです。この二つを理解する必要があります。
Key
Key
は暗号化を行うときの共通鍵です。このライブラリのキーサイズを見てみると、256 bit です。AesManaged
クラスは、初期化した際に、Key
と IV
は自動作成されるようです。個別に GenerateKey()
というメソッドも用意されています。その中のコードを見てみましょう。想像のとり、ランダムな数字のジェネレーターでバイト配列を作成しています。
public sealed override void GenerateKey()
{
byte[] key = new byte[KeySize / BitsPerByte];
RandomNumberGenerator.Fill(key);
Key = key;
}
最終的に internal sealed partial class RandmNumberGeneratorImplementation で実装されているので、外からはうかがい知ることができません。これと似たような実装を外部ですると、次のようになるでしょう。 RNGCryptoServiceProviderはまさにランダムの数字を生成してくれますので、とても良い感じです。GetBytes
メソッドで、バイト配列を埋めるところも、結構実装が似ていますね。ちなみに、実装を追っていくと、結局 RandomNumberGeneratorImplementation
にたどり着いたので、多分実装は同じですね。私のマシンでは、new byte[16]
でないと、256 bit になりませんでしたので、こうしています。
private static string GenerateKey()
{
var buffer = new byte[32];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(buffer);
return BitConverter.ToString(buffer).Replace("-", string.Empty);
}
}
IV
IV は (Initialization vector) の略です。リンクをした公式ドキュメントには記述してありますが、ASEのアルゴリズムの初期のトランスフォーメーションの初期値として与えられます。ASEのアルゴリズムで、変換を行っていく過程で、前に変換をした値を使うのですが、1回目の時にはその値がありません。ですので、作成して与える必要があります。これが、IVの概要です。細かいことはよく知りませんが、この記事を理解すると、わかるようになると思います。
実装例
ほとんど公式にあるサンプルそのままですが、出力がどんな感じになるのか試しています。ライブラリが勝手に暗号化・復号化してくれうるので楽ですね。
static void Main(string[] args)
{
// AesCryptoServiceProivder を使うときは CryptoConfig を使うと良い。
// CryptoConfig cryptoConfig = new CryptoConfig();
// var aes = (AesCryptoServiceProvider)CryptoConfig.CreateFromName("AES");
string original = "Here is some data encrypt!";
using (AesManaged myAes = new AesManaged())
{
Console.WriteLine($"KeySize: {myAes.KeySize}");
Console.WriteLine($"KEY (original) {generatedKey}");
Console.WriteLine($"KEY: {Convert.ToBase64String(myAes.Key)}");
Console.WriteLine($"IV: {Convert.ToBase64String(myAes.IV)}");
byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Encrypted: {Encoding.ASCII.GetString(encrypted)}");
Console.Write($"Encrypted (bytes) :");
foreach (var i in encrypted)
{
Console.Write($"{ConvertToHex(i)}:");
}
Console.WriteLine();
Console.WriteLine($"Encrypted (base64): {Convert.ToBase64String(encrypted)}");
Console.WriteLine($"Round Trip: {roundtrip}");
}
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
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_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
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)
{
// CryptoConfig cryptoConfig = new CryptoConfig();
// var aes = (AesCryptoServiceProvider)CryptoConfig.CreateFromName("AES");
var generatedKey = GenerateKey();
Console.WriteLine($"Key Length: {generatedKey.Length}");
string original = "Here is some data encrypt!";
using (AesManaged myAes = new AesManaged())
{
// public sealed override void GenerateKey()
//{
// byte[] key = new byte[KeySize / BitsPerByte];
// RandomNumberGenerator.Fill(key);
// Key = key;
//}
myAes.GenerateKey();
Console.WriteLine($"KeySize: {myAes.KeySize}");
myAes.Key = GetKeyBytes(generatedKey);
Console.WriteLine($"KEY (original) {generatedKey}");
Console.WriteLine($"KEY: {Convert.ToBase64String(myAes.Key)}");
Console.WriteLine($"IV: {Convert.ToBase64String(myAes.IV)}");
byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
Console.WriteLine($"Original: {original}");
Console.WriteLine($"Encrypted: {Encoding.ASCII.GetString(encrypted)}");
Console.Write($"Encrypted (bytes) :");
foreach (var i in encrypted)
{
Console.Write($"{ConvertToHex(i)}:");
}
Console.WriteLine();
Console.WriteLine($"Encrypted (base64): {Convert.ToBase64String(encrypted)}");
Console.WriteLine($"Round Trip: {roundtrip}");
}
}
// Make the key as 256-bit with the HEX string format
private static string GenerateKey()
{
var buffer = new byte[32];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(buffer);
return BitConverter.ToString(buffer).Replace("-", string.Empty);
}
}
public static byte[] GetKeyBytes(string hexOrBase64)
{
// only support 32 bytes (256 bits) key length
if (hexOrBase64.Length == 64)
{
return Enumerable.Range(0, hexOrBase64.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexOrBase64.Substring(x, 2), 16))
.ToArray();
}
return Convert.FromBase64String(hexOrBase64);
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
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_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
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;
}
}
実行結果
Key Length: 32
KeySize: 256
KEY (original) 8051E074A3DBAB6A6134F2E267C1FBF3
KEY: 8051E074A3DBAB6A6134F2E267C1FBF3
IV: 4egGQBFkFmiLBhfMvkrtxQ==
Original: Here is some data encrypt!
Encrypted: ?↓?ah→?◄6E??X?K.7*.Wqo???☺?
Encrypted (bytes) :A0:1B:9C:19:A4:94:08:07:61:68:1A:EB:11:36:45:B5:B0:58:94:4B:2E:37:2A:2E:57:71:6F:92:D2:F7:01:88:
Encrypted (base64): oBucGaSUCAdhaBrrETZFtbBYlEsuNyouV3FvktL3AYg=
Round Trip: Here is some data encrypt!
Resource
概要はこちらがわかりやすいです。
https://it-trend.jp/encryption/article/64-0070