Why are there random characters appearing in my decrypted text?
Asked Answered
O

2

13

Intro

I'm trying to encrypt and decrypt texts and sometimes, especially for larger texts, random characters appear within the decrypted text. I'm using AES cryptography within the System.Security.Cryptography namespace and the text I'm trying to encrypt at the moment would be a URL and some info, such as the page title. I've provided an example below and what I've attempted. I've also written the two encryption and decryption methods, minus any lines that output to the Debug Window. The Key and IV used shouldn't be a problem as, for now, they would be constant.

I think it would be wise for me to point out that it encrypts and decrypts 18/01/2013;18/01/2013, in a separate occurrence, as expected.

Example

Say I wanted to decrypt this text:

Barnabe Googes Information & Homepage | Search and Research on BarnabeGooge.com;18/01/2013;18/01/2013;;http://www.googe.com

By default it uses UTF-8 and it would encrypt to:

뤟౏羜ڮ胂淺弊놛荧ꠃ錺槝ヸ蘜ầᄼꕒヘ⍩㗪潺뱂施㒞ꨛ殳硪픴ی뿨춃�燲ᯁﱪ뙊힓琲鯖嶑⨹갂Ѭ쳀鿜�྄䋖⭫ퟂ㪏�荾ꆺשּ붹梾麦膛

And decrypts back to:

Barnabe Googes Information & Homepage | Search and Research on B���Ax2�!��f�M]18/01/20�;18/01[�;>َ�l?����m��*-��+��^A[=�

What I've attempted

  • I've attempted to change to other Encodings, but UTF-8 seem to affect the decrypted text the least.
  • Changed to different types of padding, but Padding.Zeros seems the best. I also can't use Padding.None because it throws a NotSupportedException: bad data length.
  • Changed the Mode to CBC (Not that it should matter).
  • Flush/Close CryptoStream so it could flush the final block, or something.
  • Just in case the fault rested with the title, I used WebUtility.HtmlDecode() to decode the title, but it didn't affect it.

Encryption Method

The encryption below uses AES Encryption, as you can see. I want to point out that key and IV are two global strings within the same class as both of the Encryption and Decryption methods. The reason I've done this is to mess around with different encodings and CryptographyServiceProviders, just if by chance a random change works. Please ignore these as they are constant and won't affect the final encryption/decryption.

public static byte[] EncryptStringToBytes(string plainText, Encoding Enc)
{

    if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
    byte[] encrypted;
    using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
    {
         tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
         tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray();
         tdsAlg.Padding = PaddingMode.Zeros;
         tdsAlg.Mode = CipherMode.CBC;
         ICryptoTransform encryptor = tdsAlg.CreateEncryptor(tdsAlg.Key, tdsAlg.IV);

         using (MemoryStream msEncrypt = new MemoryStream())
         {
             using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
             {
                 using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                 {
                     swEncrypt.Write(plainText);
                 }
                 encrypted = msEncrypt.ToArray();
                 csEncrypt.Close();
             }
         }
    }

    return encrypted;
}

Decryption Method

public static string DecryptStringFromBytes(byte[] cipherText,Encoding Enc)
{
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");

    string plaintext = null;

    using (AesCryptoServiceProvider tdsAlg = new AesCryptoServiceProvider())
    {
        tdsAlg.Key = (byte[])Enc.GetBytes(key).Take(tdsAlg.Key.Length).ToArray();
        tdsAlg.IV = (byte[])Enc.GetBytes(IV).Take(tdsAlg.IV.Length).ToArray() ;
        tdsAlg.Padding = PaddingMode.Zeros;
        tdsAlg.Mode = CipherMode.CBC;
        ICryptoTransform decryptor = tdsAlg.CreateDecryptor();

        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt,true))
                {
                    plaintext = srDecrypt.ReadToEnd().Replace("\0","");
                    csDecrypt.Close();
                    return plaintext.Replace("\0",string.Empty);
                }
            }
        }
   }    
   return plaintext;    
}

