在 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]);
}

然后它不会在空字符处剪切消息