やりたいこと
テキストとして読まれたくないが、ファイル保存しておきたい文字列があるときに、暗号化してファイル保存して、使うときに復号化するようなことがしたい。
やり方
AesCngというクラスを使用して、暗号化・復号化を行う。
サンプルコード
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
// "あいうえお"を暗号化
AesEncrypter.WriteAesEncryptedBytesToFile("あいうえお", @"C:\work\test.bin");
// 暗号化したデータを読み出し復号化する
var d = AesEncrypter.ReadAesEncryptedBytesFromFile(@"C:\work\test.bin");
Console.WriteLine(d);
Console.ReadLine();
}
}
public class AesEncrypter
{
///IV 半角16文字のランダムな文字列
private static readonly string AesIV = @"xxxxxxxxxxxxxxxx";
// キー 半角32文字のランダムな文字列
// (1文字あたり8bit→8*32=256bit→キーサイズ)
private static readonly string AesKey = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// キーサイズ(bit)
private static readonly int KeySize = 256;
// ブロックサイズ
private static readonly int BlockSize = 128;
// 暗号化
public static byte[] Encrypt(string value)
{
// AESオブジェクトを取得
var aes = GetAesCng();
// 対象の文字列をバイトデータに変換
var byteValue = Encoding.UTF8.GetBytes(value);
// バイトデータの長さを取得
var byteLength = byteValue.Length;
// 暗号化オブジェクトを取得
var encryptor = aes.CreateEncryptor();
// 暗号化
return encryptor.TransformFinalBlock(byteValue, 0, byteLength);
}
// 復号化
public static string Decrypt(byte[] encryptValue)
{
// AESオブジェクトを取得
var aes = GetAesCng();
// 復号化オブジェクトを取得
var decryptor = aes.CreateDecryptor();
// 復号化
var decryptValue = decryptor.TransformFinalBlock(encryptValue, 0, encryptValue.Length);
// 復号化されたバイトデータを文字列に変換
var stringValue = Encoding.UTF8.GetString(decryptValue);
return stringValue;
}
// AESオブジェクトを取得
private static AesCng GetAesCng()
{
// AESオブジェクトを生成し、パラメータを設定します。
var aes = new AesCng();
aes.KeySize = KeySize;
aes.BlockSize = BlockSize;
aes.Mode = CipherMode.CBC;
aes.IV = Encoding.UTF8.GetBytes(AesIV);
aes.Key = Encoding.UTF8.GetBytes(AesKey);
aes.Padding = PaddingMode.PKCS7;
return aes;
}
// 文字列を暗号化してファイルに書き出す
// targetString 暗号化したい文字列
// keyFilePath 暗号化した文字列を書き込むファイル
public static void WriteAesEncryptedBytesToFile(string targetString, string keyFilePath)
{
byte[] encrypted;
try
{
encrypted = Encrypt(targetString);
string folderPath = Path.GetDirectoryName(keyFilePath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
using (var fs = new FileStream(keyFilePath, FileMode.Create))
using (var bw = new BinaryWriter(fs))
{
bw.Write(encrypted);
}
}
catch
{
throw;
}
}
// 暗号化されたファイルからキーを読を復号して返す
// keyFilePath キーファイルパス
// return : 復号化された文字列
public static string ReadAesEncryptedBytesFromFile(string keyFilePath)
{
string roundtrip;
try
{
using (var fs = new FileStream(keyFilePath, FileMode.Open))
using (var br = new BinaryReader(fs))
{
var encrypted = new byte[fs.Length];
br.Read(encrypted, 0, (int)fs.Length);
roundtrip = Decrypt(encrypted);
}
}
catch
{
throw;
}
return roundtrip;
}
}
}
注意
暗号化の際、IVやキーが必要になるため、コードの中に「AesIV」「AesKey」として埋め込まれているが、キーなどの値がわかってしまうと、簡単に解読(復号)されてしまう。
.NETのコードは、ILSpyなどのツールを使うと簡単にコードが読めるようにできてしまうため、このコードをこのまま使うようなら、難読化などの対策が必要。
メモ
下記のクラスでも、同じようにAESの暗号化が行える
- AesCryptoServiceProvider(.NET Framework3.5以降で使用できるクラス。)
- AesManaged(.NET Framework3.5以降で使用できるクラス。これはマネージドコードで全部書かれてるらしい)
- AesCng(.NET Framework4.6.2以降で使用できる、現段階(2019年)で新しいクラス。)
また、上記のAesCngクラスの継承元のAesクラスの、さらに継承元のSymmetricAlgorithmというクラスがある。これを継承して、AESだけでなく、DES、RC2、Rijndael、TripleDESなどの暗号化用のクラスが用意されている。
参考
AesCng Class
https://docs.microsoft.com/ja-jp/dotnet/api/system.security.cryptography.aescng?view=netframework-4.8
C#で文字列を暗号化・復号化する。
ほぼほぼこちらを参考にさせてもらってます。
https://paveway.hatenablog.com/entry/2019/04/08/csharp_encrypt
Rijndaelクラスを使ったAES暗号化
https://qiita.com/kz-rv04/items/62a56bd4cd149e36ca70