ファイルにパスワードを付加する安全な方法

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));
    }
}

いいなと思ったら応援しよう!

コメント

ログイン または 会員登録 するとコメントできます。
ファイルにパスワードを付加する安全な方法|古井和雄
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1