一度暗号化してしまうと、ファイルを復号してもファイル情報が失われてしまいます。ファイル情報とはファイル名や属性、タイムスタンプなどです。前回作成したプログラムでは復号するときのファイル名はユーザーが入力しなければならない仕様になっています。できれば復号したときに自動でもとに戻るようにしておきたいものです。
そこでファイル名、属性、タイムスタンプは暗号化するときにいっしょに保存して元に戻せるようにしておきましょう。
以下はファイルを選択するとファイル名+.encで暗号化します。復号するときは暗号化されたファイルのあるフォルダにDecodeというフォルダを作成して元のファイルを生成するプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
using System; using System.Text; using System.IO; using System.Security.Cryptography; using System.IO.Compression; using System.Windows.Forms; public partial class Form1 : Form { void EncodeFile(string srcFilePath, string destFilePath, string password, int saltSize) { // Saltを生成する byte[] salt = GetSalt(saltSize); byte[] key = GetKeyFromPassword(password, salt); byte[] iv = GetIVFromPassword(password); using (AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Key = key; aes.IV = iv; aes.Padding = PaddingMode.ISO10126; // 暗号化オブジェクト生成 ICryptoTransform ct = aes.CreateEncryptor(aes.Key, aes.IV); // 出力ファイルストリーム using (FileStream outFileStream = new FileStream(destFilePath, FileMode.Create, FileAccess.Write)) { // Saltの書き込み outFileStream.Write(salt, 0, saltSize); // 初期化ベクトル 書き込み outFileStream.Write(aes.IV, 0, BLOCK_SIZE / 8); using (CryptoStream cryptoStream = new CryptoStream(outFileStream, ct, CryptoStreamMode.Write)) { // ファイル情報を取得する FileInfo info = new FileInfo(srcFilePath); long creationTime = info.CreationTime.Ticks; long lastAccessTime = info.LastAccessTime.Ticks; long lastWriteTime = info.LastWriteTime.Ticks; string name = info.Name; byte[] vs = Encoding.UTF8.GetBytes(name); int len = vs.Length; // ファイル情報を書き込む cryptoStream.Write(BitConverter.GetBytes(creationTime), 0, sizeof(long)); cryptoStream.Write(BitConverter.GetBytes(lastAccessTime), 0, sizeof(long)); cryptoStream.Write(BitConverter.GetBytes(lastWriteTime), 0, sizeof(long)); cryptoStream.Write(BitConverter.GetBytes(len), 0, sizeof(int)); cryptoStream.Write(vs, 0, len); // ファイル情報を書き込む ここまで // Deflateアルゴリズムを使用した圧縮 using(DeflateStream deflateStream = new DeflateStream(cryptoStream, CompressionMode.Compress)) { using (FileStream inFileStream = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read)) { byte[] buf = new byte[BUFFER_SIZE]; while (true) { int size = inFileStream.Read(buf, 0, buf.Length); if (size == 0) break; // 出力ファイルへ書き込み(圧縮→暗号化→書き込み) deflateStream.Write(buf, 0, size); } } } } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
public partial class Form1 : Form { void DecodeFile(string srcFilePath, string destFolderPath, string password, int saltSize) { using (AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.ISO10126; using (FileStream inFileStream = new FileStream(srcFilePath, FileMode.Open, FileAccess.Read)) { // Saltを読み込む byte[] salt = GetSalt(saltSize); inFileStream.Read(salt, 0, saltSize); byte[] key = GetKeyFromPassword(password, salt); aes.Key = key; // 初期化ベクトル読込 byte[] iv = new byte[BLOCK_SIZE / 8]; inFileStream.Read(iv, 0, iv.Length); aes.IV = iv; // 復号化オブジェクト生成 ICryptoTransform ct = aes.CreateDecryptor(aes.Key, aes.IV); using (CryptoStream cryptoStream = new CryptoStream(inFileStream, ct, CryptoStreamMode.Read)) { // ファイル情報部分を読み込む byte[] longbs = new byte[sizeof(long)]; byte[] intbs = new byte[sizeof(int)]; cryptoStream.Read(longbs, 0, sizeof(long)); long creationTime = BitConverter.ToInt64(longbs, 0); cryptoStream.Read(longbs, 0, sizeof(long)); long lastAccessTime = BitConverter.ToInt64(longbs, 0); cryptoStream.Read(longbs, 0, sizeof(long)); long lastWriteTime = BitConverter.ToInt64(longbs, 0); cryptoStream.Read(intbs, 0, sizeof(int)); int len = BitConverter.ToInt32(intbs, 0); byte[] vs1 = new byte[len]; cryptoStream.Read(vs1, 0, len); string name = Encoding.UTF8.GetString(vs1); // ファイル情報部分を読み込む ここまで if(!Directory.Exists(destFolderPath)) Directory.CreateDirectory(destFolderPath); string destFilePath = destFolderPath + "\\" + name; // Deflateアルゴリズムを使用して圧縮解除 using(DeflateStream ds = new DeflateStream(cryptoStream, CompressionMode.Decompress)) { using (FileStream outFs = new FileStream(destFilePath, FileMode.Create, FileAccess.Write)) { byte[] buf = new byte[BUFFER_SIZE]; while (true) { int size = ds.Read(buf, 0, buf.Length); if (size == 0) break; outFs.Write(buf, 0, size); } } } // ファイル情報を復元する FileInfo info = new FileInfo(destFilePath); info.CreationTime = new DateTime(creationTime); info.LastAccessTime = new DateTime(lastAccessTime); info.LastWriteTime = new DateTime(lastWriteTime); } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public partial class Form1 : Form { private void button1_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Title = "暗号化するファイルを選ぶ"; if(dialog.ShowDialog() != DialogResult.OK) return; EncodeFile(dialog.FileName, dialog.FileName + ".enc", textBoxPass.Text, 16); } private void button2_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Title = "復号するファイルを選ぶ"; if(dialog.ShowDialog() != DialogResult.OK) return; FileInfo info = new FileInfo(dialog.FileName); DecodeFile(dialog.FileName, info.DirectoryName + "\\Decode", textBoxPass.Text, 16); } } |
1 2 3 |
byte[] salt = GetSalt(saltSize); byte[] key = GetKeyFromPassword(password, salt); byte[] iv = GetIVFromPassword(password); |
は
暗号化に挑戦と同じです。
修正:
GetSalt(saltSize)は同じではないです(引数の数が違っていた)。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private static byte[] GetSalt(int saltSize) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(DateTime.Now.Ticks.ToString(), saltSize); return deriveBytes.Salt; } } |
こんにちは、初めまして。
一点質問させてください。
最後の方に「『byte[] salt = GetSalt(saltSize);』は暗号化に挑戦と同じです。」と書かれていますが、「暗号化に挑戦」の方では『byte[] salt = GetSalt();』となっているため、そのまま持ってきたらエラーになってしまいます。
この場合、最初の方の『private static byte[] GetSalt()』を『private static byte[] GetSalt(int saltSize)』にしたらエラーが無くなったのですが、コレで合っているでしょうか?
また、「暗号化に挑戦」の『Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(DateTime.Now.Ticks.ToString(), SALT_SIZE);』の最後のSALT_SIZEをsaltSizeに変えて
『Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(DateTime.Now.Ticks.ToString(), saltSize);』と記述したのですが、コレで正解でしょうか?
この場合、最初に定義した『private const int SALT_SIZE = 25;』が使用されなくなってしまうので、どうすればよいのかなと……
以上です、宜しくお願い致します。
記事を修正しました。private const int SALT_SIZE = 25;はここでは使いません。