7

I'm trying to match encryption schemes on c++ using crypto++ and c# and can't seem to get the same results on both. They both work on them selves, but not from one to the other. Any help would be great!

C++ code using Crypto++:

std::string key = "01286567891233460123456789123456";
std::string iv = "0123456789123456";

std::string encrypt(const std::string& str_in)
{
    std::string str_out;

    CryptoPP::AES::Encryption aesEncryption((byte*)key.c_str(), CryptoPP::AES::MAX_KEYLENGTH);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, (byte*)iv.c_str());

    StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(str_out));
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(str_in.c_str()), str_in.length() + 1);
    stfEncryptor.MessageEnd();

    return str_out;
}
std::string decrypt(const std::string& cipher_text)
{
    std::string str_out;
    CryptoPP::AES::Decryption aesDecryption((byte*)key.c_str(), CryptoPP::AES::MAX_KEYLENGTH);
    CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, (byte*)iv.c_str());

    CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(str_out));
    stfDecryptor.Put(reinterpret_cast<const unsigned char*>(cipher_text.c_str()), cipher_text.size());
    stfDecryptor.MessageEnd();

    return str_out;
}

The Code Ran:

 std::string str = encrypt("123456789012345");
 str = decrypt(str);

This output is:

 Encrypted: Ö&qcƒ“¹yLY»Lïv¹w“¼LLŠÀ¶ó¢,óð9·
 Length: 32
 Decrypted: 123456789012345
 Length: 16

Now in C#, I have the following code:

 public string Encrypt(string clearText)
 {
       byte[] clearBytes = Encoding.Default.GetBytes(clearText);

       using (Aes encryptor = Aes.Create("AES"))
       {
           // encryptor.BlockSize = 128;
            encryptor.Padding = PaddingMode.Zeros;
            encryptor.KeySize = 128;
            encryptor.Mode = CipherMode.CBC;
            encryptor.Key = Encoding.Default.GetBytes("01234567891234560123456789123456");
            encryptor.IV = Encoding.Default.GetBytes("0123456789123456");
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                byte[] bt = ms.ToArray();
                clearText = Encoding.Default.GetString(bt);// Convert.ToBase64String(bt);
            }
        }
        return clearText; //Return the encrypted command
  }

 public string Decrypt(string cipherText)
 {
       byte[] clearBytes = Encoding.Default.GetBytes(cipherText);

        using (Aes decryptor = Aes.Create("AES"))
        {
              // decryptor.BlockSize = 128;
                decryptor.Padding = PaddingMode.Zeros;
                decryptor.KeySize = 128;
                decryptor.Mode = CipherMode.CBC;
                decryptor.Key = Encoding.Default.GetBytes("01286567891233460123456789123456");
                decryptor.IV = Encoding.Default.GetBytes("0123456789123456");
                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, decryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(clearBytes, 0, clearBytes.Length);
                        cs.Close();
                    }
                    byte[] bt = ms.ToArray();
                    cipherText = Encoding.Default.GetString(bt);// Convert.ToBase64String(bt);
                }
         }

         return cipherText; //Return the decrypted text
     }
}

The code ran:

string orig = "123456789012345";
string cipher = Encrypt(orig);
string dedata = Decrypt(cipher);

The results are:

Orig: 123456789012345
Encrypted: êßyoº0¦ëì›X˜Ü
Length: 16
Decrypted: 123456789012345 
Length: 16

As you can see, the encrypted strings end up being different. So when I take an encrypted string in c++, it can't decrypt it in c#, as shown here:

bytes[] encryptedText = ENCRYPTED TEXT FROM C++
text = System.Text.Encoding.Default.GetString(encryptedText);
text = Decrypt(text);

The c++ returns 32 bytes for it's encrypted string which I believe is the padding being added. Not sure how to replicate this in the c# code or vise versa to match things up. Not sure if there is something else I'm missing here... Thanks for any help!

EDIT:

I've matched the keys and now the string matches on both ends except for the padding difference. When I try to decrypt the string in C# it tells me the input data is not the correct block size? Any help with this now?

EDIT AGAIN:

