1

KeePass 1.x kdb ファイルを開くことができるプログラムを C# で作成したいと考えています。ソースをダウンロードし、パスワード データベース読み取り機能を移植しようとしました。データベースの内容は暗号化されています。暗号化キーは次の方法で取得されます。

  1. ユーザーがパスワードを入力します。
  2. パスワードの SHA256 ハッシュが計算され、2 つの 128 ビットの半分に分割されます。
  3. データベースヘッダーからのキーを使用して、ハッシュの各半分に数ラウンドの AES が適用されます。
  4. 半分が連結されます。
  5. 結果は、データベース ヘッダーのソルトでソルトされます。
  6. ステップ 4 の結果の SHA256 ハッシュが計算されます。それが暗号鍵です。

ステップ 3 で行き詰まっています。KeePass はAES にCNGを使用します。簡略化されたソース (ハッシュの半分、残りの半分には同じものが適用されています):

BCRYPT_ALG_HANDLE hAes = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
BYTE pbKey32[32] = <encryption key>;
BYTE pbData16[16] = <half of hash from step 2>;

BCryptOpenAlgorithmProvider(&hAes, BCRYPT_AES_ALGORITHM, NULL, 0);

DWORD dwKeyObjLen = 0;
ULONG uResult = 0;
BCryptGetProperty(hAes, BCRYPT_OBJECT_LENGTH, (PUCHAR)&dwKeyObjLen, sizeof(DWORD), &uResult, 0);

BCryptSetProperty(hAes, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, static_cast<ULONG>((wcslen(BCRYPT_CHAIN_MODE_ECB) + 1) * sizeof(wchar_t)), 0);

BCRYPT_KEY_DATA_BLOB_32 keyBlob;
ZeroMemory(&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32));
keyBlob.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
keyBlob.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
keyBlob.cbKeyData = 32;
memcpy(keyBlob.pbData, pbKey32, 32);
pKeyObj = new UCHAR[dwKeyObjLen];

BCryptImportKey(hAes, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, pKeyObj.get(), dwKeyObjLen, (PUCHAR)&keyBlob, sizeof(BCRYPT_KEY_DATA_BLOB_32), 0);

for (int i = 0; i < rounds; ++i)
{
    BCryptEncrypt(hKey, pbData16, 16, NULL, NULL, 0, pbData16, 16, &uResult, 0);
}

したがって、私が理解している限り、ECBチェーンモードでAESアルゴリズムを使用し、 BCryptEncrypt関数の5番目と6番目の引数として通過NULLし、初期化ベクトルを使用しないことを意味します。0

では、C# で同じことを行うにはどうすればよいでしょうか。1 回の変換を行う次の関数を作成しました (MSDN サンプルに基づく)。

    public static byte[] KeyTransform(byte[] buffer, byte[] key)
    {
        Aes aes = Aes.Create();
        aes.Key = key;
        aes.BlockSize = 128;
        aes.KeySize = key.Length * 8;
        aes.Mode = CipherMode.ECB;
        //aes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

        ICryptoTransform ct = aes.CreateEncryptor();
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write))
            {
                using (BinaryWriter bw = new BinaryWriter(cs))
                {
                    bw.Write(buffer);
                }

                cs.Flush();
            }

            return ms.ToArray();
        }
    }

次に、元のコードと私のコードで AES を 1 ラウンド適用した後、バッファを比較します。私のコードは、元のコードとは異なる結果を生成します。どうすれば修正できますか?

ちなみに、IV を指定するかどうかに関係なく、私のコードは毎回異なる結果を生成します (したがって、IV は常に生成され、使用されると思います)。に設定しようとすると、に設定aes.IVできnullないという例外がスローされnullます。

4

1 に答える 1