Why are there random characters appearing in my decrypted text? - c#

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;

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().

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.

Related

How to encrypt with iOS CryptoKit and decrypt with C# in NetCore

I would like to encrypt data in iOS app with a SymetricKey and the CryptoKit and decrypt on server side with C# in Net Core.
iOS code:
class Security {
static let keyStr = "d5a423f64b607ea7c65b311d855dc48f" //32
static let iv="31348c0987c7" //12
class func encode(_ text:String)->String {
let key=SymmetricKey(data: Security.keyStr.data(using: .utf8)!)
let nonce=try! AES.GCM.Nonce(data: iv.data(using: .utf8)!)
let encrypted=try! AES.GCM.seal(text.data(using: .utf8)!, using: key, nonce: nonce)
return encrypted.combined!.base64EncodedString()
}
}
I pass the result of the encryption to my backend and I would like to decrypt
C# Code:
public string decrypt(string encryptedText)
{
string keyStr = "d5a423f64b607ea7c65b311d855dc48f";
string iv = "31348c0987c7";
string plaintext = "";
Debug.WriteLine(encryptedText);
using (Aes aesAlg = Aes.Create())
{
Debug.WriteLine(AesGcm.IsSupported);
var key = System.Text.Encoding.UTF8.GetBytes(keyStr);
var iV = System.Text.Encoding.UTF8.GetBytes(iv);
aesAlg.Key = key;
aesAlg.IV = iV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(request.pswd)))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
Debug.WriteLine(plaintext);
}
So for example word: Test gets encrypted as: MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=
When I get to line:
aesAlg.IV = iV;
I get an error "Specified initialization vector (IV) does not match the block size for this algorithm."
It seems as if C# needs byte[16], but in iOS I seem to be stuck with 12.
I got stuck at this point. Any idea greately appreciated.
Thank you.
The posted Swift code applies AES in GCM mode, s. AES.GCM. The posted C# code also uses AES, however not the GCM mode, but the default CBC mode (s. Aes, Mode).
The CBC mode applies a 16 bytes IV, while the GCM mode uses a 12 bytes nonce. That is what the error message is pointing to.
For successful decryption, AES in GCM mode must also be used on the C# side. In .NET AES in GCM mode is supported with the AesGcm class (as of .NET Core 3.0).
Note also that the data given by the Swift code is the Base64 encoding of the concatenation of 12 bytes nonce, ciphertext and 16 bytes tag (in that order), which must be separated in the C# code, where the portions are processed individually.
A possible C# implementation that decrypts the ciphertext generated by the posted Swift code is:
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
Span<byte> nonce = nonceCiphertextTagSpan[..12];
Span<byte> ciphertext = nonceCiphertextTagSpan[12..^16];
Span<byte> tag = nonceCiphertextTagSpan[^16..];
byte[] plaintext = new byte[ciphertext.Length];
using AesGcm aesGcm = new AesGcm(key);
aesGcm.Decrypt(nonce, ciphertext, tag, plaintext); // throws an 'CryptographicException: The computed authentication tag did not match the input authentication tag' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Edit: An alternative to the native .NET class AesGcm is C#/BouncyCastle. Maybe this is supported in your environment:
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
...
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");
Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
byte[] nonce = nonceCiphertextTagSpan[..12].ToArray();
byte[] ciphertextTag = nonceCiphertextTagSpan[12..].ToArray();
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key), 128, nonce);
gcmBlockCipher.Init(false, aeadParameters);
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(ciphertextTag.Length)];
int length = gcmBlockCipher.ProcessBytes(ciphertextTag, 0, ciphertextTag.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, length); // throws an 'InvalidCipherTextException: mac check in GCM failed' if authentication fails
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test
Note that unlike the native AesGcm class, C#/BouncyCastle requires the concatenation of ciphertext and tag, so only the nonce needs to be separated.

Converting code from PasswordDerivedBytes to Rfc2898DerivedBytes, unicode

