I have a program which stores encrypted text data. Whenever new data is added, the encrypted data is loaded from file, decrypted, the new string data is appended, and finally the data is re-encrypted and saved to disk. This used to work until recently, now however I get a CryptographicException: Padding is invalid and cannot be removed when decrypting the loaded data. This is the code I use to encrypt/decrypt the data:
public static byte[] Encrypt(string text, byte[] key) {
byte[] encrypted;
byte[] IV;
using (var aes = Aes.Create()) {
aes.Key = key;
aes.GenerateIV();
IV = aes.IV;
aes.Mode = CipherMode.CBC;
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (var swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(text);
}
encrypted = msEncrypt.ToArray();
}
}
// return plaintext IV and encrypted text payload concatenated
return IV.Concat(encrypted).ToArray();
} // Encrypt()
public static string Decrypt(byte[] bytes, byte[] key) {
string plaintext = null;
using (var aes = Aes.Create()) {
aes.Key = key;
// split iv and encryped cyphertext
byte[] IV = new byte[aes.BlockSize / 8];
byte[] cipherText = new byte[bytes.Length - IV.Length];
Array.Copy(bytes, IV, IV.Length);
Array.Copy(bytes, IV.Length, cipherText, 0, cipherText.Length);
aes.IV = IV;
aes.Mode = CipherMode.CBC;
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (var msDecrypt = new MemoryStream(cipherText))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var srDecrypt = new StreamReader(csDecrypt)) {
plaintext = srDecrypt.ReadToEnd();
// !!! EXCEPTION HAPPENS HERE !!!
}
}
return plaintext;
}
The encrypted byte[] array is read/written using plain File.ReadAllBytes / File.WriteAllBytes.
I tried to change the the code to read byte by byte until an exception occurs, which gave me the decrypted text minus 16 bytes. However, only the first third contained valid text, the rest was a few garbled bytes, followed by a (equally garbled) 16 byte sequence, repeated over and over. This makes me suspect that some kind of corruption occurred during a previous save operation.
Any help would be greatly appreciated, regarding to recovering the data (if possible) and pointing out problems/bugs in the encryption/decryption code (if any).
Related
I have a problem when performing a decryption in TRIPLEDES that a provider sends me in HEX: EF69FF79BBD7E8E4EF69FF79BBD7E8E4 with the following key "0123456789ABCDEFFEDCBA9876543210", applying the following method:
public IActionResult GetTokenTemp1()
{
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
tDESalg.Key = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes("0123456789ABCDEFFEDCBA9876543210"));
byte[] cipherBytes = Convert.FromBase64String("EF69FF79BBD7E8E4EF69FF79BBD7E8E4");
string finalDecrypt = _3desTest.DecryptTextFromMemory(cipherBytes, tDESalg.Key, tDESalg.IV);
return Ok(finalDecrypt);
}
public static string DecryptTextFromMemory(byte[] Data, byte[] Key, byte[] IV)
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream msDecrypt = new MemoryStream(Data);
TripleDESCryptoServiceProvider de = new TripleDESCryptoServiceProvider();
var descritor = de.CreateDecryptor(Key, IV);
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
descritor,
CryptoStreamMode.Read);
// Create buffer to hold the decrypted data.
byte[] fromEncrypt = new byte[Data.Length];
// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
string es = new UTF8Encoding().GetString(fromEncrypt);
//Convert the buffer into a string and return it.
return new UTF8Encoding().GetString(fromEncrypt);
}
catch (CryptographicException e)
{
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message);
return null;
}
}
When I leave the default padding or any other to zero or none, I get the following error "adding is invalid and cannot be removed.",
but when I leave the padding at zero or none tripleDescryptorService.Padding = PaddingMode.None I get a format:
padding.none
I don't know what to do very well, when I do it on this page:
https://neapay.com/online-tools/des-calculator.html?data=EF69FF79BBD7E8E4EF69FF79BBD7E8E4&key=0123456789ABCDEFFEDCBA9876543210&algo=3DES&decr=true
I get the desired result.
I'm already desperate, I'm not very expert in encryption.
Thank you so much
The website uses neither a padding nor an IV. Therefore in the code the padding must be disabled and the ECB mode must be applied.
Furthermore the website expects a hex encoded key and ciphertext and returns the decrypted data also hex encoded, which therefore must not be UTF-8 decoded in the code:
public static byte[] DecryptTextFromMemory(byte[] encryptedData, byte[] key)
{
using (TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider())
{
tripleDES.Key = key;
tripleDES.Padding = PaddingMode.None;
tripleDES.Mode = CipherMode.ECB;
byte[] decryptedData = new byte[encryptedData.Length];
using (MemoryStream msDecrypt = new MemoryStream(encryptedData))
{
ICryptoTransform decryptor = tripleDES.CreateDecryptor(tripleDES.Key, null);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
csDecrypt.Read(decryptedData, 0, decryptedData.Length);
}
}
return decryptedData;
}
}
For the hex encoding and decoding you can use arbitrary methods, e.g. from here.
With this the code:
byte[] data = HexStringToByteArray("EF69FF79BBD7E8E4EF69FF79BBD7E8E4");
byte[] key = HexStringToByteArray("0123456789ABCDEFFEDCBA9876543210");
Console.WriteLine(ByteArrayToHexString(DecryptTextFromMemory(data, key)));
returns the result of the website:
00000000003331720000000000333172
Please note: Your last change is not useful because it applies conversions and algorithms that are not consistent with the website.
I am trying to write code to decrypt a string.
I was given an equivalent in python and I am trying to create the same in . NET
Python:
//Initialization vector is just a string of 16 null bytes
iv = '\x00' * 16
//Create new AES object using the key and init vector
aes = AES.new(key, AES.MODE_CBC, iv)
//Decrypt password and remove padding
result = aes.decrypt(myString).rstrip('\x0b\x08\x07')
return result
Here is my attempt:
byte[] iv = new byte[16];
byte[] rawPlaintext = Convert.FromBase64String("MyBase64String");
byte[] key = // Read from common source
using (Aes aes = new AesManaged())
{
aes.Padding = PaddingMode.None;
aes.KeySize = 128; // in bits
aes.Key = new byte[128 / 8]; // 16 bytes for 128 bit encryption
aes.IV = new byte[128 / 8]; // AES needs a 16-byte IV
// Should set Key and IV here. Good approach: derive them from
// a password via Cryptography.Rfc2898DeriveBytes
byte[] cipherText = key;
byte[] plainText = iv;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(rawPlaintext, 0, rawPlaintext.Length);
}
cipherText = ms.ToArray();
}
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
}
plainText = ms.ToArray();
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);
}
It doesn't appear to be working for the result is a string of symbols.
Possible issues:
- I see a mode of CBC getting set. I'm not sure where that equivalent setting would be. I've tried to play with the PaddingMode.
- Could my iv byte[] be causing the issue? Is the default null or 0?
EDIT:
- From what I am reading AesManaged uses AES in CBC mode so that should be a non-issue.
Try replacing this:
string s = System.Text.Encoding.Unicode.GetString(plainText);
to:
string s = System.Text.Encoding.UTF8.GetString(plainText);
I use ECDiffieHellmanCng for exchange of public keys and then AES for encrypting/decrypting.
Sometimes the decryption works, other times I get the following error in decryption method: Padding is invalid and cannot be removed.
Where is the cause of this?
Here is the code:
private void Encryption(byte[] key, byte[] unencryptedMessage,out byte[] encryptedMessage, out byte[] iv) // encryption funkcija
{
using (Aes aes = new AesManaged())
{
aes.Key = key;
//aes.GenerateIV();
iv = aes.IV;
aes.Padding = PaddingMode.PKCS7;
// Encrypt the message
using (MemoryStream ciphertext = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ciphertext, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(unencryptedMessage, 0, unencryptedMessage.Length);
cs.Close();
}
encryptedMessage = ciphertext.ToArray();
}
}
}
private void Decryption(byte[] encryptedMessage, byte[] iv, out byte[] decryptedMessage)
{
using (Aes aes = new AesManaged())
{
aes.Key = receiversKey;
aes.IV = iv;
aes.Padding = PaddingMode.PKCS7;
// Decrypt the message
using (MemoryStream decryptedBytes = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(decryptedBytes, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(encryptedMessage, 0, encryptedMessage.Length);
cs.Close();
}
decryptedMessage = decryptedBytes.ToArray();
}
}
}
Since you're already using the same padding mode for both encryption and decryption, the most likely causes of invalid padding error are:
Different keys that are used for encryption and decryption.
Invalid encrypted message passed to Decryption() method. You could mistakenly pass an empty string or non-encrypted data.
If this does not help, please provide the code that calls Encryption() and Decryption() methods and handles the key used in both cases. It's required because those methods itself looks ok, the most likely problem is in passed arguments.
I've been trying to understand encryption/decryption code of TripleDES for some days. And I have seen many codes in the google, and the code shown below is one of them.
static void Main(string[] args)
{
string original = "Here is some data to encrypt!";
TripleDESCryptoServiceProvider myTripleDES = new TripleDESCryptoServiceProvider();
byte[] encrypted = EncryptStringToBytes(original, myTripleDES.Key, myTripleDES.IV);
string encrypt = Convert.ToBase64String(encrypted);
string roundtrip = DecryptStringFromBytes(encrypted, myTripleDES.Key, myTripleDES.IV);
Console.WriteLine("encryted: {0}", encrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (TripleDESCryptoServiceProvider tdsAlg = new TripleDESCryptoServiceProvider())
{
tdsAlg.Key = Key;
tdsAlg.IV = IV;
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();
}
}
}
return encrypted;
}
static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (TripleDESCryptoServiceProvider tdsAlg = new TripleDESCryptoServiceProvider())
{
tdsAlg.Key = Key;
tdsAlg.IV = IV;
ICryptoTransform decryptor = tdsAlg.CreateDecryptor(tdsAlg.Key, tdsAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
There is no error in the code. I works fine. But strangely I noticed that the plainText is never been encoded. There is no line like Encoding.Unicode.GetBytes(plainText); or Encoding.UTF8.GetBytes(plainText); or similar like that. So, my question is , how does (in the code) the plainText which is a string gets converted to the encrypted byte? Is there any work done inside the streams? If thats so then where and how? As far as I understood there is no such line in between the streams that converts the string to byte. So , How does the overall code is working without this basic transformation?
Update:
Is this code really a valid code?
You are sending the plaintext to the encryption stream in the line swEncrypt.Write(plaintext). This does the byte conversion.
The StreamWriter is doing the encoding. The constructor being used specifies UTF-8 encoding:
This constructor creates a StreamWriter with UTF-8 encoding without a
Byte-Order Mark (BOM)
I have been doing some research into using AESCryptoServiceProvider in C#. So far, I have an implementation which seems to be working. However, my understanding of initialization vectors is that it should protect my cipher text from being the same when the payload and key are the same, but it appears I am doing something wrong. Changing my initialization vector doesn't seem to affect the results of these functions at all.
Here are my functions:
public string EncryptString(string toEncrypt, byte[] encryptionKey, byte[] iv)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
//aes.GenerateIV();
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(aes.IV, 0, aes.IV.Length);
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
public string DecryptString(string toDecrypt, byte[] encryptionKey, byte[] iv)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var toDecryptBytes = Convert.FromBase64String(toDecrypt);
Array.Copy(toDecryptBytes, 0, iv, 0, iv.Length);
aes.IV = iv;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binWriter = new BinaryWriter(cs))
{
binWriter.Write(toDecryptBytes, iv.Length, toDecryptBytes.Length - iv.Length);
}
return Encoding.Default.GetString(ms.ToArray());
}
}
}
New version of my encrypt function I have now following the advice from #owlstead, changing back CBC mode. This does appear to be working correctly now using CBC.
public string EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = encryptionKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
You are putting the CryptoStream and the BinaryWriter in the wrong order for at least the encrypt function. You are currently encrypting your IV, while the IV should be put in front of the ciphertext, in plain.
The IV is XORed with the plain text before encryption. Now, this means that you zero out the plain text: you are encrypting a zero valued block.
CipherMode.ECB mode seems not picking up the IV argument(which means no matter what IV you passed in, the result will be the same),this is possible because ECB doesn't need a concept as Initial Vector,for there's no subsequent vectors either. If you want to mask your ECB block, XOR your block mannually before encryption is OK.
While you changed to CipherMode.CBC, the IV does matters,for subsequent blocks will be XORed with previous encrypted block ,as forms a chain.