RijndaelManaged 解密问题
RijndaelManaged Decryption Issue
我支持最初于 1990 年代早期开发的庞大 C++ 系统。 (我在我的其他帖子中提到过这个系统)我们正在开发所有新的 C# 应用程序以取代旧的 C++ 系统。使用 TcpClient,将未加密或加密的命令发送到我们的 C# 应用程序或从我们的 C# 应用程序发送到 C++ 系统。 (加密是可选功能)。未加密的命令可以毫无问题地发送和接收。加密命令是另一回事。我们可以成功地加密我们的命令,C++ 系统将接受这些命令。但是,从 C++ 发送到我们的 C# 系统的加密消息不能正确解密。我已经进行了一些解密,但都没有失败,但它们都导致了一个神秘的字符串。加密使用 RijndaelManaged 和 CryptoStream,它起源于 1990 年代初期。如果外面有人能让解密代码工作,我会在未来很多年里对你说好话 :-)(如果你想知道为什么我们不用更简单的东西替换加密样式,那是因为几个其他较旧的 C++ 系统仍在使用此技术)
对于发送加密的命令,这可以正常工作:
//Generic messages array
var messages = new string[4];
messages[0] = "";
messages[1] = "Service Manager";
messages[2] =
"<COMMAND_BLOCK><OPERATOR User=\"1234\" Password=\"password\"/>" +
"<COMMAND Target=\"targetServer\" Command=\"1024\" " +
"Data=\"1\" Priority=\"HIGH\" Id=\"-1\" " +
"Workstation=\"Server1\"/></COMMAND_BLOCK>{Convert.ToChar(10)}";
messages[3] = IsEncryptionEnabled ? "1" : "0";
var formattedMessage = $"{messages[1]}{Convert.ToChar(10)}{messages[2]}";
formattedMessage =
$"{Convert.ToChar(messages[2].Length)}{Convert.ToChar((int)Math.Floor((double)messages[2].Length / 255))}{formattedMessage}";
if (WebApiCommon.Globals.IsEncryptionEnabled) //Yes, encryption is crazy
{
var commandBytes = Encoding.UTF8.GetBytes(messages[2]);
var messageLength = commandBytes.Length;
var allocatedLength = messageLength;
var blockLength = 16;
allocatedLength = ((messageLength + blockLength - 1) / blockLength) * blockLength;
var messageBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x++)
{
if (x < messageLength)
messageBytes[x] = commandBytes[x];
else // pad out for encryption
messageBytes[x] = 0;
}
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
//create encryption object
var rijndael = new RijndaelManaged
{
BlockSize = 128,
KeySize = 128,
Padding = PaddingMode.None
};
//break into blockLength byte blocks and encrypt
var block = new byte[blockLength];
var encryptedBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x += blockLength)
{
// copy current block
for (var j = 0; j < blockLength; j++)
block[j] = messageBytes[x + j];
using (var ms1 = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(block, 0, block.Length);
var block1 = ms1.ToArray();
for (var j = 0; j < blockLength; j++)
encryptedBytes[x + j] = block1[j];
}
}
var lsbMessageLength = new byte[2];
lsbMessageLength[0] = (byte)(allocatedLength & 0xFF);
lsbMessageLength[1] = (byte)(((allocatedLength >> 8) & 0xF) + 0x40); // 0x40 = flag to enable encryption
var controlPortStream = controlConnection.GetStream();
controlPortStream.Write(lsbMessageLength, 0, lsbMessageLength.Length);
controlPortStream.Write(encryptedBytes, 0, encryptedBytes.Length);
controlPortStream.Flush();
}
我包括了我所有的解密代码尝试,注释掉了,连同我希望工作但没有工作的解密代码。如果有人能看到我做错了什么,请告诉我(首选代码):
private string DecryptMPString(string incomingMessage)
{
var keyString =
WebApiCommon.ApiRequests.SysSettings.GetList(new SysSettingDto { SectionName = "Configuration", VariableName = "Site name" }).FirstOrDefault()?.Value;
var encryptedBytes2 = Encoding.UTF8.GetBytes(incomingMessage);
var encryptedBytes = ConvertHexToAscii(encryptedBytes2);
var messageLength = encryptedBytes.Length;
var allocatedLength = messageLength;
var blockLength = 16;
allocatedLength = ((messageLength + blockLength - 1) / blockLength) * blockLength;
var messageBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x++)
{
if (x < messageLength)
messageBytes[x] = encryptedBytes[x];
else // pad out for encryption
messageBytes[x] = 0;
}
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
//create encryption object
//var rijndael = new RijndaelManaged
//{
// BlockSize = 128,
// KeySize = 128,
// Padding = PaddingMode.None
//};
//break into blockLength byte blocks and encrypt
//var block = new byte[blockLength];
//var decryptedBytes = new byte[allocatedLength];
//for (var x = 0; x < allocatedLength; x += blockLength)
//{
// // copy current block
// for (var j = 0; j < blockLength; j++)
// block[j] = messageBytes[x + j];
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
//using (var encryptedStream = new MemoryStream(encryptedBytes))
// {
// using (var decryptedStream = new MemoryStream())
// {
// using (var cryptoStream = new CryptoStream(encryptedStream, rijndael.CreateDecryptor(key, iv), CryptoStreamMode.Read))
// {
// int data;
// while ((data = cryptoStream.ReadByte()) != -1)
// decryptedStream.WriteByte((byte)data);
// }
// return Encoding.UTF8.GetString(decryptedStream.ToArray(), 0, decryptedStream.ToArray().Length);
// //var block1 = decryptedStream.ToArray();
// //for (var j = 0; j < blockLength; j++)
// // decryptedBytes[x + j] = block1[j];
// }
//}
//}
// return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
//using (var encryptedStream = new MemoryStream(encoding.GetBytes(incomingMessage)))
//{
// //stream where decrypted contents will be stored
// using (var decryptedStream = new MemoryStream())
// {
// using (var aes = new RijndaelManaged { KeySize = 128, BlockSize = 128, Padding = PaddingMode.None })
// {
// using (var decryptor = aes.CreateDecryptor(key, iv))
// {
// //decrypt stream and write it to parent stream
// using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
// {
// int data;
// while ((data = cryptoStream.ReadByte()) != -1)
// decryptedStream.WriteByte((byte)data);
// }
// }
// }
// //reset position in prep for reading
// decryptedStream.Position = 0;
// var decryptedBytes = decryptedStream.ToArray();
// return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
// }
//}
}
private static byte[] ConvertHexToAscii(byte[] hexBytes)
{
int newLength = (hexBytes.Length) / 2;
var buffer = new byte[newLength];
for (int i = 0, j = 0; i < newLength; i++, j += 2)
buffer[i] = (byte)(((hexBytes[j] - '0') << 4) + (hexBytes[j + 1] - '0'));
return buffer;
}
这是最初提出问题后的几天,但这里是用于创建消息的 C++ 加密代码,由我的 C# 解决方案拾取:
BYTE* CEncrypt::Encrypt(LPCTSTR pszData,WORD& nLength)
{
if(nLength && m_bEncryptEnabled && m_nBlockSize == 16)
{
int nBufferSize = m_nBlockSize * (nLength/m_nBlockSize + (nLength % m_nBlockSize == 0?0:1));
BYTE* cBuffer = new BYTE[nBufferSize];
if(cBuffer)
{
memcpy(cBuffer,pszData,nLength);
while (nLength < nBufferSize) // zero last bytes in case short
cBuffer[nLength++] = '[=12=]';
for (int nIndex = 0; nIndex < nLength; nIndex += m_nBlockSize)
{
Encrypt(cBuffer + nIndex);
}
return cBuffer;
}
}
return NULL;
}
/* There is an obvious time/space trade-off possible here. *
* Instead of just one ftable[], I could have 4, the other *
* 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */
void CEncrypt::Encrypt(BYTE *buff)
{
int i,j,k,m;
DWORD a[8],b[8],*x,*y,*t;
for (i=j=0; i<Nb; i++,j+=4)
{
a[i] = pack((BYTE *)&buff[j]);
a[i] ^= fkey[i];
}
k = Nb;
x = a;
y = b;
/* State alternates between a and b */
for (i=1; i<Nr; i++)
{ /* Nr is number of rounds. May be odd. */
/* if Nb is fixed - unroll this next loop and hard-code in the values of fi[] */
for (m=j=0; j<Nb; j++,m+=3)
{ /* deal with each 32-bit element of the State */
/* This is the time-critical bit */
y[j]=fkey[k++]^ftable[(BYTE)x[j]]^
ROTL8(ftable[(BYTE)(x[fi[m]]>>8)])^
ROTL16(ftable[(BYTE)(x[fi[m+1]]>>16)])^
ROTL24(ftable[x[fi[m+2]]>>24]);
}
t = x;
x = y;
y = t; /* swap pointers */
}
/* Last Round - unroll if possible */
for (m=j=0; j<Nb; j++,m+=3)
{
y[j] = fkey[k++]^(DWORD)fbsub[(BYTE)x[j]]^
ROTL8((DWORD)fbsub[(BYTE)(x[fi[m]]>>8)])^
ROTL16((DWORD)fbsub[(BYTE)(x[fi[m+1]]>>16)])^
ROTL24((DWORD)fbsub[x[fi[m+2]]>>24]);
}
for (i=j=0; i<Nb; i++,j+=4)
{
unpack(y[i], (BYTE *)&buff[j]);
x[i] = y[i] = 0; /* clean up stack */
}
return;
}
遇到这样的问题,通常最好将其分解成尽可能小的部分。在这种情况下,您应该将加密和解密功能与所有其他逻辑分开。这将使您能够测试这些函数,将它们的输出与已知的良好函数进行比较,并且能够 运行 通过代码而无需太多工作:
static byte[] Encrypt(string[] messages)
{
string keyString = "mysupersecretkey";
var formattedMessage = $"{messages[1]}{Convert.ToChar(10)}{messages[2]}";
formattedMessage =
$"{Convert.ToChar(messages[2].Length)}{Convert.ToChar((int)Math.Floor((double)messages[2].Length / 255))}{formattedMessage}";
if (true /*WebApiCommon.Globals.IsEncryptionEnabled*/) //Yes, encryption is crazy
{
var commandBytes = Encoding.UTF8.GetBytes(formattedMessage);
// Create a buffer block-aligned that's big enough for commandBytes
var messageBytes = new byte[((commandBytes.Length - 1) | 15) + 1];
// Copy commandBytes into the buffer
Buffer.BlockCopy(commandBytes, 0, messageBytes, 0, commandBytes.Length);
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
//create encryption object
var rijndael = new RijndaelManaged
{
BlockSize = 128,
KeySize = 128,
Padding = PaddingMode.None
};
using (var ms1 = new MemoryStream())
{
// Encrypt messageBytes
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(messageBytes, 0, messageBytes.Length);
var encryptedBytes = ms1.ToArray();
// Create the message length
var lsbMessageLength = new byte[2];
lsbMessageLength[0] = (byte)(encryptedBytes.Length & 0xFF);
lsbMessageLength[1] = (byte)(((encryptedBytes.Length >> 8) & 0xF) + 0x40); // 0x40 = flag to enable encryption
// Combine the message length along with the encrypted bytes
byte[] ret = new byte[lsbMessageLength.Length + encryptedBytes.Length];
Buffer.BlockCopy(lsbMessageLength, 0, ret, 0, lsbMessageLength.Length);
Buffer.BlockCopy(encryptedBytes, 0, ret, lsbMessageLength.Length, encryptedBytes.Length);
// Return it
return ret;
}
}
}
static string Decrypt(byte[] encryptedBytes)
{
string keyString = "mysupersecretkey";
// Get the message bytes from the bytes
var allocatedLength = (encryptedBytes[1] & 0xF) << 8 | encryptedBytes[0];
// And pull out the encrypted bytes
var messageBytes = new byte[allocatedLength];
Buffer.BlockCopy(encryptedBytes, 2, messageBytes, 0, allocatedLength);
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
}
然后您可以通过几个快速调用来练习:
string[] messages = { "", "system name", "Command" };
var encrypted = Encrypt(messages);
Console.WriteLine(BitConverter.ToString(encrypted).Replace("-", ""));
var decrypted = Decrypt(encrypted);
Console.WriteLine(decrypted);
输出:
2040622E097E5E8D438C9156384757A56C83B764BDF5B0D34346B2A7D56BD96016D7
system name
Command
然后您可以根据 C++ 实现验证加密的字符串是否正确,并且解密的响应没有 运行通过其余的网络代码。
问题是您的加密代码为每个块创建了一个新的加密器:
for (var x = 0; x < allocatedLength; x += blockLength)
{
// copy current block
for (var j = 0; j < blockLength; j++)
block[j] = messageBytes[x + j];
using (var ms1 = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(block, 0, block.Length);
var block1 = ms1.ToArray();
for (var j = 0; j < blockLength; j++)
encryptedBytes[x + j] = block1[j];
}
}
... 这使得它本质上是一个 ECB 转换。 C++ 代码做同样的事情。
在您的 C# Decrypt 上,您尝试一次解密整个块:
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
...这将适用于第一个块,但不适用于其余块。将其更改为使用新的解密器处理每个块......它会起作用:
string finalString = "";
var blockLength = 16;
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
for (var bytePos = 0; bytePos < allocatedLength; bytePos += blockLength)
{
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes.Skip(bytePos).ToArray(), 0, blockLength);
finalString += Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
}
return finalString;
我支持最初于 1990 年代早期开发的庞大 C++ 系统。 (我在我的其他帖子中提到过这个系统)我们正在开发所有新的 C# 应用程序以取代旧的 C++ 系统。使用 TcpClient,将未加密或加密的命令发送到我们的 C# 应用程序或从我们的 C# 应用程序发送到 C++ 系统。 (加密是可选功能)。未加密的命令可以毫无问题地发送和接收。加密命令是另一回事。我们可以成功地加密我们的命令,C++ 系统将接受这些命令。但是,从 C++ 发送到我们的 C# 系统的加密消息不能正确解密。我已经进行了一些解密,但都没有失败,但它们都导致了一个神秘的字符串。加密使用 RijndaelManaged 和 CryptoStream,它起源于 1990 年代初期。如果外面有人能让解密代码工作,我会在未来很多年里对你说好话 :-)(如果你想知道为什么我们不用更简单的东西替换加密样式,那是因为几个其他较旧的 C++ 系统仍在使用此技术)
对于发送加密的命令,这可以正常工作:
//Generic messages array
var messages = new string[4];
messages[0] = "";
messages[1] = "Service Manager";
messages[2] =
"<COMMAND_BLOCK><OPERATOR User=\"1234\" Password=\"password\"/>" +
"<COMMAND Target=\"targetServer\" Command=\"1024\" " +
"Data=\"1\" Priority=\"HIGH\" Id=\"-1\" " +
"Workstation=\"Server1\"/></COMMAND_BLOCK>{Convert.ToChar(10)}";
messages[3] = IsEncryptionEnabled ? "1" : "0";
var formattedMessage = $"{messages[1]}{Convert.ToChar(10)}{messages[2]}";
formattedMessage =
$"{Convert.ToChar(messages[2].Length)}{Convert.ToChar((int)Math.Floor((double)messages[2].Length / 255))}{formattedMessage}";
if (WebApiCommon.Globals.IsEncryptionEnabled) //Yes, encryption is crazy
{
var commandBytes = Encoding.UTF8.GetBytes(messages[2]);
var messageLength = commandBytes.Length;
var allocatedLength = messageLength;
var blockLength = 16;
allocatedLength = ((messageLength + blockLength - 1) / blockLength) * blockLength;
var messageBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x++)
{
if (x < messageLength)
messageBytes[x] = commandBytes[x];
else // pad out for encryption
messageBytes[x] = 0;
}
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
//create encryption object
var rijndael = new RijndaelManaged
{
BlockSize = 128,
KeySize = 128,
Padding = PaddingMode.None
};
//break into blockLength byte blocks and encrypt
var block = new byte[blockLength];
var encryptedBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x += blockLength)
{
// copy current block
for (var j = 0; j < blockLength; j++)
block[j] = messageBytes[x + j];
using (var ms1 = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(block, 0, block.Length);
var block1 = ms1.ToArray();
for (var j = 0; j < blockLength; j++)
encryptedBytes[x + j] = block1[j];
}
}
var lsbMessageLength = new byte[2];
lsbMessageLength[0] = (byte)(allocatedLength & 0xFF);
lsbMessageLength[1] = (byte)(((allocatedLength >> 8) & 0xF) + 0x40); // 0x40 = flag to enable encryption
var controlPortStream = controlConnection.GetStream();
controlPortStream.Write(lsbMessageLength, 0, lsbMessageLength.Length);
controlPortStream.Write(encryptedBytes, 0, encryptedBytes.Length);
controlPortStream.Flush();
}
我包括了我所有的解密代码尝试,注释掉了,连同我希望工作但没有工作的解密代码。如果有人能看到我做错了什么,请告诉我(首选代码):
private string DecryptMPString(string incomingMessage)
{
var keyString =
WebApiCommon.ApiRequests.SysSettings.GetList(new SysSettingDto { SectionName = "Configuration", VariableName = "Site name" }).FirstOrDefault()?.Value;
var encryptedBytes2 = Encoding.UTF8.GetBytes(incomingMessage);
var encryptedBytes = ConvertHexToAscii(encryptedBytes2);
var messageLength = encryptedBytes.Length;
var allocatedLength = messageLength;
var blockLength = 16;
allocatedLength = ((messageLength + blockLength - 1) / blockLength) * blockLength;
var messageBytes = new byte[allocatedLength];
for (var x = 0; x < allocatedLength; x++)
{
if (x < messageLength)
messageBytes[x] = encryptedBytes[x];
else // pad out for encryption
messageBytes[x] = 0;
}
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
//create encryption object
//var rijndael = new RijndaelManaged
//{
// BlockSize = 128,
// KeySize = 128,
// Padding = PaddingMode.None
//};
//break into blockLength byte blocks and encrypt
//var block = new byte[blockLength];
//var decryptedBytes = new byte[allocatedLength];
//for (var x = 0; x < allocatedLength; x += blockLength)
//{
// // copy current block
// for (var j = 0; j < blockLength; j++)
// block[j] = messageBytes[x + j];
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
//using (var encryptedStream = new MemoryStream(encryptedBytes))
// {
// using (var decryptedStream = new MemoryStream())
// {
// using (var cryptoStream = new CryptoStream(encryptedStream, rijndael.CreateDecryptor(key, iv), CryptoStreamMode.Read))
// {
// int data;
// while ((data = cryptoStream.ReadByte()) != -1)
// decryptedStream.WriteByte((byte)data);
// }
// return Encoding.UTF8.GetString(decryptedStream.ToArray(), 0, decryptedStream.ToArray().Length);
// //var block1 = decryptedStream.ToArray();
// //for (var j = 0; j < blockLength; j++)
// // decryptedBytes[x + j] = block1[j];
// }
//}
//}
// return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
//using (var encryptedStream = new MemoryStream(encoding.GetBytes(incomingMessage)))
//{
// //stream where decrypted contents will be stored
// using (var decryptedStream = new MemoryStream())
// {
// using (var aes = new RijndaelManaged { KeySize = 128, BlockSize = 128, Padding = PaddingMode.None })
// {
// using (var decryptor = aes.CreateDecryptor(key, iv))
// {
// //decrypt stream and write it to parent stream
// using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
// {
// int data;
// while ((data = cryptoStream.ReadByte()) != -1)
// decryptedStream.WriteByte((byte)data);
// }
// }
// }
// //reset position in prep for reading
// decryptedStream.Position = 0;
// var decryptedBytes = decryptedStream.ToArray();
// return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
// }
//}
}
private static byte[] ConvertHexToAscii(byte[] hexBytes)
{
int newLength = (hexBytes.Length) / 2;
var buffer = new byte[newLength];
for (int i = 0, j = 0; i < newLength; i++, j += 2)
buffer[i] = (byte)(((hexBytes[j] - '0') << 4) + (hexBytes[j + 1] - '0'));
return buffer;
}
这是最初提出问题后的几天,但这里是用于创建消息的 C++ 加密代码,由我的 C# 解决方案拾取:
BYTE* CEncrypt::Encrypt(LPCTSTR pszData,WORD& nLength)
{
if(nLength && m_bEncryptEnabled && m_nBlockSize == 16)
{
int nBufferSize = m_nBlockSize * (nLength/m_nBlockSize + (nLength % m_nBlockSize == 0?0:1));
BYTE* cBuffer = new BYTE[nBufferSize];
if(cBuffer)
{
memcpy(cBuffer,pszData,nLength);
while (nLength < nBufferSize) // zero last bytes in case short
cBuffer[nLength++] = '[=12=]';
for (int nIndex = 0; nIndex < nLength; nIndex += m_nBlockSize)
{
Encrypt(cBuffer + nIndex);
}
return cBuffer;
}
}
return NULL;
}
/* There is an obvious time/space trade-off possible here. *
* Instead of just one ftable[], I could have 4, the other *
* 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */
void CEncrypt::Encrypt(BYTE *buff)
{
int i,j,k,m;
DWORD a[8],b[8],*x,*y,*t;
for (i=j=0; i<Nb; i++,j+=4)
{
a[i] = pack((BYTE *)&buff[j]);
a[i] ^= fkey[i];
}
k = Nb;
x = a;
y = b;
/* State alternates between a and b */
for (i=1; i<Nr; i++)
{ /* Nr is number of rounds. May be odd. */
/* if Nb is fixed - unroll this next loop and hard-code in the values of fi[] */
for (m=j=0; j<Nb; j++,m+=3)
{ /* deal with each 32-bit element of the State */
/* This is the time-critical bit */
y[j]=fkey[k++]^ftable[(BYTE)x[j]]^
ROTL8(ftable[(BYTE)(x[fi[m]]>>8)])^
ROTL16(ftable[(BYTE)(x[fi[m+1]]>>16)])^
ROTL24(ftable[x[fi[m+2]]>>24]);
}
t = x;
x = y;
y = t; /* swap pointers */
}
/* Last Round - unroll if possible */
for (m=j=0; j<Nb; j++,m+=3)
{
y[j] = fkey[k++]^(DWORD)fbsub[(BYTE)x[j]]^
ROTL8((DWORD)fbsub[(BYTE)(x[fi[m]]>>8)])^
ROTL16((DWORD)fbsub[(BYTE)(x[fi[m+1]]>>16)])^
ROTL24((DWORD)fbsub[x[fi[m+2]]>>24]);
}
for (i=j=0; i<Nb; i++,j+=4)
{
unpack(y[i], (BYTE *)&buff[j]);
x[i] = y[i] = 0; /* clean up stack */
}
return;
}
遇到这样的问题,通常最好将其分解成尽可能小的部分。在这种情况下,您应该将加密和解密功能与所有其他逻辑分开。这将使您能够测试这些函数,将它们的输出与已知的良好函数进行比较,并且能够 运行 通过代码而无需太多工作:
static byte[] Encrypt(string[] messages)
{
string keyString = "mysupersecretkey";
var formattedMessage = $"{messages[1]}{Convert.ToChar(10)}{messages[2]}";
formattedMessage =
$"{Convert.ToChar(messages[2].Length)}{Convert.ToChar((int)Math.Floor((double)messages[2].Length / 255))}{formattedMessage}";
if (true /*WebApiCommon.Globals.IsEncryptionEnabled*/) //Yes, encryption is crazy
{
var commandBytes = Encoding.UTF8.GetBytes(formattedMessage);
// Create a buffer block-aligned that's big enough for commandBytes
var messageBytes = new byte[((commandBytes.Length - 1) | 15) + 1];
// Copy commandBytes into the buffer
Buffer.BlockCopy(commandBytes, 0, messageBytes, 0, commandBytes.Length);
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
//create encryption object
var rijndael = new RijndaelManaged
{
BlockSize = 128,
KeySize = 128,
Padding = PaddingMode.None
};
using (var ms1 = new MemoryStream())
{
// Encrypt messageBytes
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(messageBytes, 0, messageBytes.Length);
var encryptedBytes = ms1.ToArray();
// Create the message length
var lsbMessageLength = new byte[2];
lsbMessageLength[0] = (byte)(encryptedBytes.Length & 0xFF);
lsbMessageLength[1] = (byte)(((encryptedBytes.Length >> 8) & 0xF) + 0x40); // 0x40 = flag to enable encryption
// Combine the message length along with the encrypted bytes
byte[] ret = new byte[lsbMessageLength.Length + encryptedBytes.Length];
Buffer.BlockCopy(lsbMessageLength, 0, ret, 0, lsbMessageLength.Length);
Buffer.BlockCopy(encryptedBytes, 0, ret, lsbMessageLength.Length, encryptedBytes.Length);
// Return it
return ret;
}
}
}
static string Decrypt(byte[] encryptedBytes)
{
string keyString = "mysupersecretkey";
// Get the message bytes from the bytes
var allocatedLength = (encryptedBytes[1] & 0xF) << 8 | encryptedBytes[0];
// And pull out the encrypted bytes
var messageBytes = new byte[allocatedLength];
Buffer.BlockCopy(encryptedBytes, 2, messageBytes, 0, allocatedLength);
while (keyString.Length < 16) // make sure keystring is exactly 16 bytes.
keyString += keyString;
if (keyString.Length > 16)
keyString = keyString.Substring(0, 16);
var encoding = Encoding.GetEncoding(1252); // ANSI 1252
var key = encoding.GetBytes(keyString);
//create empty IV
var iv = new byte[16];
for (var x = 0; x < iv.Length; x++)
iv[x] = 0;
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
}
然后您可以通过几个快速调用来练习:
string[] messages = { "", "system name", "Command" };
var encrypted = Encrypt(messages);
Console.WriteLine(BitConverter.ToString(encrypted).Replace("-", ""));
var decrypted = Decrypt(encrypted);
Console.WriteLine(decrypted);
输出:
2040622E097E5E8D438C9156384757A56C83B764BDF5B0D34346B2A7D56BD96016D7
system name
Command
然后您可以根据 C++ 实现验证加密的字符串是否正确,并且解密的响应没有 运行通过其余的网络代码。
问题是您的加密代码为每个块创建了一个新的加密器:
for (var x = 0; x < allocatedLength; x += blockLength)
{
// copy current block
for (var j = 0; j < blockLength; j++)
block[j] = messageBytes[x + j];
using (var ms1 = new MemoryStream())
{
using (var cs1 = new CryptoStream(ms1, rijndael.CreateEncryptor(key, iv), CryptoStreamMode.Write))
cs1.Write(block, 0, block.Length);
var block1 = ms1.ToArray();
for (var j = 0; j < blockLength; j++)
encryptedBytes[x + j] = block1[j];
}
}
... 这使得它本质上是一个 ECB 转换。 C++ 代码做同样的事情。
在您的 C# Decrypt 上,您尝试一次解密整个块:
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes, 0, allocatedLength);
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
...这将适用于第一个块,但不适用于其余块。将其更改为使用新的解密器处理每个块......它会起作用:
string finalString = "";
var blockLength = 16;
using (var rijndael = new RijndaelManaged())
{
rijndael.BlockSize = 128;
rijndael.KeySize = 128;
rijndael.Padding = PaddingMode.None;
for (var bytePos = 0; bytePos < allocatedLength; bytePos += blockLength)
{
var decryptor = rijndael.CreateDecryptor(key, iv);
var decryptedBytes = decryptor.TransformFinalBlock(messageBytes.Skip(bytePos).ToArray(), 0, blockLength);
finalString += Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length);
}
}
return finalString;