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.
Related
I have an encryption algorithm and decryption algorithm used for a login page. When encrypting, everything seems fine and seems to encrypt everything as expected, but when the encrypted password is put back through my decryption algorithm, only the first character is returned.
Example
Here is the code:
public string encrypt(string key, string data)
{
byte[] clearBytes = Encoding.Unicode.GetBytes(data);
byte[] iv = new byte[16];
using (Aes encryptor = Aes.Create())
{
encryptor.Key = Encoding.UTF8.GetBytes(key);
encryptor.IV = iv;
encryptor.Padding = PaddingMode.PKCS7;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
data = Convert.ToBase64String(ms.ToArray());
}
}
return data;
}
public static string decryptData(string key, string data)
{
byte[] iv = new byte[16];
byte[] clearBytes = Convert.FromBase64String(data);
using (Aes decrpytor = Aes.Create())
{
decrpytor.Key = Encoding.UTF8.GetBytes(key);
decrpytor.IV = iv;
ICryptoTransform decryptor = decrpytor.CreateDecryptor(decrpytor.Key, decrpytor.IV);
decrpytor.Padding = PaddingMode.PKCS7;
using (MemoryStream ms = new MemoryStream(clearBytes))
{
using (CryptoStream cs = new CryptoStream((Stream)ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader((Stream)cs))
{
return sr.ReadToEnd();
}
}
}
}
}
public void button1_Click(object sender, EventArgs e)
{
var key = "b14ca5898a4e4133bbce2ea2315a1916";
var str = passWord.Text;
var encryptedPassword = encrypt(key, str);
var decrpted = decryptData(key, encryptedPassword);
passwordTest.Text = encryptedPassword;
decryptedtest.Text = decrpted;
}
the last 2 lines are purely for testing to see what they output.
Any help is appreciated.
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);
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).
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.
Whenever I decrypt a file I end up with \0 in between each character (this is a text file) The original is fine, and the decryption is successful without errors.
When I do open the file ; "hello" would become "h\0e\0l\0..."
Here is my decryption function: (I came up with a fix of converting the byte array to utf8 then manually removing the nulls, this is obviously not a solution. )
public static void DecryptFileToFile(String fromFile, String toFile, byte[] Key)
{
byte[] encryptedFile = IO.convertFileToByte(fromFile);
using (Aes aesAlg = Aes.Create())
{
byte[] dataIV = encryptedFile.Take(16).ToArray(); //first 16 bytes is iv
byte[] encryptedData = encryptedFile.Skip(16).Take(encryptedFile.Length-16).ToArray();
aesAlg.Key = Key;
using (var decryptor = aesAlg.CreateDecryptor(Key, dataIV))
{
byte[] final = PerformCryptography(decryptor, encryptedData);
string result = System.Text.Encoding.UTF8.GetString(final);
result = result.Replace("\0", string.Empty);
IO.writeStringToFile(result,toFile);
}
}
}
private static byte[] PerformCryptography(ICryptoTransform cryptoTransform, byte[] data)
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();
}
}
}
to encrypt:
public static void EncryptFileToFile(String fromFile, String toFile, byte[] Key){
byte[] original = IO.convertFileToByte(fromFile);
using (Aes aesAlg = Aes.Create())
{
aesAlg.GenerateIV();
aesAlg.Key = Key;
using (var encryptor = aesAlg.CreateEncryptor(Key, aesAlg.IV))
{
byte[] encryptedData = PerformCryptography(encryptor, original);
byte[] final = Combine(aesAlg.IV,encryptedData);
IO.writeByteToFile(final, toFile);
}
}
}