为什么我使用DPAPI加密时会得到不同的输出?

6
我正在使用C++中的DPAPI来加密一些需要存储在文件中的数据。问题是,我需要从C#中读取该文件,因此我需要能够:
C++加密,C++解密(正常工作)
C#加密,C#解密(正常工作)
C++加密,C#解密和相反情况(不起作用)
在C#中,我使用DllImport来pInvoke CryptProtectData和CryptUnprotectData方法,并按照 这里所述实现它们。 我知道在C#中我可以使用ProtectedData类中包含的方法,但我以这种方式进行操作(使用DllImport),以确保两个代码(c++和c#)看起来并且基本上运行相同。
现在奇怪的是,即使两种代码看起来相同,我会得到不同的输出。 例如,对于这个文本:
“plain text”
在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
而在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 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

正如您所看到的,前面几个字符是相同的,但其余部分不同。如果有人知道这可能发生的原因,我会非常感谢帮助。

谢谢。

C++代码:


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#代码:

(您可以在此处查看我的C#代码外观,因为它与此Microsoft示例中的代码完全相同)


2
一些格式化的十六进制编辑器,例如数据示例,而不是那些截图会更好。 - schnaader
无法辨认截图。 - Newton Falls
@Newton:您可以在单独的选项卡中打开该图形链接以更好地查看它(在Firefox中,“显示图形”也可以使用)。 - schnaader
1
每次使用不同的IV进行块链加密,会为相同的数据创建不同的二进制大对象。这并不让我感到惊讶。 - Steve Gilham
可能相关:http://stackoverflow.com/questions/24386336/do-i-need-to-store-key-for-cryptunprotectdata-and-cryptprotectdata - Cristian Amarie
显示剩余4条评论
1个回答

5
如果您能发布您的C++和C#代码,那将很有帮助。也许存在一些微妙的参数差异或类似的问题。例如,您应该确保pOptionalEntropy参数相同(或将其设置为NULL以测试是否是错误源)。此外,请确保在同一台计算机上尝试加密和解密:
"[...]通常只能在加密数据的计算机上进行解密"。
(来源:MSDN
编辑:对于您发布的代码以及来自MSDN的C#版本,以下是一些评论(其中包含部分内容)。
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,因此这显然是一个差异。

我不确定熵。如果您没有传递optionalEntropy = null,则存在差异,但如果它为null,则存在“new byte [0]”分配,我不确定这将创建什么,但我认为您应该至少尝试将IntPtr.Zero传递给CryptProtectData以与C++代码相匹配。

最后但并非最不重要的是,您的C++代码包括结尾的NUL,用于界定C字符串,我不知道此处使用的加密方式如何工作,但有些加密会在一个字节不同(或者您多了一个字节,就像这种情况)时给您非常不同的输出,因此您应该在C#代码中包含终止NUL或在C++代码中删除它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接