It seems to be generating the same byte string for each c# and c++ encryption function, so that problem is resolved. The problem I now seem to have is on the c# side, I receive the encrypted string and convert the bytes using: text = System.Text.Encoding.Default.GetString(recvBuf); recvBuf being the encrypted string from c++ and it's missing the last character of the string. It matches up with the c++ string minus the last char?? Not sure why this is happening.

For example, my c++ sends over this encrypted string: Ö&qcƒ“¹yLY»Lïv and my c# program will only receive this: Ö&qcƒ“¹yLY»Lï which in turn will make it fail at decryptng. The encrypted string is being sent over a TCP SOCKET if that makes any difference.

EDIT

Still missing bytes after changing encoding and decoding to base64 on both ends.

C++ 1iZxY4OTHrl5TFm7Gkzvdrl3k7xMTIrAtvOiLPPwObc=
C# 1iZxY4OTHrl5TFm7Gkzvdg==
C# RECEIVED 1iZxY4OTHrl5TFm

New Code C#:

    public string Encrypt(string clearText)
    {
        byte[] clearBytes = Encoding.Default.GetBytes(clearText);

        using (Aes encryptor = Aes.Create("AES"))
        {
           // encryptor.BlockSize = 128;
            encryptor.Padding = PaddingMode.Zeros;
            encryptor.KeySize = 128;
            encryptor.Mode = CipherMode.CBC;
            encryptor.Key = Encoding.Default.GetBytes("01286567891233460123456789123456");
            encryptor.IV = Encoding.Default.GetBytes("0123456789123456");
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                byte[] bt = ms.ToArray();
                clearText =  Convert.ToBase64String(bt);
            }
        }
        return clearText; //Return the encrypted command
    }

And C++ Code:

std::string encrypt(const std::string& str_in)
{
    std::string str_out;
    std::string str_out2;

    CryptoPP::AES::Encryption aesEncryption((byte*)key.c_str(), CryptoPP::AES::MAX_KEYLENGTH);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, (byte*)iv.c_str());

    StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(str_out));
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(str_in.c_str()), str_in.length() + 1);
    stfEncryptor.MessageEnd();

    str_out2 = base64_encode(reinterpret_cast<const unsigned char*>(str_out.c_str()), strlen(str_out.c_str()));

    return str_out2;
}

EDIT

IT WORKS!!! It was simply an overlook on my part, my socket was checking the size of the data before encrypted and sending that size instead of the encrypted string size. Fix that and all it working perfect. Thanks Brandon for the help!

HOC13
  • 71
  • 3
  • your C++ code is using `01286567891233460123456789123456` key which is different than your C# key of `01234567891234560123456789123456`. Have you tried your code samples with the same key? – Brandon Jan 22 '19 at 20:22
  • Don't know how I ended up doing that. Thanks for pointing that out. They do match now, the problem is now when I try to decrypt the c++ string in c#, it says Error: The Input data is not a complete block size? – HOC13 Jan 22 '19 at 20:41
  • 1
    check your character encoding of the strings in C++ and C#. Probably one is using ascii and the other utf8: https://stackoverflow.com/questions/19614178/aes-encryption-error-the-input-data-is-not-a-complete-block – Brandon Jan 22 '19 at 20:46
  • `Encoding.Default` - https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.default?view=netframework-4.7.2 – Daniel A. White Jan 22 '19 at 20:46
  • I've tried using Encoding.ASCII, Encoding.UTF8 but they didn't seem to work. Not sure how to change the encoding on the c++ side of things if that's where I need to change it. – HOC13 Jan 22 '19 at 20:48
  • 1
    examine the bytes arrays you pass to the encryptor and the bytes arrays you get back from the encryptor in the C++ and C# version. Examine the bytes as integers. Prove that you are passing the same bytes into the 2 encryptors and then getting the same bytes out of them. That will point you to your problem. In short: you are converting strings to bytes and later bytes to strings. Those conversions are probably different in your c# and C++ versions and thus your problem is not the encrypt/decrypt itself, but a difference in string encoding handling. – Brandon Jan 22 '19 at 20:59
  • It seems to be generating the same byte string for each c# and c++ encryption function, so that problem is resolved. The problem I now seem to have is on the c# side, I receive the encrypted string and convert the bytes using: text = System.Text.Encoding.Default.GetString(recvBuf); recvBuf being the encrypted string from c++ and it's missing the last character of the string. It matches up with the c++ string minus the last char?? Not sure why this is happening. – HOC13 Jan 22 '19 at 21:22
  • For example, my c++ sends over this encrypted string: Ö&qcƒ“¹yLY»Lïv and my c# program will only receive this: Ö&qcƒ“¹yLY»Lï which in turn will make it fail at decryptng. The encrypted string is being sent over a TCP SOCKET if that makes any difference. – HOC13 Jan 22 '19 at 21:25
  • when converting the encrypted bytes to string, run the bytes through a converter guaranteed to produce ASCII characters. For example, instead of just converting the bytes to string using ASCII or UTF8 or default or whatever, try base64 encoding the bytes. Then, when your program gets a string to decrypt you base64 decode the string to get the actual encrypted bytes. – Brandon Jan 22 '19 at 21:29
  • Are you meaning something like this on the c# when receiving the encrypted string?text = Convert.ToBase64String(encryptedText); text = Decrypt(text); If so, I tried and that also doesn't work. Maybe you can elaborate on what you mean? – HOC13 Jan 22 '19 at 21:46

