ファイルにパスワードを付加する安全な方法
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class FileProtector
{
/// <summary>
/// パスワードをSHA-256でハッシュ化し、バイト配列として返す。
/// </summary>
/// <param name="password_">ユーザーが設定したパスワード</param>
/// <returns>ハッシュ化されたバイト配列(256ビット=32バイト)</returns>
private static byte[] hash_password(string password_)
{
using (var sha256 = SHA256.Create())
{
// パスワードをUTF-8でバイト配列に変換し、ハッシュ化
return sha256.ComputeHash(Encoding.UTF8.GetBytes(password_));
}
}
/// <summary>
/// 元のファイルデータにパスワードに基づくランダムバイトを挿入し、新しいデータを作成する。
/// </summary>
/// <param name="file_data_">元のファイルデータ</param>
/// <param name="hash_">パスワードのハッシュ値</param>
/// <param name="new_file_data_">バイトが挿入された新しいファイルデータ</param>
private static void insert_bytes(byte[] file_data_, byte[] hash_, out byte[] new_file_data_)
{
// ハッシュ値から挿入の開始位置、間隔、バイト数を決定
uint start_pos = BitConverter.ToUInt32(hash_, 0) % (uint)file_data_.Length; // 開始位置(ファイルサイズで割った余り)
ushort interval = (ushort)(BitConverter.ToUInt16(hash_, 4) % 10 + 1); // 挿入間隔(1~10バイト)
ushort byte_count = (ushort)(BitConverter.ToUInt16(hash_, 6) % 5 + 1); // 挿入バイト数(1~5バイト)
// ランダムバイト生成のためのシード(ハッシュ値から取得)
int seed = BitConverter.ToInt32(hash_, 8);
var random = new Random(seed);
// 新しいファイルデータをメモリストリームに構築
using (var ms = new MemoryStream())
{
int pos = 0; // 現在の処理位置
while (pos < file_data_.Length)
{
// 開始位置まで元のデータをそのままコピー
if (pos < start_pos)
{
int copy_length = (int)Math.Min(start_pos - pos, file_data_.Length - pos);
ms.Write(file_data_, pos, copy_length);
pos += copy_length;
}
else
{
// 挿入位置にランダムバイトを追加
byte[] random_bytes = new byte[byte_count];
random.NextBytes(random_bytes);
ms.Write(random_bytes, 0, byte_count);
// 次の挿入位置まで元のデータをコピー
int next_pos = pos + interval;
if (next_pos < file_data_.Length)
{
ms.Write(file_data_, pos, interval);
pos = next_pos;
}
else
{
ms.Write(file_data_, pos, file_data_.Length - pos);
pos = file_data_.Length;
}
}
}
new_file_data_ = ms.ToArray();
}
}
/// <summary>
/// バイトが挿入されたファイルデータから無関係なバイトを除去し、元のデータに戻す。
/// </summary>
/// <param name="file_data_">バイトが挿入されたファイルデータ</param>
/// <param name="hash_">パスワードのハッシュ値</param>
/// <param name="original_file_data_">復元された元のファイルデータ</param>
private static void remove_bytes(byte[] file_data_, byte[] hash_, out byte[] original_file_data_)
{
// ハッシュ値から挿入時の開始位置、間隔、バイト数を再計算
uint start_pos = BitConverter.ToUInt32(hash_, 0) % (uint)file_data_.Length;
ushort interval = (ushort)(BitConverter.ToUInt16(hash_, 4) % 10 + 1);
ushort byte_count = (ushort)(BitConverter.ToUInt16(hash_, 6) % 5 + 1);
// 元のデータをメモリストリームに構築
using (var ms = new MemoryStream())
{
int pos = 0; // 現在の処理位置
while (pos < file_data_.Length)
{
// 開始位置までデータをそのままコピー
if (pos < start_pos)
{
int copy_length = (int)Math.Min(start_pos - pos, file_data_.Length - pos);
ms.Write(file_data_, pos, copy_length);
pos += copy_length;
}
else
{
// 挿入されたバイトをスキップ
pos += byte_count;
// 次の挿入位置までデータをコピー
int next_pos = pos + interval;
if (next_pos < file_data_.Length)
{
ms.Write(file_data_, pos, interval);
pos = next_pos;
}
else
{
ms.Write(file_data_, pos, file_data_.Length - pos);
pos = file_data_.Length;
}
}
}
original_file_data_ = ms.ToArray();
}
}
/// <summary>
/// パスワードを使ってファイルを保存する。
/// </summary>
/// <param name="file_path_">保存先のファイルパス</param>
/// <param name="password_">設定するパスワード</param>
/// <param name="file_data_">保存する元のファイルデータ</param>
public static void save_file(string file_path_, string password_, byte[] file_data_)
{
byte[] hash = hash_password(password_); // パスワードをハッシュ化
insert_bytes(file_data_, hash, out byte[] new_file_data); // バイトを挿入
File.WriteAllBytes(file_path_, new_file_data); // ファイルに書き込み
}
/// <summary>
/// パスワードを使ってファイルを開く。
/// </summary>
/// <param name="file_path_">読み込むファイルパス</param>
/// <param name="password_">設定されたパスワード</param>
/// <returns>復元された元のファイルデータ</returns>
public static byte[] load_file(string file_path_, string password_)
{
byte[] hash = hash_password(password_); // パスワードをハッシュ化
byte[] file_data = File.ReadAllBytes(file_path_); // ファイルを読み込み
remove_bytes(file_data, hash, out byte[] original_file_data); // バイトを除去
return original_file_data;
}
// 使用例
static void Main()
{
string file_path = "test.bin";
string password = "MySecretPass123";
byte[] original_data = Encoding.UTF8.GetBytes("これはテストデータです。");
// ファイル保存
save_file(file_path, password, original_data);
Console.WriteLine("ファイルが保存されました。");
// ファイル読み込み
byte[] loaded_data = load_file(file_path, password);
Console.WriteLine("読み込んだデータ: " + Encoding.UTF8.GetString(loaded_data));
}
}
コメント