I'm attempting to replace PasswordDerivedBytes with Rfc2898DerivedBytes but I'm having a problem with the latter when getting back a unicode encoded result.
Take this code for example:
[TestMethod]
public void DerivedBytesTest()
{
string encrypted = "y4Ijqo9Ga/mHlFbLHDdDUkYZlyu7CHF4PVXGLnb8by7FAVtCgPLhFSiA9Et6hDac";
string key = "{00B3403A-3C29-4f26-A9CC-14C411EA8547}";
string salt = "gT5M07XB9hHl3l1s";
string expected = "4552065703414505";
string decrypted;
decrypted = Decrypt(encrypted, key, salt, true);
Assert.IsTrue(decrypted == expected); // Works
decrypted = Decrypt(encrypted, key, salt, false);
Assert.IsTrue(decrypted == expected); // Doesn't work, get wrong unicode characters in 24 character string
}
private string Decrypt(string encrypted, string key, string salt, bool legacy = false)
{
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] encryptedDataBytes = Convert.FromBase64String(encrypted);
byte[] saltBytes = encoding.GetBytes(salt);
RijndaelManaged encryption = new RijndaelManaged();
DeriveBytes secretKey;
if (legacy)
{
secretKey = new PasswordDeriveBytes(key, saltBytes) {IterationCount = 100};
encryption.Padding = PaddingMode.PKCS7;
}
else
{
secretKey = new Rfc2898DeriveBytes(key, saltBytes, 100);
encryption.Padding = PaddingMode.Zeros; // This is the only one that doesn't throw the "Padding is invalid and cannot be removed" exception, but gives me a non-ASCII result
}
ICryptoTransform decryptor = encryption.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
string decryptedText = "";
using (MemoryStream memoryStream = new MemoryStream(encryptedDataBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] bytes = new byte[encryptedDataBytes.Length];
int decryptedCount = cryptoStream.Read(bytes, 0, bytes.Length);
decryptedText = encoding.GetString(bytes, 0, decryptedCount);
if (!legacy)
{
// Something more to do with result?
}
}
}
return decryptedText;
}
I wonder if anyone can advise where I'm going wrong?
PasswordDeriveBytes is a badly implemented extension of PBKDF1, while Rfc2898DeriveBytes is the implementation of PBKDF2. Both derive a key from a password, but they are two different algorithms and therefore they derive two different results. As they are using cryptographically secure hashes underneath, there is no way to convert one to another.
If you can spare a few bytes of storage you could still derive the key using PKBDF1 and then encrypt that key using the result of PBKDF2. If the output size is identical you could even use XOR encryption for that (a one-time-pad) but AES would of course also work. So then the decryption becomes: calculate PBKDF2 result, decrypt data key, use data key to decrypt ciphertext.
Otherwise you will have to decrypt and then re-encrypt the result.
If you want to compare the decryption result then compare the resulting bytes; do not first convert it into a string. Using authenticated encryption or a MAC is highly advised so that a authentication tag can be validated instead. Just ignoring padding exceptions by using Zero Padding is not the way to go. These padding errors occur because the key is wrong.
Generic notes:
PasswordDeriveBytes should not be used for any amount of bytes > 20 bytes as the Mickeysoft extension of PBKDF1 is horribly insecure, even repeating bytes in the output (!). If you do the same for PBKDF2 then any adversary will have to do half the work that you have to do so that's not a good idea either.
The iteration count in the question is very low, but as you seem to use a highly random UID instead of a password that should be OK.

Creating Encryption Using string

Hi I'm just trying to encrypt a string but i want to reverse the decryption method to create exactly encrypted key
decryption was
public string newSample(string s)
{
byte[] buffer = Convert.FromBase64String(s);
Encoding utF8 = Encoding.UTF8;
byte[] bytes1 = utF8.GetBytes("key1");
byte[] bytes2 = utF8.GetBytes("key2");
RijndaelManaged rijndaelManaged1 = new RijndaelManaged();
rijndaelManaged1.Mode = CipherMode.CBC;
rijndaelManaged1.Padding = PaddingMode.Zeros;
rijndaelManaged1.BlockSize = 128;
rijndaelManaged1.KeySize = 128;
RijndaelManaged rijndaelManaged2 = rijndaelManaged1;
ICryptoTransform transform = (ICryptoTransform)null;
transform = rijndaelManaged2.CreateDecryptor(bytes2, bytes1);
byte[] bytes3 = (byte[])null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(buffer, 0, buffer.GetLength(0));
cryptoStream.FlushFinalBlock();
}
rijndaelManaged2.Clear();
bytes3 = memoryStream.ToArray();
}
return new string(Encoding.UTF8.GetChars(bytes3));
}
is it possible to reverse the code and create encryption key ? if so
how could be the encryption should look lik for this decryption method ??
thanks
This is the problem - or at least the initial problem:
return new string(Encoding.UTF8.GetChars(bytes3));
The result of encryption is not a UTF-8-encoded byte array... it's arbitrary bytes. By assuming it's valid UTF-8-encoded text, you're losing information.
Instead, you should use a hex or base64 approach, both of which are designed to convert arbitrary binary data to text in a lossless fashion. For example:
return Convert.ToBase64String(bytes3);
Now, your decryption code should start with:
byte[] encryptedData = Convert.FromBase64String(base64EncryptedText);
(Where base64EncryptedText is the value returned from your encryption method.)
From there, it should be a matter of just reversing each step, and there are numerous examples around. You may well find that you've got a problem due to the padding mode, however - you may need to separately record the length of the original data.
As an aside, it's not clear why your method takes a string in the first place. It's odd for an encryption method to take a base64-encoded piece of data. It's more common for it to take either a normal plain text string which is converted into bytes using something like Encoding.UTF8, or for it to take a byte[] to start with.