1 Answers1

0

What I am suggesting is your running afoul of text encoding both when converting the clear text to bytes for encrypting and also when converting from bytes to display string for "transmission".

Try this:

C# code

public string Encrypt(string clearText, Encoding encoding)
{
  // use supplied encoding to convert clear text to bytes
  byte[] clearBytes = encoding.GetBytes(clearText);

  byte[] bt = // insert your encrypt code here...
  // bt bytes are *not* ascii or utf8 or anything else.  If you just
  // use an encoding to convert to text, you won't have good results
  // lets use base64 encoding to get a nice display string representation
  // of the bytes
  return Convert.ToBase64String(bt);
}

public string Decrypt(string base64EncryptedString, Encoding encoding)
{
  // decode the base64
  byte[] bt = Convert.FromBase64String(base64EncryptedString);

  byte[] decrypted = // insert your decrypt code here

  // now we have the original bytes.  Convert back to string using the same
  // encoding we used when encrypting
  return encoding.GetString(decrypted);
}


// Usage:
var clearText = "Hello World";
var asciiEncrypted = Encrypt(clearText, Encoding.ASCII);
var decrypted = Decrypt(clearText, Encoding.ASCII); // MUST USE SAME ENCODING

var utf8Encrypted = Encrypt(clearText, Encoding.UTF8);
var utf8Decrypted = Decrypt(clearText, Encoding.UTF8);

You need to make the same base64 changes in your C++ code. I'm less familiar with the encoding of your C++ strings. I think that if you are using C++ string literals hard coded in the code, then they will be UTF8. This means that your C++ and C# code should agree once you make the C# changes, make the base64 changes in your C++ code, and pass UTF8 to your C# Encrypt/Decrypt methods.

Brandon
  • 38,310
  • 8
  • 82
  • 87
  • thank you for your help. I understand now and also unfamiliar with encoding in c++. I'm looking into that now and I think I should be good once I get that going. – HOC13 Jan 22 '19 at 22:39
  • So I've changed the encryption on both sides to base64 the encrypted string and decrypt the base64 on both sides. That seems to be working, but I still seem to have the same issue I had before. I'm missing some bytes when the data is sent to the C# app. Here are my results now: C++ 1iZxY4OTHrl5TFm7Gkzvdrl3k7xMTIrAtvOiLPPwObc= C# 1iZxY4OTHrl5TFm7Gkzvdg== C# RECEIVED: 1iZxY4OTHrl5TFm So it looks like I'm missing the last 3 bytes now instead of just the 1?? – HOC13 Jan 23 '19 at 05:57
  • glad you got it working. Considering you had many things in play (networking, text encoding, encryption) it is often easier to isolate the parts and test them individually. Makes it much easier to figure out which part is the problem. – Brandon Jan 23 '19 at 13:09