I have two functions that should encrypt and decrypt a given file:
public static void Encrypt(string name, string skey)
{
using (var sourceStream = File.OpenRead(name))
using (var destinationStream = File.Create(name + ".sav"))
using (var provider = new AesCryptoServiceProvider())
using (var cryptoTransform = provider.CreateEncryptor())
using (var cryptoStream = new CryptoStream(destinationStream, cryptoTransform, CryptoStreamMode.Write))
{
provider.Padding = PaddingMode.None;
List<byte> key = new List<byte>();
key.AddRange(ASCIIEncoding.ASCII.GetBytes(skey));
while (key.Count < 32)
{
key.Add(0);
}
provider.Key = key.ToArray();
destinationStream.Write(provider.IV, 0, provider.IV.Length);
sourceStream.CopyTo(cryptoStream);
}
}
public static void Decrypt(string name, string skey)
{
// Decrypt the source file and write it to the destination file.
using (var sourceStream = File.OpenRead(name + ".sav"))
using (var destinationStream = File.Create(name))
using (var provider = new AesCryptoServiceProvider())
{
provider.Padding = PaddingMode.None;
List<byte> key = new List<byte>();
key.AddRange(ASCIIEncoding.ASCII.GetBytes(skey));
while(key.Count < 32)
{
key.Add(0);
}
var IV = new byte[provider.IV.Length];
sourceStream.Read(IV, 0, IV.Length);
using (var cryptoTransform = provider.CreateDecryptor(key.ToArray(), IV))
using (var cryptoStream = new CryptoStream(sourceStream, cryptoTransform, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(destinationStream);
}
}
}
Using the text document (d):
<?xml version="1.0" encoding="utf-8"?>
<Game xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<username>d</username>
<superSecretString>foo</superSecretString>
</Game>
The following program:
static void Main(string[] args)
{
string pass = "foo";
Encrypt("d", pass);
Decrypt("d", pass);
}
Results in the text document (d):
豄襚釞뮲冋ⵂૠɥ摸▪穨嶾⏘ﳿ僣ꙍ帏પ軆ꝍ遨䄩䰀玢⊟尩㯇Ўꉚ파 駧㪡襻긗몸뵊➯턊鏶㠌늸㗐蕛貣ㆪⲫ咻ꨱ壉뷢䅱眡�׀罹吗㶝�晊垚猰Ġ�淧脽ᣉ赮榑舎㈁쾹͂쵎숵戥힠┻蔦쬦눲ꔲ빬䁔㸻�ⵄ➞ޅ㥿࠾ऱ룮2侚神ヵ튙腶㖮衦ྗ
It seems that decrypting the message is causing the unexpected result however I don't what I should expect in the "*.sav" files besides random noise. Checking the watchlist provider.key is an array that reads out 102, 111, 111, 0 ... 0 in both Encrypt and Decrypt methods.
Okay so your problem is that you are creating Encryptor but never set it's IV.
Change the Encrypt method like so:
public static void Encrypt(string name, string skey)
{
// First, process key
List<byte> key = new List<byte>();
key.AddRange(ASCIIEncoding.ASCII.GetBytes(skey));
while (key.Count < 32)
{
key.Add(0);
}
using (var sourceStream = File.OpenRead(name))
using (var destinationStream = File.Create(name + ".sav"))
using (var provider = new AesCryptoServiceProvider())
using (var cryptoTransform = provider.CreateEncryptor(key.ToArray(), provider.IV)) // when creating Encryptor pass the key and the IV of the provider.
using (var cryptoStream = new CryptoStream(destinationStream, cryptoTransform, CryptoStreamMode.Write))
{
provider.Padding = PaddingMode.None;
provider.Key = key.ToArray();
destinationStream.Write(provider.IV, 0, provider.IV.Length);
sourceStream.CopyTo(cryptoStream);
}
}
Related
I want to make change in below encryption code so it gives me same encrypted string for a given text again and again.
e.g input string : "test"
output encrypted string : "##$##$##$SDFDSF"
If I input "test" again and again output should be same.
public static string Encrypt(string text, bool encryptSame = false)
{
var key = Encoding.UTF8.GetBytes("E546C8DF278CD5931069B522E695D4F3");
using (var aesAlg = Aes.Create())
{
if (encryptSame)
{
aesAlg.Padding = PaddingMode.Zeros;
aesAlg.Mode = CipherMode.ECB;
}
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
}
pretty new to c# and currently having a problem decrypting long passwords with an error of
Specified key is not a valid size for this algorithm
I know this has something to do with the encrypted password bits length not being supported but unsure how to go about suggested ways to allow for these longer passwords.
Here's my encrypt and decrypt
"cipherKey": "0123456789abcdef", "cipherVector":
"somereallycooliv"
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
namespace DataApi
{
public class Encryption
{
private readonly IConfigurationService _configService;
private const string _vector = "cipherVector";
private const string _key = "cipherKey";
public Encryption(IConfigurationService configService)
{
_configService = configService;
}
public string EncryptString(string text)
{
if(string.IsNullOrEmpty(text))
{
return "";
}
try
{
var key = Encoding.UTF8.GetBytes(_configService.Get(_key));
byte[] IV = Encoding.ASCII.GetBytes(_configService.Get(_vector));
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var decryptedContent = msEncrypt.ToArray();
var result = new byte[IV.Length + decryptedContent.Length];
Buffer.BlockCopy(IV, 0, result, 0, IV.Length);
Buffer.BlockCopy(decryptedContent, 0, result, IV.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
}
catch(Exception e) {
Loggifer.Error("Unable to encrypt string: "+text , e );
throw e;
}
}
public string DecryptString(string cipherText)
{
if(string.IsNullOrEmpty(cipherText))
{
return "";
}
try
{
var fullCipher = Convert.FromBase64String(cipherText);
byte[] IV = Encoding.ASCII.GetBytes(_configService.Get(_vector));
var cipher = new byte[16];
Buffer.BlockCopy(fullCipher, 0, IV, 0, IV.Length);
Buffer.BlockCopy(fullCipher, IV.Length, cipher, 0, IV.Length);
var key = Encoding.UTF8.GetBytes(_configService.Get(_key));
using (var aesAlg = Aes.Create())
{
using (var decryptor = aesAlg.CreateDecryptor(key, IV))
{
string result;
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
}
catch (Exception e)
{
Loggifer.Error("Unable to decrypt string: "+cipherText , e );
throw e;
}
}
}
}
Two changes are required in the function public string DecryptString(string cipherText)
var cipher = new byte[fullCipher.Length - IV.Length];
and
Buffer.BlockCopy(fullCipher, IV.Length, cipher, 0, fullCipher.Length - IV.Length);
I am using AES - Standard SymmetricAlgorithm, for Encrypting and Decrypting string. String is encrypting successfully but when it comes to decrypt the compiler gives me the exception i.e
“Padding is invalid and cannot be removed”.
I have created a demo console application for testing, please have a look on below code.
The Main Method:
static void Main(string[] args)
{
var content = "5466160057107706";
var key = "E546C8DF278CD5931069B522E695D4F2";
var encrypted = EncryptString(content, key);
Console.WriteLine(encrypted);
var decrypted = DecryptString(encrypted, key);
Console.WriteLine(decrypted);
Console.ReadLine();
}
Method added for Encryption:
public static string EncryptString(string text, string keyString)
{
var key = Encoding.UTF8.GetBytes(keyString);
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
}
Method added for Decryption:
public static string DecryptString(string cipherText, string keyString)
{
var fullCipher = Convert.FromBase64String(cipherText);
var iv = new byte[16];
var cipher = new byte[16];
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length);
var key = Encoding.UTF8.GetBytes(keyString);
using (var aesAlg = Aes.Create())
{
using (var decryptor = aesAlg.CreateDecryptor(key, iv))
{
string result;
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
}
Here is the Error Snap which occurs in the DecryptString() method:
Any advice, how to solve this issue?
Clue:
var cipher = new byte[16];
Why are you assuming your cipher would be ONLY 16 bytes ? What if it is more than this ?
In fact, if I run this program and debug, I see that your cipher is 32 bytes.
So, the following 2 line changes makes it work:
var cipher = new byte[32];
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, cipher.Length);
In any case, you would need to dynamically determine your size of cipher.
The code below and in the Fiddle isn't for production, it is for educational purposes. I do not want to fix anything, as I have a viable solution. However, I would like to know why:
var password = "password";
var salt = Encoding.ASCII.GetBytes(password.Length.ToString());
var secret = new PasswordDeriveBytes(password, salt);
When the above is implemented, in the following method FixedEncryptor will work.
// Valid:
public static string FixedEncryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes(password, salt);
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
However, if you implement:
var secret = new PasswordDeriveBytes("password",
Encoding.ASCII.GetBytes("password"));
The code will suddenly produce:
Run-time exception (line 70): Padding is invalid and cannot be
removed.
Stack Trace:
[System.Security.Cryptography.CryptographicException: Padding is
invalid and cannot be removed.] at Crypt.Decryptor(String content):
line 70 at Program.Main(): line 17
As denoted in the following method:
// Invalid:
public static string Encryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
So why can one successfully decrypt, while the other doesn't decrypt correctly and produces the above error?
A Fiddle with a small example is here.
From your posted code example your problem comes from the fact you are using two different salts.
In FixedEncryptor you use a salt of
Encoding.ASCII.GetBytes(password.Length.ToString());
That encodes to be a byte array equal to { 56 }, this is because Length returns 8 then calling ToString() on that returns the string "8" which you convert in to the ascii value 56.
In Encryptor you use a salt of
Encoding.ASCII.GetBytes("password")
That encodes to be a byte array equal to { 112, 97, 115, 115, 119, 111, 114, 100}, which is the ascii values of the characters "p", "a", "s", "s", "w", "o", "r", and "d".
The problem you are running in to is you only attempt to use { 56 } in your decrypt function, so your problem comes down to your encrypt function and your decrypt function are using two different salts.
If I make a make a new Decrypter to use the same salt and password as Encryptor then make a separate FixedDecryptor to match the salt of FixedEncryptor everything will work fine
public class Program
{
public static void Main()
{
var message = "Hello World!";
var fixedCipherText = Crypt.FixedEncryptor(message);
var cipherText = Crypt.Encryptor(message);
Console.WriteLine(cipherText);
Console.WriteLine(fixedCipherText);
var plainText = Crypt.Decryptor(cipherText);
var fixedPlainText = Crypt.FixedDecryptor(fixedCipherText);
Console.WriteLine(plainText);
Console.WriteLine(fixedPlainText);
}
}
public static class Crypt
{
private const string password = "password";
private readonly static byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());
public static string FixedEncryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes(password, salt);
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
public static string Encryptor(string content)
{
var cipher = new RijndaelManaged();
var plain = Encoding.Unicode.GetBytes(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var encrypt = cipher.CreateEncryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encrypt, CryptoStreamMode.Write))
{
crypto.Write(plain, 0, plain.Length);
crypto.FlushFinalBlock();
return Convert.ToBase64String(stream.ToArray());
}
}
public static string FixedDecryptor(string content)
{
var cipher = new RijndaelManaged();
var encrypted = Convert.FromBase64String(content);
var key = new PasswordDeriveBytes(password, salt);
using (var decryptor = cipher.CreateDecryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream(encrypted))
using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
byte[] plain = new byte[encrypted.Length];
int decrypted = crypto.Read(plain, 0, plain.Length);
string data = Encoding.Unicode.GetString(plain, 0, decrypted);
return data;
}
}
public static string Decryptor(string content)
{
var cipher = new RijndaelManaged();
var encrypted = Convert.FromBase64String(content);
var key = new PasswordDeriveBytes("password", Encoding.ASCII.GetBytes("password"));
using (var decryptor = cipher.CreateDecryptor(key.GetBytes(32), key.GetBytes(16)))
using (var stream = new MemoryStream(encrypted))
using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
{
byte[] plain = new byte[encrypted.Length];
int decrypted = crypto.Read(plain, 0, plain.Length);
string data = Encoding.Unicode.GetString(plain, 0, decrypted);
return data;
}
}
}
Fiddel of the code.
However this still is not the "correct" way to do things. See Sine Nomen's answer
First of all, the method by which you generate a salt is not secure at all; secondly, PasswordDerivedBytes is deprecated and you should be looking at its successor, Rfc2898DeriveBytes.
Try something like the following - note that this requires a few using statements: System, System.IO, System.Security.Cryptography and System.Text.
Simply encrypt the data with Encrypt(PlainText, Password) and decrypt it again with Decrypt(EncryptedData, Password). The salt is rolled into the encrypted data as the first 16 bytes and it is completely random for each encryption/decryption round.
This code is part of my own open source password manager.
/*
* Encryption/Decryption, based on AES256 and PBKDF2
*/
public string Encrypt (string plainText, string passPhrase, bool fast_encrypt = false)
{
string result;
using (Rijndael algR = Rijndael.Create ()) {
RNGCryptoServiceProvider rngC = new RNGCryptoServiceProvider ();
byte[] iv = new byte[16];
rngC.GetBytes (iv);
Rfc2898DeriveBytes derived = new Rfc2898DeriveBytes (passPhrase, iv, fast_encrypt ? 10 : 3000);
algR.KeySize = 256;
algR.BlockSize = 128;
algR.Key = derived.GetBytes (32);
algR.IV = iv;
using (MemoryStream memoryStream = new MemoryStream ()) {
memoryStream.Write (iv, 0, 16);
using (CryptoStream cryptoStreamEncrypt = new CryptoStream (memoryStream, algR.CreateEncryptor (algR.Key, algR.IV), CryptoStreamMode.Write)) {
using (StreamWriter streamWriterEncrypt = new StreamWriter (cryptoStreamEncrypt)) {
streamWriterEncrypt.Write (plainText);
}
}
result = Convert.ToBase64String (memoryStream.ToArray ());
}
}
return result;
}
public string Decrypt (string cipherText, string passPhrase, bool fast_decrypt = false)
{
string result;
using (Rijndael algR = Rijndael.Create ()) {
using (MemoryStream memoryStream = new MemoryStream (Convert.FromBase64String (cipherText))) {
byte[] iv = new byte[16];
memoryStream.Read (iv, 0, 16);
Rfc2898DeriveBytes derived = new Rfc2898DeriveBytes (passPhrase, iv, fast_decrypt ? 10 : 3000);
algR.KeySize = 256;
algR.BlockSize = 128;
algR.Key = derived.GetBytes (32);
algR.IV = iv;
using (CryptoStream cryptoStreamDecrypt = new CryptoStream (memoryStream, algR.CreateDecryptor (algR.Key, algR.IV), CryptoStreamMode.Read)) {
using (StreamReader streamReaderDecrypt = new StreamReader (cryptoStreamDecrypt)) {
result = streamReaderDecrypt.ReadToEnd ();
}
}
}
}
return result;
}
per my question Aes Encryption... missing an important piece, I have now learned that my assumption for creating a reversible encryption on a string was a bit off. I now have
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
and this produces consistent results; however, I will not be able to decrypt without knowing/ setting the initialization vector. I really do not want to pass three values into this method (on for the IV), which leaves me with hardcoding the IV or deriving it from the key. I'd like to know if this is a good practice, or if it will render the encrypted value vulnerable to attack somehow... or am I really overthinking this and should just hardcode the IV?
UPDATE
Per Iridium's suggestion, I tried something like this instead:
public static byte[] EncryptString(string toEncrypt, byte[] encryptionKey)
{
if (string.IsNullOrEmpty(toEncrypt)) throw new ArgumentException("toEncrypt");
if (encryptionKey == null || encryptionKey.Length == 0) throw new ArgumentException("encryptionKey");
var toEncryptBytes = Encoding.UTF8.GetBytes(toEncrypt);
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(provider.IV, 0, 16);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(toEncryptBytes, 0, toEncryptBytes.Length);
cs.FlushFinalBlock();
}
return ms.ToArray();
}
}
}
}
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream(encryptedString))
{
byte[] buffer;
ms.Read(buffer, 0, 16);
provider.IV = buffer;
using (var decryptor = provider.CreateDecryptor(provider.Key, provider.IV))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
byte[] decrypted = new byte[encryptedString.Length];
var byteCount = cs.Read(decrypted, 0, encryptedString.Length);
return Encoding.UTF8.GetString(decrypted, 0, byteCount);
}
}
}
}
}
however, this shows something odd in my unit test:
[TestMethod]
public void EncryptionClosedLoopTest()
{
var roundtrip = "This is the data I am encrypting. There are many like it but this is my encryption.";
var encrypted = Encryption.EncryptString(roundtrip, encryptionKey);
var decrypted = Encryption.DecryptString(encrypted, encryptionKey);
Assert.IsTrue(roundtrip == decrypted);
}
my decrypted text shows up as "92ʪ�F"�,hpv0�� I am encrypting. There are many like it but this is my encryption." which seems almost right but of course completely wrong. It looks like I'm close though. Am I missing an offset on the memory stream?
The IV should be random and unique for every run of your encryption method. Deriving it from the key/message or hard-coding it is not sufficiently secure. The IV can be generated within this method, instead of passed into it, and written to the output stream prior to the encrypted data.
When decrypting, the IV can then be read from the input before the encrypted data.
When Encrypting, generate your IV and pre-pend it to the cipher text (something like this)
using (var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
var input = Encoding.UTF8.GetBytes(originalPayload);
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
//Prepend IV to data
//tBinaryWriter.Write(iv); This is the original broken code, it encrypts the iv
cipherStream.Write(iv); //Write iv to the plain stream (not tested though)
tBinaryWriter.Write(input);
tCryptoStream.FlushFinalBlock();
}
string encryptedPayload = Convert.ToBase64String(cipherStream.ToArray());
}
}
When decrypting this back, get first 16 bytes out and use it in crypto stream
var aes= new AesCryptoServiceProvider()
{
Key = PrivateKey,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
//get first 16 bytes of IV and use it to decrypt
var iv = new byte[16];
Array.Copy(input, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
//Decrypt Cipher Text from Message
binaryWriter.Write(
input,
iv.Length,
input.Length - iv.Length
);
}
return Encoding.Default.GetString(ms.ToArray());
}
Great input from folks. I took the combined answers from ankurpatel and Konstantin and cleaned it up and added some convenient method overrides. This works as of June 2019 in .NET Core 2.2.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
private const int AesKeySize = 16;
public static void Main()
{
// the data to encrypt
var message = "Here is some data to encrypt!";
// create KeySize character key
var key = "g(KMDu(EEw63.*V`";
// encrypt the string to a string
var encrypted = AesEncrypt(message, key);
// decrypt the string to a string.
var decrypted = AesDecrypt(encrypted, key);
// display the original data and the decrypted data
Console.WriteLine($"Original: text: {encrypted}");
Console.WriteLine($"Round Trip: text: {decrypted}");
}
static string AesEncrypt(string data, string key)
{
return AesEncrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesDecrypt(string data, string key)
{
return AesDecrypt(data, Encoding.UTF8.GetBytes(key));
}
static string AesEncrypt(string data, byte[] key)
{
return Convert.ToBase64String(AesEncrypt(Encoding.UTF8.GetBytes(data), key));
}
static string AesDecrypt(string data, byte[] key)
{
return Encoding.UTF8.GetString(AesDecrypt(Convert.FromBase64String(data), key));
}
static byte[] AesEncrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
aes.GenerateIV();
var iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(aes.Key, iv))
using (var cipherStream = new MemoryStream())
{
using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var tBinaryWriter = new BinaryWriter(tCryptoStream))
{
// prepend IV to data
cipherStream.Write(iv);
tBinaryWriter.Write(data);
tCryptoStream.FlushFinalBlock();
}
var cipherBytes = cipherStream.ToArray();
return cipherBytes;
}
}
}
static byte[] AesDecrypt(byte[] data, byte[] key)
{
if (data == null || data.Length <= 0)
{
throw new ArgumentNullException($"{nameof(data)} cannot be empty");
}
if (key == null || key.Length != AesKeySize)
{
throw new ArgumentException($"{nameof(key)} must be length of {AesKeySize}");
}
using (var aes = new AesCryptoServiceProvider
{
Key = key,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
// get first KeySize bytes of IV and use it to decrypt
var iv = new byte[AesKeySize];
Array.Copy(data, 0, iv, 0, iv.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cs))
{
// decrypt cipher text from data, starting just past the IV
binaryWriter.Write(
data,
iv.Length,
data.Length - iv.Length
);
}
var dataBytes = ms.ToArray();
return dataBytes;
}
}
}
I modified your decryption method as follows and it works:
public static string DecryptString(byte[] encryptedString, byte[] encryptionKey)
{
using (var provider = new AesCryptoServiceProvider())
{
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
{
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
}
}
}
The problem with your implementation is that you are reading too many bytes into the CryptoStream. You really need to read encryptedText.Length - 16. Using a StreamReader simplifies this, since you don't need to worry about offsets anywhere anymore.
The accepted answer is correct, but doesn't provide a good example of how to get a random IV.
It turns out that this is a lot easier than people are trying to make it. The AesCryptoServiceProvider in .NET automatically generates a cryptographically random IV every time you construct one. And if you need to use the same instance for multiple encryptions, you can call GenerateIV()
You can also prepend the IV to the encrypted value before returning it and have the decrypting end pull it off
private static void Main(string[] args) {
var rnd = new Random();
var key = new byte[32]; // For this example, I'll use a random 32-byte key.
rnd.NextBytes(key);
var message = "This is a test";
// Looping to encrypt the same thing twice just to show that the IV changes.
for (var i = 0; i < 2; ++i) {
var encrypted = EncryptString(message, key);
Console.WriteLine(encrypted);
Console.WriteLine(DecryptString(encrypted, key));
}
}
public static string EncryptString(string message, byte[] key) {
var aes = new AesCryptoServiceProvider();
var iv = aes.IV;
using (var memStream = new System.IO.MemoryStream()) {
memStream.Write(iv, 0, iv.Length); // Add the IV to the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateEncryptor(key, aes.IV), CryptoStreamMode.Write)) {
using (var writer = new System.IO.StreamWriter(cryptStream)) {
writer.Write(message);
}
}
var buf = memStream.ToArray();
return Convert.ToBase64String(buf, 0, buf.Length);
}
}
public static string DecryptString(string encryptedValue, byte[] key) {
var bytes = Convert.FromBase64String(encryptedValue);
var aes = new AesCryptoServiceProvider();
using (var memStream = new System.IO.MemoryStream(bytes)) {
var iv = new byte[16];
memStream.Read(iv, 0, 16); // Pull the IV from the first 16 bytes of the encrypted value
using (var cryptStream = new CryptoStream(memStream, aes.CreateDecryptor(key, iv), CryptoStreamMode.Read)) {
using (var reader = new System.IO.StreamReader(cryptStream)) {
return reader.ReadToEnd();
}
}
}
}
[EDIT: I modified my answer to include how to pass the IV in the encrypted value and get it when decrypting. I also refactored the example a bit]
In order to resolve the setting of IV on the provider (As Iridium pointed out):
ms.Read(provider.IV, 0, 16);
I added the following to your code:
var iv = new byte[provider.IV.Length];
memoryStream.Read(iv, 0, provider.IV.Length);
using (var decryptor = provider.CreateDecryptor(key, iv);
granted, my key is not set by the provider on each run. I generated it once and then stored it. The IV is randomly generated off of the provider for each encryption.
In my case, to generate the IV, I use something like this
/// <summary>
/// Derives password bytes
/// </summary>
/// <param name="Password">password</param>
/// <returns>derived bytes</returns>
private Rfc2898DeriveBytes DerivePass(string Password)
{
byte[] hash = CalcHash(Password);
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password, hash, _KEY_ITER);
return pdb;
}
/// <summary>
/// calculates the hash of the given string
/// </summary>
/// <param name="buffer">string to hash</param>
/// <returns>hash value (byte array)</returns>
private byte[] CalcHash(string buffer)
{
RIPEMD160 hasher = RIPEMD160.Create();
byte[] data = Encoding.UTF8.GetBytes(buffer);
return hasher.ComputeHash(data);
}
that is, I calculate the password hash using RIPEMD160 and use it to generate the derived bytes, at that point, when it comes to intializing the encryption/decryption I just use something like this
Rfc2898DeriveBytes pdb = DerivePass(Password);
SymmetricAlgorithm alg = _engine;
alg.Key = pdb.GetBytes(_keySize);
alg.IV = pdb.GetBytes(_IVSize);
I don't know if it's "correct" (probably crypto gurus here will shoot at me :D), but, at least, it gives me a decent IV and I don't have to store it "somewhere" since just entering the correct password will give back the needed IV value; as a note, the _engine in the above example is declared as "SymmetricAlgorithm" and initialized using something like this
_engine = Rijndael.Create();
_keySize = (_engine.KeySize / 8);
_IVSize = (_engine.BlockSize / 8);
which creates the desired crypto objects and initializes the key and IV sizes
To generate random IV you would need a truly random number. Whichever language specific API you use for generating the random number, should generate true random number. Both android and ios have apis which generate random numbers based on sensor data.
I recently implemented AES 256 with random IV (Generated using really random numbers) and hashed key. For more secure(random IV + hashed key) cross platform (android, ios, c#) implementation of AES see my answer here - https://stackoverflow.com/a/24561148/2480840