security

Windows暗号化の基礎と安全な実装

Windows環境での暗号化の基本概念と、BCryptを使用した安全な実装方法について解説します。

3 min read

Windows暗号化の基礎と安全な実装

はじめに

データの暗号化は、現代のセキュリティにおいて不可欠です。この記事では、Windows環境での暗号化の基本概念と、安全な実装方法について解説します。

暗号化の基本概念

対称暗号と非対称暗号

特性対称暗号非対称暗号
キー数1つ(共有キー)2つ(公開鍵・秘密鍵)
速度高速低速
用途大量データの暗号化キー交換、署名
AES, ChaCha20RSA, ECC

Windows暗号化API

Windowsには以下の暗号化APIが提供されています:

  • BCrypt: カーネルモード対応のCNG(Cryptography Next Generation)
  • NCrypt: キーストレージ機能付きCNG
  • CryptoAPI: 従来のAPI(非推奨)

AES暗号化の安全な実装

推奨されるパラメータ

  • アルゴリズム: AES-256-GCM(認証付き暗号化)
  • IV(初期化ベクトル): 毎回ランダムに生成(96ビット)
  • 認証タグ: 128ビット

実装例(BCrypt)

HLJSC
#include <windows.h> #include <bcrypt.h> #include <stdio.h> #pragma comment(lib, "bcrypt.lib") #define AES_KEY_SIZE 32 // 256 bits #define AES_IV_SIZE 12 // 96 bits for GCM #define AES_TAG_SIZE 16 // 128 bits // セキュアなキー生成 NTSTATUS generate_key(BCRYPT_ALG_HANDLE hAesAlg, PBYTE pbKey, DWORD cbKey) { NTSTATUS status; BCRYPT_GEN_RANDOM pBcryptGenRandom = NULL; // CryptGenRandomは非推奨、BCryptGenRandomを使用 status = BCryptGenRandom(NULL, pbKey, cbKey, BCRYPT_USE_SYSTEM_PREFERRED_RNG); return status; } // AES-256-GCM暗号化 NTSTATUS encrypt_aes_gcm( const BYTE* pbPlainText, DWORD cbPlainText, const BYTE* pbKey, const BYTE* pbIV, BYTE* pbCipherText, DWORD cbCipherText, BYTE* pbTag, DWORD cbTag ) { NTSTATUS status; BCRYPT_ALG_HANDLE hAesAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; DWORD cbResult; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; // AES-GCMプロバイダーのオープン status = BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0); if (!NT_SUCCESS(status)) goto cleanup; // GCMモードの設定 status = BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); if (!NT_SUCCESS(status)) goto cleanup; // キーのインポート status = BCryptGenerateSymmetricKey(hAesAlg, &hKey, NULL, 0, (PBYTE)pbKey, AES_KEY_SIZE, 0); if (!NT_SUCCESS(status)) goto cleanup; // 認証情報の初期化 BCRYPT_INIT_AUTH_MODE_INFO(authInfo); authInfo.pbNonce = (PUCHAR)pbIV; authInfo.cbNonce = AES_IV_SIZE; authInfo.pbTag = pbTag; authInfo.cbTag = cbTag; // 暗号化 status = BCryptEncrypt(hKey, (PUCHAR)pbPlainText, cbPlainText, &authInfo, NULL, 0, pbCipherText, cbCipherText, &cbResult, 0); cleanup: if (hKey) BCryptDestroyKey(hKey); if (hAesAlg) BCryptCloseAlgorithmProvider(hAesAlg, 0); return status; }

ハッシュ化とパスワード管理

安全なパスワードハッシュ

HLJSC
#include <windows.h> #include <bcrypt.h> // Argon2は現在BCryptでは未サポートのため、PBKDF2を使用 // より安全な実装にはOpenSSLまたはlibsodiumの使用を推奨 NTSTATUS hash_password( const wchar_t* password, const BYTE* salt, DWORD cbSalt, BYTE* pbHash, DWORD cbHash ) { NTSTATUS status; BCRYPT_ALG_HANDLE hAlg = NULL; // PBKDF2の使用 status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!NT_SUCCESS(status)) return status; // パスワードをUTF-16としてエンコード int passwordLen = lstrlenW(password) * sizeof(wchar_t); // キー導出 status = BCryptDeriveKeyPBKDF2(hAlg, (PUCHAR)password, passwordLen, (PUCHAR)salt, cbSalt, 100000, // 繰り返し回数(高いほど安全だが遅い) pbHash, cbHash); BCryptCloseAlgorithmProvider(hAlg, 0); return status; }

Windowsデータ保護API(DPAPI)

ユーザー固有の暗号化

HLJSC
#include <windows.h> #include <dpapi.h> #include <stdio.h> #pragma comment(lib, "crypt32.lib") // データの暗号化(現在のユーザーに紐付け) BOOL encrypt_data(const BYTE* plaintext, DWORD plaintextLen, DATA_BLOB* encryptedBlob) { DATA_BLOB dataIn; dataIn.cbData = plaintextLen; dataIn.pbData = (BYTE*)plaintext; // CRYPTPROTECT_LOCAL_MACHINEを使用しない(ユーザー固有) return CryptProtectData( &dataIn, L"My Application Data", NULL, // 追加エントロピーなし NULL, // 予約済み NULL, // プロンプトなし CRYPTPROTECT_UI_FORBIDDEN, encryptedBlob ); } // データの復号 BOOL decrypt_data(const DATA_BLOB* encryptedBlob, BYTE** plaintext, DWORD* len) { DATA_BLOB dataOut; if (!CryptUnprotectData( encryptedBlob, NULL, // 説明不要 NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &dataOut)) { return FALSE; } *plaintext = dataOut.pbData; *len = dataOut.cbData; return TRUE; }

暗号化のベストプラクティス

絶対に避けるべき実装

HLJSC
// 悪い例:ECBモードの使用(パターンが残る) // 悪い例:固定IVの使用 // 悪い例:独自の暗号アルゴリズムの実装 // 悪い例:短いキー(128ビット以下)の使用 // 良い例:ランダムIVの生成 void generate_secure_iv(BYTE* iv, DWORD len) { BCryptGenRandom(NULL, iv, len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); } // 良い例:セキュアなキー導出 void derive_key_securely(const wchar_t* password, BYTE* key, DWORD keyLen) { BYTE salt[32]; BCryptGenRandom(NULL, salt, sizeof(salt), BCRYPT_USE_SYSTEM_PREFERRED_RNG); // 高い反復回数を使用 // PBKDF2、scrypt、Argon2idのいずれかを使用 }

監査と監視

暗号化操作のログ記録

HLJSPOWERSHELL
# 暗号化関連のイベントを監査 Get-WinEvent -FilterHashtable @{ LogName='Security' ID=4673, # 特権サービスが呼び出された 4688, # 新しいプロセスが作成された 4692 # バックアップデータ復元の試行 }

まとめ

安全な暗号化の実装には以下が必要です:

  1. 推奨されるアルゴリズムの使用: AES-256-GCM、ChaCha20-Poly1305
  2. 適切なパラメータ: 十分な長さのキーとIV、十分な繰り返し回数
  3. 安全なランダム生成: 暗号論的セキュア乱数生成器(CSPRNG)
  4. 定期的な更新: 暗号化ライブラリとアルゴリズムの更新
  5. 監査: 暗号化操作のログ記録と監視

これらのプラクティスを守ることで、データの機密性と完全性を保護できます。