I'm using DPAPI in C++ to encrypt some data that I need to store in a file. The thing is that I need to read that file from C#, so I need to be able to:
C++ encrypt, C++ decrypt (is working good)
C# encrypt, C# decrypt (is working good)
C++ encrypt, C# decrypt and vice-versa (not working)
In C# I'm using DllImport to pInvoke the methods CryptProtectData and CryptUnprotectData, and I implement them as explained here. I know that in C# I can use the methods contained in the ProtectedData class but I'm doing it in this way (using DllImport ) to make sure that both codes (c++ and c#) look and work pretty much the same.
Now the weird thing is that even if both codes looks the same I get different outputs, for example for this text:
"plain text"
in C++ I get:
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
and in C# I get:
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
As you can see the first characters are the same but the rest are not, so if anyone has an idea of why this may be happening, I will appreciate the help.
Thanks.
Code in 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))
Code in C#:
(you can see how my C# code looks here, since is identical to the one in this Microsoft example )
It would help if you could post your C++ and your C# code. Perhaps there are some subtle parameter differences or something like this. For example, you should make sure that the pOptionalEntropy parameter is the same (or set it to NULL to test if this is the error source). Also, make sure to try to encrypt and decrypt on the same PC:
[...]decryption usually can only be done on the computer where the data was encrypted
(Source: MSDN)
EDIT: Some comments on the code you posted and the C# version from MSDN (parts of it following):
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);
[...]
}
And here's your C++ code again to have both in view:
[...]
BYTE *pbDataInput =(BYTE *)(char*)value.c_str();
DWORD cbDataInput = strlen((char *)pbDataInput)+1;
[...]
CryptProtectData(&DataIn, NULL, NULL, NULL, NULL, 0, &DataOut))
The parameters don't match and I think that's the source of the differences.
The first thing is the flags. The C# code uses dwFlags != 0, your C++ code uses dwFlags = 0, so this is clearly a difference.
I'm not sure about the entropy. If you didn't pass optionalEntropy = null it is a difference, but if it is null, there's a "new byte[0]" assignment and I'm not sure about what this will create, but I think you should at least try to pass IntPtr.Zero instead of entropyBlob to CryptProtectData to match with the C++ code.
Last, but not least, your C++ code includes the trailing NUL that delimits the C string, I don't know how the encryption used here works but there are encryptions that will give you very different outputs if one byte is different (or you have one more byte like in this case), so you should either include a terminating NUL in the C# code or remove it in the C++ code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With