在 C# 中加密和使用 Crypto++ 解密不起作用
Encrypting in C# and Decrypting with Crypto++ does not work
加密:
public static byte[] EncryptAES(Message msg)
{
byte[] encText; // This will keep the encrypted text
byte[] encLength; // This will keep the length of the encrypted text
byte[] finalEncText = null; // This keeps the encLength + encText (#####[encText] / [encLength][encText])
// Building the plaintext message :
string plainText = msg.MessageCode.ToString();
if (msg.Parameters != null)
foreach (string parameter in msg.Parameters)
plainText += parameter;
// Encrypting the plaintext :
encText = EncryptAES(plainText);
string encLen = encText.Length.ToString();
string fittedEncLen = MessageSender.FitStringIntoSize(encLen, Globals.MESSAGE_LENGTH_LEN); // Fit the length of the encrypted text into a certain size
encLength = Encoding.ASCII.GetBytes(fittedEncLen); // convert the length into byte[]
finalEncText = new byte[encLength.Length + encText.Length];
System.Buffer.BlockCopy(encLength, 0, finalEncText, 0, encLength.Length);
System.Buffer.BlockCopy(encText, 0, finalEncText, encLength.Length, encText.Length); // Copy the byte arrays into the new byte array
return finalEncText;
}
private static byte[] EncryptAES(string text)
{
// This function encrypts a plaintext message using the aes key we have from the server
if (AesKey == null || IV == null) // If we dont have an aes key / iv, dont encrypt
return Encoding.ASCII.GetBytes(text);
byte[] encryptedText;
try
{
Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(AesKey);
aes.IV = Encoding.ASCII.GetBytes(IV);
ICryptoTransform cryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
}
aes.Dispose();
}
catch
{
// In case of an error while encrypting, dont encrypt
encryptedText = Encoding.ASCII.GetBytes(text);
}
return encryptedText;
}
[添加的fittedEncLen基本上是一个固定长度为5个字符的前缀,其中包含其后的加密消息的长度,在解密之前服务器读取这5个字符然后解密加密的部分]
正在发送消息到服务器 [TCPClient] [C#] :
public int Send(Message message)
{
/*
* Encrpyts the message and then sends it to the network stream.
*
* Return code:
* 0 on success.
* -1 on failure.
*/
byte[] msg = Cryptography.EncryptAES(message); // Encrypt the message
// Sending message
try
{
this._networkStream.Write(msg, 0, msg.Length);
this._networkStream.Flush();
}
catch
{
return -1;
}
return 0;
}
正在接收 [C++]:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
char* Helper::getPartFromSocket(SOCKET sc, int bytesNum, int flags)
{
if (bytesNum == 0)
return "";
char* data = new char[bytesNum + 1];
int res = recv(sc, data, bytesNum, flags);
if (res == INVALID_SOCKET)
{
string s = "Error while recieving from socket: ";
s += to_string(sc);
throw exception(s.c_str());
}
data[bytesNum] = 0;
return data;
}
BufferedString* Helper::makeBufferedString(SOCKET sc)
{
/*
The socket contains <length of encrypted message (unencrypted)> <encrypted message>.
This function will read the length of the unencrypted message, read
the encrypted message, decrypt it, store it in a BufferedString
object and return the object.
Length of length number: MESSAGE_LENGTH_LEN.
*/
int sizeOfMessage = Helper::getIntPartFromSocket(sc, MESSAGE_LENGTH_LEN);
if (sizeOfMessage == 0)
return NULL;
wstring wideString = getWideStringPartFromSocket(sc, sizeOfMessage);
string decrypted = "";
if (wideString.length() < sizeOfMessage)
{
std::wstringstream cls;
cls << wideString;
cls << getWideStringPartFromSocket(sc, sizeOfMessage - wideString.length());
wideString = cls.str();
}
SocketEncryptionKeychain* keyChain = SocketEncryptionKeychain::getKeychain(sc);
if (keyChain != nullptr) // If the socket has a keychain, decrypt the message
decrypted = Cryptography::decryptAES(wideString, keyChain->getKey(), keyChain->getIV()); // Try to decrypt the message
else // If the keychain is null, just convert the widestring to a string
decrypted = wideStringToString(wideString);
return new BufferedString(decrypted);
}
SocketEncryptionKeychain 基本上包含每个套接字的 AES 密钥和 IV
BufferedString 是一个包含字符串的 class ,您可以像从套接字中读取一样从中读取[它是一个缓冲区,一旦您从中读取,您读取的内容就会被删除] [基本上是一个字符串缓冲区,没什么特别的]
正在解密 [C++]:
string Cryptography::decryptAES(wstring cipherText, byte aesKey[], byte iv[])
{
if (aesKey == nullptr || iv == nullptr) // If the key or iv are null, dont decrypt
return Helper::wideStringToString(cipherText);
string plaintext;
try
{
// Decrypt :
byte* cipher = wideStringToByteArray(cipherText); // Convert the wide string to byte*
CryptoPP::AES::Decryption aesDecryption(aesKey, 32);
CryptoPP::CBC_Mode_ExternalCipher::Decryption ecbDecryption(aesDecryption, iv);
CryptoPP::StreamTransformationFilter stfDecryptor(ecbDecryption, new CryptoPP::StringSink(plaintext), StreamTransformationFilter::ZEROS_PADDING);
stfDecryptor.Put(cipher, cipherText.length());
stfDecryptor.MessageEnd();
Helper::safeDelete(cipher);
}
catch (CryptoPP::InvalidCiphertext& ex)
{
// In case of an error don't decrypt
plaintext = Helper::wideStringToString(cipherText);
}
return plaintext;
}
byte* Cryptography::wideStringToByteArray(wstring text)
{
// This function translates the wstring into a byte*
byte* bytes = new byte[text.length()]; // Convert the wstring to byte*
for (int i = 0; i < text.length(); i++)
{
bytes[i] = text[i];
}
return bytes;
}
[Helper::safeDelete 是一个函数,只是删除指针并将其设置为空]
解密只是偶尔失败一次
您可能还有其他问题,但这里有一个:
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
您在告诉 CryptoStream 完成之前耗尽了 CryptoStream 的输出。所以你可能丢失了多达 16 个字节。
您需要:
- 在
crypotStream
(原文如此)上调用 FlushFinalBlock()。
- 在 CryptoStream 的使用退出之前不要调用
memStream.ToArray()
。
所以,问题出在函数中将 char* 解析为 wstring 时
这个函数的问题是我解析它的方式:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
我使用了 wstringstream,加密文本有时会包含空终止字符。
所以我没有使用 wstringstream,而是使用了这个:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
char* readBuffer = getPartFromSocket(sc, bytesNum, 0);
return wstring(&readBuffer[0], &readBuffer[bytesNum]);
}
然后它不会在空字符处剪切消息
加密:
public static byte[] EncryptAES(Message msg)
{
byte[] encText; // This will keep the encrypted text
byte[] encLength; // This will keep the length of the encrypted text
byte[] finalEncText = null; // This keeps the encLength + encText (#####[encText] / [encLength][encText])
// Building the plaintext message :
string plainText = msg.MessageCode.ToString();
if (msg.Parameters != null)
foreach (string parameter in msg.Parameters)
plainText += parameter;
// Encrypting the plaintext :
encText = EncryptAES(plainText);
string encLen = encText.Length.ToString();
string fittedEncLen = MessageSender.FitStringIntoSize(encLen, Globals.MESSAGE_LENGTH_LEN); // Fit the length of the encrypted text into a certain size
encLength = Encoding.ASCII.GetBytes(fittedEncLen); // convert the length into byte[]
finalEncText = new byte[encLength.Length + encText.Length];
System.Buffer.BlockCopy(encLength, 0, finalEncText, 0, encLength.Length);
System.Buffer.BlockCopy(encText, 0, finalEncText, encLength.Length, encText.Length); // Copy the byte arrays into the new byte array
return finalEncText;
}
private static byte[] EncryptAES(string text)
{
// This function encrypts a plaintext message using the aes key we have from the server
if (AesKey == null || IV == null) // If we dont have an aes key / iv, dont encrypt
return Encoding.ASCII.GetBytes(text);
byte[] encryptedText;
try
{
Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(AesKey);
aes.IV = Encoding.ASCII.GetBytes(IV);
ICryptoTransform cryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
}
aes.Dispose();
}
catch
{
// In case of an error while encrypting, dont encrypt
encryptedText = Encoding.ASCII.GetBytes(text);
}
return encryptedText;
}
[添加的fittedEncLen基本上是一个固定长度为5个字符的前缀,其中包含其后的加密消息的长度,在解密之前服务器读取这5个字符然后解密加密的部分]
正在发送消息到服务器 [TCPClient] [C#] :
public int Send(Message message)
{
/*
* Encrpyts the message and then sends it to the network stream.
*
* Return code:
* 0 on success.
* -1 on failure.
*/
byte[] msg = Cryptography.EncryptAES(message); // Encrypt the message
// Sending message
try
{
this._networkStream.Write(msg, 0, msg.Length);
this._networkStream.Flush();
}
catch
{
return -1;
}
return 0;
}
正在接收 [C++]:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
char* Helper::getPartFromSocket(SOCKET sc, int bytesNum, int flags)
{
if (bytesNum == 0)
return "";
char* data = new char[bytesNum + 1];
int res = recv(sc, data, bytesNum, flags);
if (res == INVALID_SOCKET)
{
string s = "Error while recieving from socket: ";
s += to_string(sc);
throw exception(s.c_str());
}
data[bytesNum] = 0;
return data;
}
BufferedString* Helper::makeBufferedString(SOCKET sc)
{
/*
The socket contains <length of encrypted message (unencrypted)> <encrypted message>.
This function will read the length of the unencrypted message, read
the encrypted message, decrypt it, store it in a BufferedString
object and return the object.
Length of length number: MESSAGE_LENGTH_LEN.
*/
int sizeOfMessage = Helper::getIntPartFromSocket(sc, MESSAGE_LENGTH_LEN);
if (sizeOfMessage == 0)
return NULL;
wstring wideString = getWideStringPartFromSocket(sc, sizeOfMessage);
string decrypted = "";
if (wideString.length() < sizeOfMessage)
{
std::wstringstream cls;
cls << wideString;
cls << getWideStringPartFromSocket(sc, sizeOfMessage - wideString.length());
wideString = cls.str();
}
SocketEncryptionKeychain* keyChain = SocketEncryptionKeychain::getKeychain(sc);
if (keyChain != nullptr) // If the socket has a keychain, decrypt the message
decrypted = Cryptography::decryptAES(wideString, keyChain->getKey(), keyChain->getIV()); // Try to decrypt the message
else // If the keychain is null, just convert the widestring to a string
decrypted = wideStringToString(wideString);
return new BufferedString(decrypted);
}
SocketEncryptionKeychain 基本上包含每个套接字的 AES 密钥和 IV BufferedString 是一个包含字符串的 class ,您可以像从套接字中读取一样从中读取[它是一个缓冲区,一旦您从中读取,您读取的内容就会被删除] [基本上是一个字符串缓冲区,没什么特别的]
正在解密 [C++]:
string Cryptography::decryptAES(wstring cipherText, byte aesKey[], byte iv[])
{
if (aesKey == nullptr || iv == nullptr) // If the key or iv are null, dont decrypt
return Helper::wideStringToString(cipherText);
string plaintext;
try
{
// Decrypt :
byte* cipher = wideStringToByteArray(cipherText); // Convert the wide string to byte*
CryptoPP::AES::Decryption aesDecryption(aesKey, 32);
CryptoPP::CBC_Mode_ExternalCipher::Decryption ecbDecryption(aesDecryption, iv);
CryptoPP::StreamTransformationFilter stfDecryptor(ecbDecryption, new CryptoPP::StringSink(plaintext), StreamTransformationFilter::ZEROS_PADDING);
stfDecryptor.Put(cipher, cipherText.length());
stfDecryptor.MessageEnd();
Helper::safeDelete(cipher);
}
catch (CryptoPP::InvalidCiphertext& ex)
{
// In case of an error don't decrypt
plaintext = Helper::wideStringToString(cipherText);
}
return plaintext;
}
byte* Cryptography::wideStringToByteArray(wstring text)
{
// This function translates the wstring into a byte*
byte* bytes = new byte[text.length()]; // Convert the wstring to byte*
for (int i = 0; i < text.length(); i++)
{
bytes[i] = text[i];
}
return bytes;
}
[Helper::safeDelete 是一个函数,只是删除指针并将其设置为空]
解密只是偶尔失败一次
您可能还有其他问题,但这里有一个:
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
您在告诉 CryptoStream 完成之前耗尽了 CryptoStream 的输出。所以你可能丢失了多达 16 个字节。
您需要:
- 在
crypotStream
(原文如此)上调用 FlushFinalBlock()。 - 在 CryptoStream 的使用退出之前不要调用
memStream.ToArray()
。
所以,问题出在函数中将 char* 解析为 wstring 时
这个函数的问题是我解析它的方式:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
我使用了 wstringstream,加密文本有时会包含空终止字符。
所以我没有使用 wstringstream,而是使用了这个:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
char* readBuffer = getPartFromSocket(sc, bytesNum, 0);
return wstring(&readBuffer[0], &readBuffer[bytesNum]);
}
然后它不会在空字符处剪切消息