Почему я получаю разные результаты при шифровании с использованием DPAPI?

Я использую DPAPI в C++ для шифрования некоторых данных, которые мне нужно сохранить в файле. Дело в том, что мне нужно прочитать этот файл с C#, поэтому мне нужно иметь возможность:

Шифрование С++, расшифровка С++ (работает хорошо)

Шифрование С#, расшифровка С# (работает хорошо)

Шифрование С++, расшифровка С# и наоборот (не работает)

В C# я использую DllImport для pInvoke методов CryptProtectData и CryptUnprotectData и реализую их, как описано здесь. Я знаю, что в C# я могу использовать методы, содержащиеся в классе ProtectedData, но я делаю это таким образом (используя DllImport ), чтобы убедиться, что оба кода (C++ и C#) выглядят и работают практически одинаково.

Теперь странно то, что даже если оба кода выглядят одинаково, я получаю разные результаты, например, для этого текста:

"простой текст"

в С++ я получаю:

01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB 01 00 00 00 2E 6F 88 86 E6 16 9B 4F 9B BF 35 DA 9F C6 EC 12 00 00 00 00 02 00 00 00 00 00 03 66 00 00 A8 00 00 00 10 00 00 00 93 06 68 39 DB 58 FE E9 C4 1F B0 3D 7B 0A B7 48 00 00 00 00 04 80 00 00 A0 00 00 00 10 00 00 00 36 4E 84 05 0D 4A 34 15 97 DC 5B 1F 6C A4 19 D9 10 00 00 00 F5 33 9F 55 49 94 26 54 2B C8 CB 70 7B FE EC 96 14 00 00 00 C5 23 DA BA C8 23 6C 0B B3 88 69 06 00 95 29 AE 76 A7 63 E4

и в С# я получаю:

01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB 01 00 00 00 2E 6F 88 86 E6 16 9B 4F 9B BF 35 DA 9F C6 EC 12 00 00 00 00 02 00 00 00 00 00 03 66 00 00 A8 00 00 00 10 00 00 00 34 C4 40 CD 91 EC 94 66 E5 E9 23 F7 9E 04 9C 83 00 00 00 00 04 80 00 00 A0 00 00 00 10 00 00 00 12 54 1E 26 72 26 0A D1 11 1D 4D EF 13 1D B2 6F 10 00 00 00 81 9D 46 37 D1 68 5D 17 B8 23 78 48 18 ED 06 ED 14 00 00 00 E4 45 07 1C 08 55 99 80 A4 59 D9 33 BC 0B 71 35 39 05 C4 BB

Как видите, первые символы одинаковы, а остальные нет, поэтому, если у кого-то есть идеи, почему это может происходить, я буду признателен за помощь.

Спасибо.

Код на С++:


value = "plain text";
DATA_BLOB DataIn;
DATA_BLOB DataOut;

BYTE *pbDataInput =(BYTE *)(char*)value.c_str();
DWORD cbDataInput = strlen((char *)pbDataInput)+1;
DataIn.pbData = pbDataInput; 
DataIn.cbData = cbDataInput;

CryptProtectData(&DataIn, NULL, NULL, NULL, NULL, 0, &DataOut))

Код на С#:

(вы можете увидеть, как мой код C# выглядит здесь, поскольку он идентичен тот, что в этом примере Microsoft)


person Vic    schedule 18.09.2009    source источник
comment
Было бы неплохо использовать какой-нибудь отформатированный шестнадцатеричный редактор, например пример данных, вместо этих скриншотов.   -  person schnaader    schedule 19.09.2009
comment
Не могу разобрать скриншоты.   -  person Newton Falls    schedule 19.09.2009
comment
@Newton: Вы можете открыть ссылку на графику в отдельной вкладке, чтобы лучше ее рассмотреть (в Firefox Show графика тоже работает)   -  person schnaader    schedule 19.09.2009
comment
@schnaader - Спасибо, не то, чтобы там было на что посмотреть!   -  person Newton Falls    schedule 19.09.2009
comment
И если вы не знаете, как сделать шестнадцатеричный дамп, вы все равно можете использовать командную строку: опубликуйте первые несколько строк fc /b cred.ini cred2.ini   -  person schnaader    schedule 19.09.2009
comment
спасибо, я должен извиниться, я думал, что, возможно, изображения будут полезны, но вы правы, шестнадцатеричный текст лучше.   -  person Vic    schedule 19.09.2009
comment
Шифрование цепочки блоков с другим IV каждый раз будет создавать разные большие двоичные объекты для одних и тех же данных. Меня бы это не удивило.   -  person Steve Gilham    schedule 19.09.2009
comment
Спасибо, Стив, вы были правы, алгоритм каждый раз генерирует разные результаты, независимо от того, использую ли я только C++ или C#, я до сих пор этого не осознавал.   -  person Vic    schedule 21.09.2009
comment
Возможно, связано: stackoverflow.com/questions/24386336/   -  person Cristian Amarie    schedule 11.04.2016


Ответы (1)


Было бы полезно, если бы вы могли опубликовать свой код C++ и C#. Возможно, есть какие-то тонкие различия в параметрах или что-то в этом роде. Например, вы должны убедиться, что параметр pOptionalEntropy такой же (или установить для него значение NULL, чтобы проверить, является ли это источником ошибки). Кроме того, обязательно попробуйте зашифровать и расшифровать на одном и том же ПК:

[...] расшифровку обычно можно выполнить только на том компьютере, на котором данные были зашифрованы

(Источник: MSDN)

РЕДАКТИРОВАТЬ: некоторые комментарии к коду, который вы разместили, и версии С# из MSDN (части этого следующие):

public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy) {
  [...]
  int bytesSize = plainText.Length;
  plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize);
  plainTextBlob.cbData = bytesSize;
  Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize);
  [...]
  dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
  [...]
  if(null == optionalEntropy)
  {//Allocate something
  optionalEntropy = new byte[0]; // Is copied to entropyBlob later
  }
  [...]
  retVal = CryptProtectData(ref plainTextBlob, "", ref entropyBlob,    
    IntPtr.Zero, ref prompt, dwFlags, 
    ref cipherTextBlob);
  [...]
}

И вот ваш код C++ снова, чтобы иметь в виду оба:

[...]
BYTE *pbDataInput =(BYTE *)(char*)value.c_str();
DWORD cbDataInput = strlen((char *)pbDataInput)+1;
[...]
CryptProtectData(&DataIn, NULL, NULL, NULL, NULL, 0, &DataOut))

Параметры не совпадают, и я думаю, что это источник различий.

В первую очередь это флаги. Код C# использует dwFlags != 0, ваш код C++ использует dwFlags = 0, так что это явное различие.

Я не уверен насчет энтропии. Если вы не передали optionEntropy = null, это разница, но если оно равно null, есть назначение «новый байт [0]», и я не уверен, что это создаст, но я думаю, вы должны хотя бы попробовать для передачи IntPtr.Zero вместо entropyBlob в CryptProtectData для соответствия коду C++.

И последнее, но не менее важное: ваш код C++ включает завершающий NUL, который ограничивает строку C. Я не знаю, как работает используемое здесь шифрование, но есть шифрования, которые дадут вам очень разные результаты, если один байт отличается (или у вас есть еще один байт, как в этом случае), поэтому вы должны либо включить завершающий NUL в код C#, либо удалить его в коде C++.

person schnaader    schedule 18.09.2009