Bootnote

Just in case it matters, I'm using this to get the title of the webpage, but as I've mentioned using HtmlDecode doesn't affect it.

WebClient x = new WebClient();
string source = x.DownloadString(Url);
x.Dispose();
string title= Regex.Match(source, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
title = title.Replace(";", " ");
return title;
Omniscient answered 18/1, 2013 at 18:14 Comment(8)
(too long, didn't read code) :) - based on the fact that you got some of the content decrypted OK most likely some conversion code string->byte->string is wrong.Jeu
have a look at this MSDN site perhaps you are doing something wrong too much code on your question for someone to read AesCryptoServiceProvider ClassElda
You cannot store the byte[] you get out of the encryption into a string or a text file. String normalization will destroy byte values. Use FileStream instead. Use base64 encoding if you really need a string.Tweeddale
I am seeing many subtle and not so subtle problems with this crypto. I strongly advise you to reuse code written by experts, or you will end up with f*cked up security without knowing it. It will look secure but it won't be.Heavenly
Thanks, Hans. I had been using Encoding.Utf8.GetString() or GetBytes(), I never knew about base64.Omniscient
+1 Now it is a question that is worth getting called 'question' on SO.Blanche
Undelete your answer explaining the solution, and accept it, or delete the question altogether. Putting [Solved] in the title isn't the way SO works. Glad you found a solution though!Dramatization
I can't accept it, I have to wait 2 days, so I ended up editing the original post and put a solution at the bottom. But I've now un-deleted the answer and removed the answer in the original post.Omniscient
O
5

Thanks to Hans Passant I found the solution. The problem was that I was using Encoding.GetString() or Encoding.GetBytes() when I was encrypting and decrypting, when I should have been using Convert.ToBase64String() or Convert.FromBase64String().

Omniscient answered 18/1, 2013 at 19:14 Comment(3)
Why do we need base64 to encrypt and decrypt ? Are you reading data from files ? and writing data to files ?Allenaallenby
@shuva I was writing and reading the data to files but wanted to encrypt it first. This was a pet project from a while ago so I cannot remember the specifics.Omniscient
In my case I had a problem that not opening a file in binary mode adds extra characters in the file. Like fopen(filename, "w") causes extra characters. And it failed decryption .Allenaallenby
A
-1

I had the same problem of extra output. For me it was not encoding-problem, because I was passing it as byte array in BCrypt library. As it is plain-text, I would use space-character as padding before encryption and trim after decryption.

int padding = BLOCK_SIZE - (input_len+1)%BLOCK_SIZE;
if(padding && (input_len+padding) <= buf_size)
{
   memset(buf+input_len, ' ', padding);
   input_len += padding;
}

For 128 bit encryption, the block-size is 16. Note that the buf_size should be multiple of the block-size to make it work all the time. As we padded the input already, we do not need the padding algorithm in decryption.

tdsAlg.Padding = PaddingMode.None;

And at the end of decryption, I would trim the output.

Allenaallenby answered 10/1, 2018 at 20:6 Comment(3)
No, the problem is the byte[] he got out of the encryption he was calling a Encoding.Utf8.GetString( on, then on the decrption he was calling Encoding.Utf8.GetBytes(. GetString can't round trip a byte[] that was not originally a string, values for invalid UTF-8 in the input byte array will cause errors in the conversion process. You need to encode to a string using a method that is designed to hold arbitrary byte arrays, which Base64 is one of.Berceuse
So is it not possible to send bytes in UTF-8 format ?Allenaallenby
Not arbitrary binary bytes it is not. That is what Base64 is designed for, to represent arbitrary bytes as text but because it has to work with all bytes and can't break like any of the Encoding based ones it comes at a cost of the string will be 4/3 bigger than the original byte[]Berceuse

© 2022 - 2024 — McMap. All rights reserved.