XmlSerializer fails to deserialize XML containing encrypted string

I am serializing an object to an XML string using the .net XML serializer. That object contains a property of type string, whose content is an encrypyted string. The encryption is done using the Rijndael algorithm also provided by the .net, and the call looks like this:
var encryptedArr = EncryptStringToBytes(plainText, RijndaelKey, RijndaelIv);
return Encoding.Default.GetString(encryptedArr);
Although serialization goes fine, the problem is when trying to deserialize. the serializer throws an exception saying
"There is an error in XML document (1,1130). ' ', hexadecimal value
0x02, is an invalid character. Line..."
The thing is that these characters are to my understanding results of the encryption process so I guess messing with the encrypted string to make it XML-compatible is not an option. I also tried encoding the output string in the above piece of code differently:
UTF-8, Base64(which throws an exception saying the string is base64-incompatible) etc.
I've been looking into it for quite some time now. What do you recommend?
Have you taken a look at the example at the bottom of the RijndaelManaged class on MSDN?
Just wondering as they have a method, with the same name as the code you posted. If you are or arent encrypting via similar means, you could try returning a string instead of a byte array, from your method, by calling MemoryStream.GetString() and returning that value:
static string EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
//...
string cipherText = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
cipherText = msEncrypt.ToString();
}
}
}
// Return the encrypted bytes from the memory stream.
return cipherText;
}
What happens if your plainText goes though that? Maybe more information is needed about the plaintext. Might be the case of: Old Post

Decrypting PKCS#5 Padded AES/ECB With Unknown IV

So I'm making a program which retrieves an image from a server which is encrypted in AES/ECB and padded using PKCS#5. I know the single synchronous key used to encrypt the image (M02cnQ51Ji97vwT4), however, in the code that I am using to decrypt it, it requires me to input a IV, which I don't know the value of.
Here is the code I am using to decrypt it:
public static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
Here is the current code I am calling to decrypt the image, and then right it to my desktop:
Byte[] lnByte = Encoding.UTF8.GetBytes(General.DecryptStringFromBytes(reader.ReadBytes(1 * 1024 * 1024 * 10), Encoding.UTF8.GetBytes("M02cnQ51Ji97vwT4"), Encoding.UTF8.GetBytes("\0")));
using (FileStream lxFS = new FileStream("C:\\Users\\Admin\\Desktop\\image.jpg", FileMode.Create))
{
lxFS.Write(lnByte, 0, lnByte.Length);
}
This code runs without any errors, however when I go to open the image that it saved, it says it is corrupt or damaged.
The reason right now that the IV is set to "\0" is because that is what I found online, however it isn't working.
Any help would be appreciated as to what I must set the IV to. Thanks.
ECB mode does not require an IV. But if I'm not mistaken, RijndaelManaged defaults to CBC. So you are using a different mode for the decryption than you are using for the encryption. It's best to not use default values for things like key size, mode of encryption or padding mode.
Try again after explicitly setting the mode of encryption to ECB and the padding mode to PKCS#7 padding. You should not have to provide an IV for ECB.
If you do have to provide it for an implementation, then provide an IV of all zero's. In CBC mode the IV is XORed with the first block of plaintext, so it is easy to see that an IV of all zeros does not do much.
If you use CBC with a zero IV instead of ECB then the first 16 bytes (one block) will be correct. All the blocks after that will be random. Most of the time you would then receive a padding error at the end, but you may be "lucky" (about once in 256) and get a correct padding at the end.
Moreover, you convert the image to character encoding (a string) and back. This will result in data loss most of the time. Instead, you should treat the image as binary.
public static void DecryptStringFromBytes(byte[] cipherText, byte[] Key, Stream stream)
{
// ...
// Don't use StreamReader
csDecrypt.CopyTo(stream)
// ...
}
Now give the FileStream you generated to the method as last parameter.
ECB mode does not need an IV. An IV is required for CBC mode and a Nonce for CTR mode. An all zero IV is equivalent to no IV. In some cases the IV is prepended to the cyphertext, so you could try using the first block of input as an IV for the rest.
As an aside, ECB mode is not secure. There is a good illustration of why in Wikipedia: ECB Mode

Categories