I inherited the code below. Unfortunately, the decrypted value of hello_world is not:
hello world
but (in my case):
&�|ktR���ڼ��S����%��< ���8�
Any ideas? It also appears that the result is different every time, which is kind of obvious given the code. Could I change this so that I can send the data encryted once and then decrypt in the future again? Thanks!
Code:
using System;
using System.IO;
using System.Security.Cryptography;
namespace crypt
{
class Program
{
static void Main(string[] args)
{
var key = #"abcdefghijklmnopqrstuvw==";
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = Convert.FromBase64String(key);
aesAlg.GenerateIV();
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
var enc_hello_world = EncryptProperty(encryptor, "hello world");
var hello_world = DecryptProperty(encryptor, enc_hello_world);
}
}
private static string EncryptProperty(ICryptoTransform encryptor, string valueToEncrypt)
{
byte[] encrypted;
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(valueToEncrypt);
}
encrypted = msEncrypt.ToArray();
}
}
return Convert.ToBase64String(encrypted);
}
private static string DecryptProperty(ICryptoTransform decryptor, string valueToDecrypt)
{
string decrypted;
using (var msDecrypt = new MemoryStream(Convert.FromBase64String(valueToDecrypt)))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
return decrypted;
}
}
}
AES-CBC requires two variables to encode and decode data: key and IV (initialization vector). Initialization vector can be sent in plaintext and it doesn't result in worse security of your encryption.
aesAlg.GenerateIV();
This is where your IV gets created, you need to store this (I do this by prepending that to the resulting data) and then access it and set the IV when decrypting.
You could also use an empty IV but this will make it easier for attackers to expose your key, so this is not recommended.
Here is a good example of AES in C#: https://gist.github.com/mark-adams/87aa34da3a5ed48ed0c7 (it seems to be using the method I've mentioned).
Related
I have written a SecurityExtensions static class to deal with encryption using AES CBC 128 Bit
public static class SecurityExtensions
{
private static Aes GetAes(string keyText, byte[] iv)
{
var key = keyText.ToByteArray();
using var result = Aes.Create();
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.Zeros;
result.KeySize = 128;
if (iv.Length > 0)
{
result.IV = iv;
}
result.Key = key;
return result;
}
public static AesEncryptionInfo EncryptWithAes(this string plainText, string keyText)
{
var aesAlg = GetAes(keyText, new byte[0]);
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
byte[] encrypted;
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
var result = new AesEncryptionInfo(encrypted, aesAlg.IV);
return result;
}
public static string DecryptFromAes(this byte[] cipherText, string keyText, byte[] iv)
{
var aesAlg = GetAes(keyText, iv);
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.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))
{
var result = srDecrypt.ReadToEnd();
return result;
}
}
}
}
}
This crashes unless I have the result.Padding = PaddingMode.Zeros line (which is not in the MS example I used as a basis)
When I use the extension as below, the original value is not returned
const string key = "test1234test1234";
var textToEncrypt = "TESTING ENCRYPTION";
var actualEncryptionInfo = textToEncrypt.EncryptWithAes(key);
var decryptedText = actualEncryptionInfo.Encrypted.DecryptFromAes(key, actualEncryptionInfo.InversionVector);
What am I doing wrong?
The AesEncryptionInfo is a simple class that holds the encrypted value and the IV
public class AesEncryptionInfo
{
public AesEncryptionInfo(byte[] encrypted, byte[] inversionVector)
{
InversionVector = inversionVector;
Encrypted = encrypted;
}
public byte[] InversionVector { get; set; }
public byte[] Encrypted { get; set; }
}
Paul
You are disposing the Aes instance with using statement in GetAes method.
Change:
using var result = Aes.Create();
to:
var result = Aes.Create();
And dispose the Aes in DecryptFromAes and EncryptWithAes.
using var aesAlg = GetAes(keyText, new byte[0]);
using var aesAlg = GetAes(keyText, iv);
The code below works fine and encrypts:
hello world
to:
BgGUY2eR7GfumjbQr58tBQ==
for a run. The next time this may be different, for example:
CYIM7V/h3iXu5PYzwmQ33g==
I think this is the point of this algorithm. How do I decrypt a string that was encrypted a while back?
If I do:
static void Main(string[] args)
{
var key = #"abcdefghijklmnopqrstuvw==";
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = Convert.FromBase64String(key);
aesAlg.GenerateIV();
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var helloWorld = DecryptProperty(decryptor, "CYIM7V/h3iXu5PYzwmQ33g==");
}
}
I get:
System.Security.Cryptography.CryptographicException
HResult=0x80131430
Message=Padding is invalid and cannot be removed.
Source=System.Core
StackTrace:
at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.ReadToEnd()
at crypt.Program.DecryptProperty(ICryptoTransform decryptor, String valueToDecrypt)
Any ideas? How can I decrypt a string encrypted in the past using the key? Thanks!
Working code:
using System;
using System.IO;
using System.Security.Cryptography;
namespace crypt
{
class Program
{
static void Main(string[] args)
{
var key = #"abcdefghijklmnopqrstuvw==";
using (var aesAlg = Aes.Create())
{
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
aesAlg.Key = Convert.FromBase64String(key);
aesAlg.GenerateIV();
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
var encHelloWorld = EncryptProperty(encryptor, "hello world");
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var helloWorld = DecryptProperty(decryptor, encHelloWorld);
}
}
private static string EncryptProperty(ICryptoTransform encryptor, string valueToEncrypt)
{
byte[] encrypted;
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(valueToEncrypt);
}
encrypted = msEncrypt.ToArray();
}
}
return Convert.ToBase64String(encrypted);
}
private static string DecryptProperty(ICryptoTransform decryptor, string valueToDecrypt)
{
string decrypted;
using (var msDecrypt = new MemoryStream(Convert.FromBase64String(valueToDecrypt)))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
return decrypted;
}
}
}
You are generating a new (random) Initialization Vector on every decrypt. You need to use the same IV when decrypting that was used when encrypting.
Your sample code works because it uses the same algo state (IV included) for encrypting and decrypting in the same run, but won't work across runs because GenerateIV creates a new random buffer every time.
Typically one would write the IV before the encrypted value and then store them together.
I am trying to implement image steganography with LSB and everything works except decrypting.
There is my class responsible for encryption and decryption of strings below. Encrypting works fine but Decrypt method always returns null:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp1
{
class Encryptor {
//text to encrypt or already decrypted
private String decryptedText = "";
//text to decrypt or already encrypted
private String encryptedText = "";
private String key = "";
public Encryptor setDecryptedText(String text)
{
decryptedText = text;
return this;
}
public Encryptor setEncryptedText(String text)
{
encryptedText = text;
return this;
}
public Encryptor setKey(String text)
{
key = text;
return this;
}
Byte[] getHash(Byte[] hash)
{
Byte[] newHash = new Byte[32];
for (int i = 0; i < 32; i++)
{
newHash[i] = hash[i];
}
return newHash;
}
Byte[] getIV(Byte[] hash)
{
Byte[] newHash = new Byte[16];
int j = 0;
for (int i = 32; i < 48; i++)
{
newHash[j++] = hash[i];
}
return newHash;
}
String EncryptAesManaged()
{
SHA512 shaM = new SHA512Managed();
Byte[] data = Encoding.UTF8.GetBytes(key);
Byte[] hash = shaM.ComputeHash(data);
try
{
return Encrypt(decryptedText, getHash(hash), getIV(hash));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
String DecryptAesManaged()
{
SHA512 shaM = new SHA512Managed();
var data = Encoding.UTF8.GetBytes(key);
Byte[] hash = shaM.ComputeHash(data);
try
{
return Decrypt(Convert.FromBase64String(encryptedText), getHash(hash), getIV(hash));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return "";
}
String Encrypt(string plainText, byte[] Key, byte[] IV)
{
Byte[] encrypted;
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs)) {
sw.Write(Encoding.UTF8.GetBytes(plainText));
cs.FlushFinalBlock();
encrypted = ms.ToArray();
}
}
}
aes.Clear();
}
return Convert.ToBase64String(encrypted);
}
string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform decryptor = aes.CreateDecryptor(Key, IV);
try
{
using (MemoryStream ms = new MemoryStream(cipherText))
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (StreamReader reader = new StreamReader(cs))
{
plaintext = reader.ReadToEnd(); //Here get null
}
aes.Clear();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return plaintext;
}
public String getEncrypted()
{
return EncryptAesManaged();
}
public String getDecrypted()
{
return DecryptAesManaged();
}
}
}
Why is Decrypt() returning null rather than the originally encrypted string?
You don't show how you use your Encryptor class, so your question doesn't quite include a Minimal, Complete, and Verifiable example. I was able to reproduce the problem with the following test harness:
public static void Test()
{
var key = "my key";
var plainText = "hello";
var encryptor = new Encryptor();
encryptor.setDecryptedText(plainText);
encryptor.setKey(key);
var encrypted = encryptor.getEncrypted();
Console.WriteLine(encrypted);
var deecryptor = new Encryptor();
deecryptor.setEncryptedText(encrypted);
deecryptor.setKey(key);
var decrypted = deecryptor.getDecrypted();
Console.WriteLine(decrypted);
Assert.IsTrue(plainText == decrypted);
}
Demo fiddle #1 here.
Given that, your code has 2 problems, both of which are actually in encryption rather than decryption.
Firstly, in Encrypt(string plainText, byte[] Key, byte[] IV), you are writing to the StreamWriter sw, then flushing the CryptoStream and returning the MemoryStream contents -- but you never flush or dispose sw, so its buffered contents are never forwarded to the underlying stream(s).
To fix this, your code should looks something like:
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(Encoding.UTF8.GetBytes(plainText));
}
}
encrypted = ms.ToArray();
}
Now getDecrypted() no longer returns a null result -- but instead returns a wrong result of "System.Byte[]", as shown in demo fiddle #2 here.
Secondly, again in Encrypt(...), you are effectively encoding your plainText twice at this line:
sw.Write(Encoding.UTF8.GetBytes(plainText));
Encoding.UTF8.GetBytes(plainText) converts the plain text to a byte array, but the StreamWriter is also intended to do this job, converting strings to bytes and passing them to the underlying stream. So, since you are not passing a string to Write(), the overload that gets called is StreamWriter.Write(Object):
Writes the text representation of an object to the text string or stream by calling the ToString() method on that object.
Thus what actually gets encrypted is the ToString() value of a byte array, which is "System.Byte[]".
To fix this, simply remove the call to Encoding.UTF8.GetBytes(plainText) and write the string directly. Thus your Encrypt() method should now look like:
static String Encrypt(string plainText, byte[] Key, byte[] IV)
{
string encrypted;
using (var aes = new RijndaelManaged())
{
aes.Mode = CipherMode.CBC;
aes.BlockSize = 128;
aes.KeySize = 256;
ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write, true))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(plainText);
}
}
// Calling GetBuffer() avoids the extra allocation of ToArray().
encrypted = Convert.ToBase64String(ms.GetBuffer(), 0, checked((int)ms.Length));
}
aes.Clear();
}
return encrypted;
}
Demo fiddle #3 here that now passes successfully.
Disclaimer: this answer does not attempt to to review your code for security best practices such as secure setup of salt and IV.
I'm trying to encrypt and decrypt a file using AES. The problem that I have is that when the file gets decrypted, it is broken and you can't open it. The original file has a length of 81.970 bytes and the decrypted file has a length of 81.984 bytes...so there are 14 bytes added for some reason. The problem could be in the way the file gets encrypted but I don't know what I'm doing wrong.
What am I missing here? Could it be the way I'm processing the password, the iv and the padding?
Thanks for your time!
This is the code I use to encrypt:
private AesManaged aesManaged;
private string filePathToEncrypt;
public Encrypt(AesManaged aesManaged, string filePathToEncrypt)
{
this.aesManaged = aesManaged;
this.filePathToEncrypt = filePathToEncrypt;
}
public void DoEncryption()
{
byte[] cipherTextBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using(ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(textBytes, 0, textBytes.Length);
cs.FlushFinalBlock();
cipherTextBytes = ms.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherTextBytes);
}
This is the code I use to decrypt:
private AesManaged aesManaged;
private string filePathToDecrypt;
public Decrypt(AesManaged aesManaged, string filePathToDecrypt)
{
this.aesManaged = aesManaged;
this.filePathToDecrypt = filePathToDecrypt;
}
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] clearBytes = new byte[cypherBytes.Length];
ICryptoTransform encryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV);
using (MemoryStream ms = new MemoryStream(cypherBytes))
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Read))
{
cs.Read(clearBytes, 0, clearBytes.Length);
clearBytes = ms.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", clearBytes);
}
And here is how I call the functions:
string filePathToEncrypt = "dilbert.gif";
string filePathToDecrypt = "EncryptedFile.aes";
string password = "Password";
string passwordSalt = "PasswordSalt";
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(passwordSalt));
var aesManaged = new AesManaged
{
Key = deriveBytes.GetBytes(128 / 8),
IV = deriveBytes.GetBytes(16),
Padding = PaddingMode.PKCS7
};
Console.WriteLine("Encrypting File...");
var encryptor = new Encrypt(aesManaged, filePathToEncrypt);
encryptor.DoEncryption();
Thread.Sleep(300);
Console.WriteLine("Decrypting File...");
var decryptor = new Decrypt(aesManaged, filePathToDecrypt);
decryptor.DoDecrypt();
Thread.Sleep(300);
Try with:
public void DoEncryption()
{
byte[] cipherBytes;
byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);
using (ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(textBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
{
input.CopyTo(cs);
cs.FlushFinalBlock();
cipherBytes = output.ToArray();
}
File.WriteAllBytes("EncryptedFile.aes", cipherBytes);
}
and
public void DoDecrypt()
{
byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
byte[] textBytes;
using (ICryptoTransform decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
using (MemoryStream input = new MemoryStream(cypherBytes))
using (MemoryStream output = new MemoryStream())
using (CryptoStream cs = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
{
cs.CopyTo(output);
textBytes = output.ToArray();
}
File.WriteAllBytes("DecryptedFile.gif", textBytes);
}
Note that the code could be modified to not use temporary byte[] and read/write directly to input/output streams.
In general you can't desume the length of the plaintext from the length of the cyphertext, so this line:
new byte[cypherBytes.Length]
was totally wrong.
And please, don't use Encoding.ASCII in 2016. It is so like previous century. Use Encoding.UTF8 to support non-english characters.
The answer may be very simple. I don't see where do u try to choose a cipher mode, so by default it probably takes CBC, as IV was inited. Then, 81.970 are padded by 14 bytes, to be divisible by 32. So when it happens, the memory you allocated was just 81.970, so the padding bytes doesn't write correctly, cause of some sort of memory leak, and when decrypt is started, unpadding doesn't work correctly.
Please suggest me where i need to update/refactor the code to get rid of exception. I am getting exception while I try to decrypt the encrypted string using following code.
Following line is throwing exception
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
public string EncryptAuthenticationTokenAes(string plainText)
{
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
aesAlg.Padding = PaddingMode.None;
// 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);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return Convert.ToBase64String(encrypted);
}
public string DecryptPasswordAes(string encryptedString)
{
//Convert cipher text back to byte array
byte[] cipherText = Convert.FromBase64String(encryptedString);
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
aesAlg.Padding = PaddingMode.None;
// 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;
}
Pretty standard bug when using CryptoStream, you forgot to force it to encrypt the last bytes of the stream. It keeps bytes in an internal buffer until enough of them arrive to emit a block. You must force the last few bytes out. Fix:
using (var msEncrypt = new MemoryStream())
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(plainText);
csEncrypt.FlushFinalBlock();
encrypted = msEncrypt.ToArray();
}
You got the exception when decrypting it because encrypted is missing the final padding. The real problem is caused by the using statement, you wouldn't have this problem if you waited obtaining the encrypted bytes until after the CryptoStream is closed. But that doesn't work well because the using statement on the StreamWriter also closes the CryptoStream and the MemoryStream. Explicitly using FlushFinalBlock() is the best workaround.