I want to make a program in C# that can open KeePass 1.x kdb files. I downloaded sources and trying to port password database reading functionality. Database contents is encrypted. Encryption key is obtained the following way:
User enters password;
SHA256 hash of password is calculated and split in two 128-bit halves;
Several rounds of AES is applied to each half of hash using key from database header;
Halves are concatenated back;
Result is salted with salt from database header;
SHA256 hash of step 4 result is calculated. That is the encryption key.
I'm stuck on step 3. KeePass uses CNG for AES. Simplified source (for half of hash, other half had the same applied to it):
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);
}
So, as far as I understand, it uses AES algorithm with ECB chaining mode and it passes NULL and 0 as 5th and 6th argument of BCryptEncrypt function meaning it will not use initialization vector.
Now, how do I do the same in C#? I wrote following function to do one round of transformation (based on MSDN sample):
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();
}
}
Then I compare buffers after one round of AES applied in original and in my code. My code produces different results from original. How do I fix it?
By the way, no matter if I specify IV or not my code produces different result every time (so I believe IV is always generated and used). If I try to set aes.IV to null it throws exception saying that I can't set it to null.
It seems that initializing ICryptoTransform like this:
ICryptoTransform ct = aes.CreateEncryptor(key, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
does the trick.
The only thing that worries me is that resulting memory stream has 32 bytes instead of 16. But if I drop last 16 of them it produces what I need.
Related
I have a 3rd party AES library for C (from Lantronix). I wrapped their API from within C#'s managed code as shown below, and it works:
[DllImport("cbx_enc.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void VC_blockEncrypt(char* iv, char* key, int length, char* text, int RDkeyLen);
/// <summary>
/// Managed Encrypt Wrapper
/// </summary>
/// <param name="buffer">provides the plain text and receives the same length cipher text</param>
static readonly string key = "abcdef0123456789";
static readonly string iv = "0123456789ABCDEF";
public static void Encrypt(ref byte[] buffer)
{
var keyPtr = Marshal.StringToHGlobalAnsi(key);
var ivPtr = Marshal.StringToHGlobalAnsi(iv);
byte[] temp = new byte[16];
Marshal.Copy(ivPtr, temp, 0, 16);
int index = 0;
for (int i = 0; i < buffer.Length; i++)
{
if (index == 0)
{
Marshal.Copy(temp, 0, ivPtr, 16);
unsafe
{
VC_blockEncrypt((char*) ivPtr, (char*) keyPtr, 0, (char*) ivPtr, 128);
}
Marshal.Copy(ivPtr, temp, 0, 16);
index = 16;
}
temp[16 - index] ^= buffer[i];
buffer[i] = temp[16 - index];
index--;
}
Marshal.FreeHGlobal(ivPtr);
Marshal.FreeHGlobal(keyPtr);
}
Now, when I wrote my own, using System.Security.Cryptography to completely avoid using their unmanaged DLL, my final ciphertext seems to differ from them! I am using the same mode, same key, same iv and same plain text, yet the algorithms are not compatible. Shown below is the property settings for the RijndaelManaged object and the code; am I missing something that causes this incompatibility?
/// <summary>
/// Managed Encrypt
/// </summary>
/// <param name="buffer">provides the plain text and receives the same length cipher text</param>
static readonly string key = "abcdef0123456789";
static readonly string iv = "0123456789ABCDEF";
public static void Encrypt(ref byte[] buffer)
{
using (RijndaelManaged cipher = new RijndaelManaged())
{
cipher.Mode = CipherMode.CFB;
cipher.Key = Encoding.ASCII.GetBytes(key);
cipher.IV = Encoding.ASCII.GetBytes(iv);
cipher.Padding = PaddingMode.None;
cipher.FeedbackSize = 128;
ICryptoTransform encryptor = cipher.CreateEncryptor(cipher.Key, cipher.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (BinaryWriter swEncrypt = new BinaryWriter(csEncrypt))
{
swEncrypt.Write(buffer);
}
buffer = msEncrypt.ToArray();
}
}
}
}
Alternatively, the algorithm that I elucidated from Lantronix architecture looks very straightforward - the API does the encryption, and XORing the output with the plain text is done in the calling-method. Whereas, with .NET library, I don't have such access to the intermediate encrypted output (or is there one?), so that I could XOR the way Lantronix does manually after the encryption...
The end goal is to stop using the unmanaged code, yet should be able to generate the same ciphertext using fully managed .NET code.
Thanks for your help in advance.
p.s. I can provide the 3rd party C library cbx_enc.dll, if you need.
Edit: #Topaco, here are some sample data as requested. Haven’t heard from the vendor as with distributing their DLL; working on it…
Common inputs to CFB:
byte[] buffer = Encoding.ASCII.GetBytes("AAAAAAAAAAAAAABBBBBBBBBBBBBBBBBD"); //plain text
string key = "abcdef0123456789";
string iv = "0123456789ABCDEF";
I/O from the wrapper to the unmanaged DLL:
PlainText Hex: 4141414141414141414141414141424242424242424242424242424242424244
CipherText Hex: C9094F820428E07AE035B6749E18546C62F9D5FD4A78480215DA3625D376A271
I/O from the managed code with FeedbackSize = 128; //CFB128:
PlainText Hex: 4141414141414141414141414141424242424242424242424242424242424244
CipherText Hex: 6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83
I/O from the managed code with FeedbackSize = 8 //CFB8:
PlainText Hex: 4141414141414141414141414141424242424242424242424242424242424244
CipherText Hex: 6ACA3B1159D38568504248CDFF159C87BB2D3850EDAEAD89493BD91087ED7507
I also did the additional test using ECB to see whether their API behaves like ECB (hence comes the need for external XORing). So, I passed the IV to my ECB code as plain text as shown below, and compared it with their output right before the first XOR – they both don’t match either!
Passed IV as the PlainText to ECB : 30313233343536373839414243444546
CipherText Hex: 2B5B11C9ED9B111A065861D29C478FDA
CipherText Hex from the unmanaged DLL, before the first XOR: 88480EC34569A13BA174F735DF59162E
And finally, here is my ECB implementation for the above test:
static readonly string key = "abcdef0123456789";
static readonly string iv = "0123456789ABCDEF";
public static void Encrypt(ref byte[] buffer)
{
buffer = Encoding.ASCII.GetBytes(iv);
Console.WriteLine($"PlainText: {HexHelper.ToHexString(buffer)}");
var aes = new AesManaged
{
KeySize = 128,
Key = Encoding.ASCII.GetBytes(key),
BlockSize = 128,
Mode = CipherMode.ECB,
Padding = PaddingMode.None,
IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
buffer = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
Console.WriteLine($"CipherText: {HexHelper.ToHexString(buffer)}");
}
Thanks.
Like to express my thanks all for the help, especially to #Topaco many thanks – your insight in encoding the plain text in HEX as according to the docs helped!
Here is the revised wrapper code; as you can see its cipher now matches the managed code’s cipher! Perfect!!
static readonly string key = "61626364656630313233343536373839"; //"abcdef0123456789";
static readonly string iv = "0123456789ABCDEF";
public static void Encrypt(ref byte[] buffer)
{
Console.WriteLine($"PlainText: {HexHelper.ToHexString(buffer)}");
var keyPtr = Marshal.StringToHGlobalAnsi(key);
var ivPtr = Marshal.StringToHGlobalAnsi(iv);
byte[] temp = new byte[16];
Marshal.Copy(ivPtr, temp, 0, 16);
int index = 0;
for (int i = 0; i < buffer.Length; i++)
{
if (index == 0)
{
Marshal.Copy(temp, 0, ivPtr, 16);
unsafe
{
VC_blockEncrypt((char*) ivPtr, (char*) keyPtr, 0, (char*) ivPtr, 128);
}
Marshal.Copy(ivPtr, temp, 0, 16);
index = 16;
Console.WriteLine($"CipherText BeforeXOR: {HexHelper.ToHexString(temp)}");
}
temp[16 - index] ^= buffer[i];
buffer[i] = temp[16 - index];
index--;
}
Marshal.FreeHGlobal(ivPtr);
Marshal.FreeHGlobal(keyPtr);
Console.WriteLine($"CipherText: {HexHelper.ToHexString(buffer)}");
}
I/O from the revised wrapper code:
PlainText: 4141414141414141414141414141424242424242424242424242424242424244
CipherText: 6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83
I/O from the managed code:
PlainText: 4141414141414141414141414141424242424242424242424242424242424244
CipherText: 6A1A5088ACDA505B47192093DD06CD987868BFD85278A4D7D3120CC85FCD3D83
Cheers!!
I'm trying to port following code to java, but wondering how this can work in C#.
Provider is being initialized with keySize = 192, but key is only 16 bytes long.
When doing this in java I got error about incorrect key size.
Can someone explain what is going on here?
public static byte[] TripleDesEncryptOneBlock(byte[] plainText)
{
//This is key of length 16 bytes (128 bits)
byte[] key = Encoding.ASCII.GetBytes("0123456789abcdef");
byte[] result;
try
{
int count = (plainText.Length > 8) ? 8 : plainText.Length;
byte[] array = new byte[8];
Buffer.BlockCopy(plainText, 0, array, 0, count);
ICryptoTransform cryptoTransform = new TripleDESCryptoServiceProvider
{
KeySize = 192,
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.None
}.CreateEncryptor();
byte[] array2 = cryptoTransform.TransformFinalBlock(array, 0, 8);
result = array2;
}
catch (Exception)
{
result = null;
}
return result;
}
It's copying the first 8 bytes of the key to the end of the key. The TDEA standard calls this "keying option 2", and it only provides 80 bits of effective security (that is, it's a weak cipher).
Many Java providers won't do this automatically; by forcing the application to do it explicitly, unsuspecting fallback to a weaker scheme is less likely.
I would like to be able to perform application-level encryption in ASP.NET, producing an array of bytes that would then be saved to a MySQL blob column. I would then like it to be an option that, if you have the encryption key, you would be able to decrypt it using MySQL's AES_DECRYPT() function. This seems like it should be possible, since AES_DECRYPT is an implementation of AES/Rijndael.
The MySQL AES_ENCRYPT/DECRYPT functions simply take a key and the string to encrypt/decrypt as parameters. The examples i've seen for encryption in ASP.NET/C#, however, involve also specifying values for Key and IV (initialization vector). How do these affect the final, encrypted byte array, and how can they be taken into account when decrypting with AES_DECRYPT)_?
You can do that by setting RijndaelManaged to use ECB mode.
However, ECB mode is not secure and should be avoided.
In general, a database is a very bad place to perform encryption.
If you are able to encrypt your data in the database, that implies that you have both the ciphertext and the key in the same place; this defeats the purpose of encryption.
You should keep the key as far away from ciphertext storage as possible; using any sort of SQL encryption function is usually indicative of a fundamental design flaw in your encryption strategy which can have disastrous consequences.
Encryption
In Mysql use HEX(AES_ENCRYPT('unencryptedString', 'Password'))
Example
UPDATE `secrets` SET `value`=HEX(AES_ENCRYPT('unencryptedString', 'Password')) WHERE `Id` = 2;
you will see in the database there is a value similar to this D4B5E4CAD92FFB73FCAEB5ED3B31E9EDD8FA7440E9E3F582FE5A9237DB8EE013
Now the equivalent code in C# is (Original Source:link)
public static String AES_encrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
byte[] xXml = Encoding.UTF8.GetBytes(Input);
cs.Write(xXml, 0, xXml.Length);
cs.FlushFinalBlock();
}
xBuff = ms.ToArray();
}
return xBuff.ToHexString();
}
Helper methods and extensions that used
Refernce Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static class ByteArrayExtensions
{
public static string ToHexString(this byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
}
Decryption
in Mysql use CAST(AES_DECRYPT(UNHEX(c.value), 'Password') as char)
Example
SELECT c.*,CAST(AES_DECRYPT(UNHEX(c.`value`), 'Password') as char) FROM `secrets` as c where `Id` = 2;
Equivalent code in C# is
public static String AES_decrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var decrypt = aes.CreateDecryptor();
byte[] encryptedStr = Input.FromHex2ByteArray();
string Plain_Text;
using (var ms = new MemoryStream(encryptedStr))
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cs))
{
Plain_Text = reader.ReadToEnd();
}
}
}
return Plain_Text;
}
Helper methods and extensions that used
Reference Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static byte[] FromHex2ByteArray(this string hex)
{
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}
return arr;
}
private static int GetHexVal(char hex)
{
int val = (int)hex;
//For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
//return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}
Took the vectors from this site http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-ecb-128
In javascript (sjcl) have the same result
var key = [0x2b7e1516,0x28aed2a6,0xabf71588,0x09cf4f3c];
var test = [0x6bc1bee2,0x2e409f96,0xe93d7e11,0x7393172a];
aes = new sjcl.cipher.aes(key);
r = aes.encrypt(test);
console.log(r);
But I can not reach it in the C#
[TestMethod]
public void EncryptIntsToInts()
{
Int32[] key = { unchecked((Int32)0x2b7e1516), 0x28aed2a6, unchecked((Int32)0xabf71588), 0x09cf4f3c };
Int32[] test = { 0x6bc1bee2,0x2e409f96,unchecked((Int32)0xe93d7e11),0x7393172a };
Int32[] answer = { 0x3ad77bb4, 0x0d7a3660, unchecked((Int32)0xa89ecaf3), 0x2466ef97 };
var r = AES.EncryptIntsToInts(test, key.ToByteArray());
Assert.IsTrue(r.SequenceEqual(answer));
}
static byte[] zeroIV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public static Int32[] EncryptIntsToInts(Int32[] input, byte[] key)
{
// Check arguments.
if (input == null || input.Length <= 0)
throw new ArgumentNullException("input");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
byte[] bResult;
try
{
aesAlg = new RijndaelManaged
{
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.None,
KeySize = 128,
BlockSize = 128,
IV = zeroIV
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
byte[] bInput = new byte[input.Length * sizeof(int)];
Buffer.BlockCopy(input, 0, bInput, 0, bInput.Length);
bResult = encryptor.TransformFinalBlock(bInput, 0, input.Length);
}
finally
{
if (aesAlg != null)
aesAlg.Clear();
}
int[] iResult = new int[bResult.Length / sizeof(int)];
Buffer.BlockCopy(bResult, 0, iResult, 0, bResult.Length);
return iResult;
}
What is my error?
========================================================
Start edit
New code in which right order of the bytes, but it does not work
[TestMethod]
public void EncryptIntsToInts()
{
byte[] key = "2b7e151628aed2a6abf7158809cf4f3c".HEX2Bytes();
byte[] test = "6bc1bee22e409f96e93d7e117393172a".HEX2Bytes();
byte[] answer = "3ad77bb40d7a3660a89ecaf32466ef97".HEX2Bytes();
RijndaelManaged aesAlg = new RijndaelManaged
{
Key = key,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
IV = zeroIV
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor();
var r = encryptor.TransformFinalBlock(test, 0, test.Length);
Assert.IsTrue(r.SequenceEqual(answer));
}
public static byte[] HEX2Bytes(this string hex)
{
if (hex.Length%2 != 0)
{
throw new ArgumentException(String.Format(CultureInfo.InvariantCulture,
"The binary key cannot have an odd number of digits: {0}", hex));
}
byte[] HexAsBytes = new byte[hex.Length/2];
for (int index = 0; index < HexAsBytes.Length; index++)
{
string byteValue = hex.Substring(index*2, 2);
HexAsBytes[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return HexAsBytes;
}
static byte[] zeroIV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Right code (just add a try / using):
[TestMethod]
public void EncryptIntsToInts()
{
byte[] key = "2b7e151628aed2a6abf7158809cf4f3c".HEX2Bytes();
byte[] test = "6bc1bee22e409f96e93d7e117393172a".HEX2Bytes();
byte[] answer = "3ad77bb40d7a3660a89ecaf32466ef97".HEX2Bytes();
var r = AES.Encrypt(test, key);
Assert.IsTrue(answer.SequenceEqual(r));
}
public static byte[] Encrypt(byte[] input, byte[] key)
{
var aesAlg = new AesManaged
{
KeySize = 128,
Key = key,
BlockSize = 128,
Mode = CipherMode.ECB,
Padding = PaddingMode.Zeros,
IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
return encryptor.TransformFinalBlock(input, 0, input.Length);
}
You use 32 bit integers to define the key. When you transform them to bytes, you use native endianness, which typically is little endian. So your key is 16157e2b a6... and not 2b7e1516 28....
I wouldn't use ints to represent a key in the first place. But if you really want to, write a big endian conversion function.
I also strongly recommend against ECB mode. You could use CBC together with HMAC (in an encrypt then mac construction), or use a third party lib to implement GCM.
I would like to decrypt a file that I previously encrypted with C# using the TripleDESCryptoServiceProvider.
Here's my code for encrypting:
private static void EncryptData(MemoryStream streamToEncrypt)
{
// initialize the encryption algorithm
TripleDES algorithm = new TripleDESCryptoServiceProvider();
byte[] desIV = new byte[8];
byte[] desKey = new byte[16];
for (int i = 0; i < 8; ++i)
{
desIV[i] = (byte)i;
}
for (int j = 0; j < 16; ++j)
{
desKey[j] = (byte)j;
}
FileStream outputStream = new FileStream(TheCryptedSettingsFilePath, FileMode.OpenOrCreate, FileAccess.Write);
outputStream.SetLength(0);
CryptoStream encStream = new CryptoStream(outputStream, algorithm.CreateEncryptor(desKey, desIV),
CryptoStreamMode.Write);
// write the encrypted data to the file
encStream.Write(streamToEncrypt.ToArray(), 0, (int)streamToEncrypt.Length);
encStream.Close();
outputStream.Close();
}
I already found the Crypto++ library and managed to build and link it. So I tried to decrypt the file that was stored with C# after the encryption with the following (native) C++ code:
FILE *fp;
long len;
char *buf;
if (_wfopen_s(&fp, _T("MyTest.bin"), _T("rb")) != 0)
{
return false;
}
fseek(fp ,0 ,SEEK_END); //go to end
len = ftell(fp); //get position at end (length)
fseek(fp, 0, SEEK_SET); //go to beg.
buf = (char *)malloc(len); //malloc buffer
fread(buf, len, 1, fp); //read into buffer
fclose(fp);
BYTE pIV[] = {0, 1, 2, 3, 4, 5, 6, 7};
BYTE pKey[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
const BYTE* lpData = (const BYTE*)(LPCTSTR)buf;
size_t bufferSize = strlen(buf);
BYTE* result = (BYTE *)malloc(bufferSize);
CFB_FIPS_Mode<DES_EDE2>::Decryption decryption_DES_EDE2_CFB;
decryption_DES_EDE2_CFB.SetKeyWithIV(pKey, sizeof(pKey), pIV, sizeof(pIV));
decryption_DES_EDE2_CFB.ProcessString(result, lpData, bufferSize);
That code won't decrypt properly. The result after the decryption doesn't match the plain text that was encrypted previously. Any idea about my code?
Can you encrypt and decrypt in c++?
Can you encrypt and decrypt in c#?
Are you sure you are using the same mode, padding and encrypt, decrypt sequence?
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
I managed to do that task with Windows Crypto API as stated in my other post.
Try CBC mode (the TripleDESCryptoServiceProvider's default mode)