I have used below code to encrypt my value. However, I noticed that for the same value new encryption format is generated instead of same encryption value. Can anyone help me to solve this issue?
Example:
Value is HelloWorld123$
When I executed for the first time, I am getting this encryption - EAAAAE+WzLTCsNOJSQBuTwnRsfrRxqLa6WLVr0zWQ8eozkr1
When I executed for the second time, I am getting this encryption - EAAAAEJuBne0limVQ4aQij89v2SjU8eHasyDlnsGGQ1MD43V
Question: How can I solve to get same encryption all time for same value?
private static byte[] _salt = { 1, 2, 3, 4, 5, 6, 7, 8 }; // Array of numbers
internal static byte[] key = { 0x0A, 01, 02, 0x48 };
/// <summary>
/// Encrypt the given string using AES. The string can be decrypted using
/// DecryptStringAES(). The sharedSecret parameters must match.
/// </summary>
/// <param name="plainText">The text to encrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for encryption.</param>
private static string EncryptStringAES(string plainText, string sharedSecret)
{
if (string.IsNullOrEmpty(plainText))
throw new ArgumentNullException("plainText");
//if (string.IsNullOrEmpty(sharedSecret))
// throw new ArgumentNullException("sharedSecret");
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Create a decryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
// prepend the IV
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return outStr;
}
/// <summary>
/// Decrypt the given string. Assumes the string was encrypted using
/// EncryptStringAES(), using an identical sharedSecret.
/// </summary>
/// <param name="cipherText">The text to decrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
private static string DecryptStringAES(string cipherText, string sharedSecret)
{
if (string.IsNullOrEmpty(cipherText))
throw new ArgumentNullException("cipherText");
//if (string.IsNullOrEmpty(sharedSecret))
// throw new ArgumentNullException("sharedSecret");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Get the initialization vector from the encrypted stream
aesAlg.IV = ReadByteArray(msDecrypt);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
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();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
private static byte[] ReadByteArray(Stream s)
{
byte[] rawLength = new byte[sizeof(int)];
if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
{
throw new SystemException("Stream did not contain properly formatted byte array");
}
byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
{
throw new SystemException("Did not read byte array properly");
}
return buffer;
}
User Case:
I have a form which insert form value into database. Including some valuable items which are encrypted.
I have another form which checks whether value exist in database, When I using some method for some lookup functions I need to compare many condition for the same value. So I am directly coparing encrypted values. But as new value is created. I am unable to compare those value
Hope I am able to explain mu Use Case
Take a look at - https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netcore-3.1
This class uses a pseudorandom number generator in its work, which means that it is supposed to generate different data over time which means that your encrypted data is supposed to change. It always decrypts the same, but it isn't intended to be deterministic (producing same output for same inputs)
If you're doing something like storing a password you should use a hashing function (eg SHA256) rather than an encrypting function; they produce the same output for given inputs so you can compare the output today to the output yesterday and if they're the same you can decide that the user typed the same password today as he did yesterday
Ultimately you are probably in (or should want to be in) one of two places:
you need to store some data securely and be able to retrieve it and turn it back into the data it was, maybe because you're the only person who knows it but it needs to be used or known elsewhere. You need to encrypt the data for storage, decrypt it, use it, if you update it you need to re-encrypt and store it again
you need to be able to confirm some data that someone else knows; they will give you the data and you will check your record and decide whether they got it right or not. You need to hash the data then forget the original, the next time the person appears claiming they know the original data you hash what they claim it is and compare the hashes. If it's the same then their claim they know the data is correct
You might be wanting to have an encryption that is deterministic, but it's quite a rare thing to want and it feels more like you're misunderstanding some aspect of your use case. Go into more detail so we can better advise
If you encrypt the same value it will give you different result, it’s basically security and most of the encryptions work in this manner.
If it gives you same results on each encryption then it will be deterministic and should be avoided.
Related
I'm trying to learn cyber security and this is the very first thing I've done on it. I'm using this MSDN document ( https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?redirectedfrom=MSDN&view=netcore-3.1 ) and it partly works. I assume it's encrypting it fine as when it comes to the decryption some of the original data is there but some is lost. The data that is being encrypted is a class that has been formatted into a JSON string( I don't think this is relevant as it's still a string being encrypted).
But once it's been encrypted and decrypted it turns out like this:
I've ran this code and compared the results 5+ times and it's always: the start is wrong, username is partly right, password is always right and loginkey is partly right. So the error is recurring and always in the same spot.
Information you should know, the data get's encrypted and saved to a .txt file. The programme will run again and it will try and decrypted it. The Salt and password are saved on another file and those are read and used in the decryption.
There is a similar question on stackoverflow but the answer just says to use Rijndael(so not really an answer), this code is for me to learn and want an answer that isn't 4 lines long.
Code if curious(but it's basically the same as the MSDN document):
Encryption:
static void EncryptFile()
{
string pwd1 = SteamID;//steamID is referring to account ID on Valve Steam
using (RNGCryptoServiceProvider rngCsp = new
RNGCryptoServiceProvider())
{
rngCsp.GetBytes(salt1); //salt1 is a programme variable and will get saved to a file
}
SecureData File = new SecureData(_UserName,_PassWord,_LoginKey);
string JsonFile = JsonConvert.SerializeObject(File); //puts the class into Json format
int myIterations = 1000; //not needed
try
{
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1,
myIterations);
Aes encAlg = Aes.Create(); // This might be the issue as AES will be different when you decrypt
encAlg.Key = k1.GetBytes(16);
MemoryStream encryptionStream = new MemoryStream();
CryptoStream encrypt = new CryptoStream(encryptionStream,
encAlg.CreateEncryptor(), CryptoStreamMode.Write);
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(
JsonFile); //encrypt Data
encrypt.Write(utfD1, 0, utfD1.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
byte[] edata1 = encryptionStream.ToArray();
k1.Reset();
System.IO.File.WriteAllBytes(SecureFile, edata1); //writes encrypted data to file
}
catch (Exception e)
{
Console.WriteLine("Error: ", e);
}
}
Decryption:
static void DecryptFile()
{
string pwd1 = SteamID;
byte[] edata1;
try
{
edata1 = System.IO.File.ReadAllBytes(SecureFile); //reads the file with encrypted data on it
Aes encAlg = Aes.Create(); //I think this is the problem as the keyvalue changes when you create a new programme
Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1); //inputs from last time carry over
Aes decAlg = Aes.Create();
decAlg.Key = k2.GetBytes(16);
decAlg.IV = encAlg.IV;
MemoryStream decryptionStreamBacking = new MemoryStream();
CryptoStream decrypt = new CryptoStream(
decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
decrypt.Write(edata1, 0, edata1.Length);
decrypt.Flush();
decrypt.Close();
k2.Reset();
string data2 = new UTF8Encoding(false).GetString(
decryptionStreamBacking.ToArray());//decrypted data
SecureData items = JsonConvert.DeserializeObject<SecureData>(data2); //reformat it out of JSon(Crashes as format isn't accepted)
_UserName = items.S_UserName;
_PassWord = items.S_Password;
_LoginKey = items.S_LoginKey;
}
catch (Exception e)
{
Console.WriteLine("Error: ", e);
NewLogin();
}
}
Class Struct:
class SecureData
{
public string S_UserName { get; set; } //These are variables that are apart of Valve steam Login process
public string S_Password { get; set; }
public string S_LoginKey { get; set; }
public SecureData(string z, string x, string y)
{
S_UserName = z;
S_Password = x;
S_LoginKey = y;
}
}
The problem is caused by different IVs for encryption and decryption. For a successful decryption the IV from the encryption must be used.
Why are different IVs applied? When an AES instance is created, a random IV is implicitly generated. Two different AES instances therefore mean two different IVs. In the posted code, different AES instances are used for encryption and decryption. Although the reference encAlg used in the decryption has the same name as that of the encryption, the referenced instance is a different one (namely an instance newly created during decryption). This is different in the Microsoft example. There, the IV of the encryption is used in the decryption: decAlg.IV = encAlg.IV, where encAlg is the AES instance with which the encryption was performed.
The solution is to store the IV from the encryption in the file so that it can be used in the decryption. The IV is not secret and is usually placed before the ciphertext:
Necessary changes in EncryptFile:
...
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(JsonFile);
encryptionStream.Write(encAlg.IV, 0, encAlg.IV.Length); // Write the IV
encryptionStream.Flush();
encrypt.Write(utfD1, 0, utfD1.Length);
...
Necessary changes in DecryptFile:
...
edata1 = System.IO.File.ReadAllBytes(SecureFile);
byte[] iv = new byte[16]; // Separate IV and ciphertext
byte[] ciphertext = new byte[edata1.Length - iv.Length];
Array.Copy(edata1, 0, iv, 0, iv.Length);
Array.Copy(edata1, iv.Length, ciphertext, 0, ciphertext.Length);
...
Aes encAlg = Aes.Create(); // Remove this line
...
decAlg.IV = iv; // Use the separated IV
...
decrypt.Write(ciphertext, 0, ciphertext.Length); // Use the separated ciphertext
A few remarks:
For each encryption a new, random salt should be generated and concatenated with the ciphertext analogous to the IV. During decryption, the salt can then be determined analogous to IV. Consider additionally RFC8018, sec 4.1.
The iteration count slows down the derivation of the key, which should make an attack by repeated attempts more difficult. Therefore the value should be as large as possible. Consider additionally RFC8018, sec 4.2.
Authentication data (i.e. passwords) are not encrypted, but hashed, here.
I have simple symmetric encrypt/decrypt application that works fine:
namespace Crypto
{
class Program
{
public static void EncryptSomeText()
{
string original ="My secretdata!";
using (SymmetricAlgorithm symmetricAlgorithm =
new AesManaged())
{
byte[] encrypted = Encrypt(symmetricAlgorithm, original);
string roundtrip = Decrypt(symmetricAlgorithm, encrypted);
// Displays: My secret data!
Console.WriteLine("Original:{ 0}", original);
Console.WriteLine("RoundTrip:{ 0}", roundtrip);
}
}
static byte[] Encrypt(SymmetricAlgorithm aesAlg, string plainText)
{
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt =
new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
return msEncrypt.ToArray();
}
}
}
static string Decrypt(SymmetricAlgorithm aesAlg, byte[] cipherText)
{
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt =
new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
static void Main(string[] args)
{
SymmetricAlgorithm sma = SymmetricAlgorithm.Create();
byte[] b = Encrypt(sma,"bla bla");
string s= Decrypt(sma, b);
Console.WriteLine("Decrypted {0}", s);
}
}
}
I'm just wondering regarding key creation. Does it means that it is created in automatic way during symmetric algorithm creation? Is it generated each time different? I suppose, user should pass key for this purpose.
SymmetricAlgorithm sma = SymmetricAlgorithm.Create();
Random key is generated when you access Key property of your SymmetricAlgorithm, assuming you did not set that Key before that to some predefined key. It's not generated during construction of SymmetricAlgorithm itself.
var sma = SymmetricAlgorithm.Create();
// no key generated here yet
var key = sma.Key; // generated key
// accessing `Key` causes random key generation
var sma = SymmetricAlgorithm.Create();
// no key here
sma.Key = myKey;
// you set the key, so no random key is generated
The same is true for IV (nonce) value.
Generating random key makes sense only once, then you have to share it between encrypting\decrypting party in some safe way and use it for future encryptions\decryptions. While you are there, note that IV value should be random for each encryption, so usually you just prepend that IV value to the encrypted binary array and before decrypting you cut that IV from the beginning of encrypted array and use for decryption.
On this github page there is utility class written in Java which performs encryption/decryption using symmetric algorithm, below could be one of encryption flows using symmetric algorithm :
generate secret key using the selected algorithm (DES, 3DES, AES etc)
generate secure random number of seed bytes which is computed with available the seed generation algorithm
probably text message to be encrypted is not the multiples of 8 byte blocks that's why message must be padded with additional bytes to make the text message to be multiples of 8-byte blocks.(e.g. PKCS5Padding padding scheme )
use random generated secure seed bytes as initialization vector as block cipher
initialize cipher function with the symmetric key and block cipher
finish encryption
apply binary to text encoding with selected standard for initialization vector(IV) and encrypted binary data
define message format before sending in order to split the message into IV and encrypted for decryption on recipient side
I'm trying to encrypt a stream and decrypt it again. When encrypting, I store the salt and IV (8 and 16 bytes) first into the target stream. When decrypting, I get a padding error in the line where CopyTo() is called. The full source of the class can be found in a Gist.
The relevant code snippet for encryption is:
// Set position to start of stream.
encryptedOutStream.Seek (0, SeekOrigin.Begin);
// Store the salt in the output stream. The salt is not a secret. Salt is used to generate different keys for identical passwords.
var keyInfo = GenerateKey (password);
encryptedOutStream.Write (keyInfo.Salt, 0, keyInfo.Salt.Length);
// Store the IV in the output stream. The IV is randomly generated if not set explicitly. It is not a secret and used to create
// different encrypted output for identical plaintext input when using CBC cipher mode.
encryptedOutStream.Write (aesAlgo.IV, 0, aesAlgo.IV.Length);
// Let the algorithm know our key.
aesAlgo.Key = keyInfo.Key;
// Get an encrypting ICryptoTransform interface from the algorithm.
using(var cryptoTransform = aesAlgo.CreateEncryptor ())
// Pump the input stream through a crypto stream wrapping a memory stream.
using(var encryptionStream = new CryptoStream(encryptedOutStream, cryptoTransform, CryptoStreamMode.Write))
{
plainInStream.CopyTo (encryptionStream);
}
and for decryption:
// Read the salt.
byte[] salt = new byte[8];
encryptedInStream.Read (salt, 0, 8);
// Read the IV.
byte[] iv = new byte[16];
encryptedInStream.Read (iv, 0, 16);
aesAlgo.IV = iv;
// Generate the key from the password and the salt.
var keyInfo = GenerateKey (password, salt);
aesAlgo.Key = keyInfo.Key;
// Get a decrypting ICryptoTransform interface from the algorithm.
using(var cryptoTransform = aesAlgo.CreateDecryptor ())
// Pump the input stream through a crypto stream wrapping a memory stream.
using(var decryptionStream = new CryptoStream(encryptedInStream, cryptoTransform, CryptoStreamMode.Read))
{
decryptionStream.CopyTo (decryptedOutStream);
}
I suspect an issue involving the EncryptString and DecryptString methods, specifically the lines:
encryptedString = Encoding.UTF8.GetString(encryptedOutStream.ToArray());
and
using (var encryptedInStream = new MemoryStream(Encoding.UTF8.GetBytes(s)))
Effectively, this code incorrectly attempts to use text encoding on ciphertext, which is binary data. This will introduce errors when the binary data does not happen to be a legal UTF8 sequence, corrupting the ciphertext and introducing padding issues. Instead, a binary encoding method needs to be used (most simply, base64).
To correct the issue, change the above lines to:
// change line 281:
// encryptedString = Encoding.UTF8.GetString(encryptedOutStream.ToArray());
// to:
encryptedString = Convert.ToBase64String(encryptedOutStream.ToArray());
// change line 251:
// using (var encryptedInStream = new MemoryStream(Encoding.UTF8.GetBytes(s)))
// to:
using (var encryptedInStream = new MemoryStream(Convert.FromBase64String(s)))
With this change, the conversion appears to work. A simple driver to encrypt and then decrypt the plaintext "payload" with the password "password" prints the desired output:
string password = "password";
SymmetricCrypto c = new SymmetricCrypto();
string ct = c.EncryptString("payload", password);
Console.WriteLine(ct); // prints sLSZfzVQGCoML29... (ciphertext will vary)
string dt = c.DecryptString(ct, password);
Console.WriteLine(dt); // prints "payload"
I am experiencing a strange problem with RijndaelManaged. Basically I have a new instance of it where I set up the CipherMode, Padding, IV and Key. Then I create another instance and assign the same values of the following properties from the original instance to the 2nd instance: Mode, Padding, KeySize, FeedbackSize, BlockSize, IV and Key.
So, having copied ALL property values from instance 1 to instance 2 I should get the same results, right? WRONG! the GetHashCode() of both instances differ somehow, but if I dump their properties (named above) then they are all the same.
If I encrypt a string of text with a length equal to the block size (16 bytes, 128 bits) then both produce the same result, if the input is less than BlockSize then the encryption results are NOT the same.
I have this to create the initial Rijndael instance.
public static RijndaelManaged CreateSymmetricKey(string passphrase)
{
RijndaelManaged symCrypto = new RijndaelManaged();
symCrypto.Mode = CipherMode.CBC;
symCrypto.Padding = PaddingMode.PKCS7;
byte[] salt = Encoding.UTF8.GetBytes("dummy dummy dummy dummy test");
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(passphrase, salt);
symCrypto.Key = key.GetBytes(symCrypto.KeySize / 8);
symCrypto.IV = key.GetBytes(symCrypto.BlockSize / 8);
return symCrypto;
}
To encrypt the string for the sake of an example:
private string Encrypt(RijndaelManaged rm, string text)
{
byte[] encrypted;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rm.CreateEncryptor(rm.Key, rm.IV);
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(text);
}
encrypted = msEncrypt.ToArray();
}
}
return BitConverter.ToString(encrypted);
}
So then do this
RijndaelManaged rm1 = CreateSymmetricKey("there is something weird happening");
RijndaelManaged rm2 = new RijndaelManaged();
// copy ALL public properties to the new instance so that it has the same parameters
rm2.BlockSize = rm1.BlockSize; // 128
rm2.FeedbackSize = rm1.FeedbackSize; // 128
rm2.KeySize = rm1.KeySize; // 256
rm2.Mode = rm1.Mode; // CBC
rm2.Padding = rm1.Padding; // PKCS7
rm2.IV = rm1.IV;
rm2.Key = rm1.Key;
// Encryption
string cypher1 = Encrypt(rm1, "this is a test 6"); // length equal to BlockSize
string cypher2 = Encrypt(rm2, "this is a test 6"); // length equal to BlockSize
string cypher11 = Encrypt(rm1, "this is a test"); // length less than BlockSize
string cypher21 = Encrypt(rm2, "this is a test"); // length less than BlockSize
I am getting that cyper1 == cypher2 and cypher11 != cypher21 also rm1.GetHashCode() != rm2.GetHashCode() yet all public parameters are the same!
I also dumped all the public properties of both instances to see if I was missing something but not, all values are the same.
You should not use GetHashCode() in this way. For classes that did not override the base object.GetHashCode() implementation, it will return the integer handler of this specific instance.
Since the handle for two different instances will always be different this will never match.
Also GetHashCode() never actually guarantees uniqueness, it's merely meant to be used as a light weight pre check, before the actual equality is tested.
This is heavily used in any kind of Hashing data structure like Dictionary etc.
For more info on this topic:
http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx
I also executed your code and for me the following was the case:
cyper1 == cypher2 and cypher11 == cypher21
I'm pretty sure the issue was the comparison of GetHashCode().
I'm trying to store a password in a file that I'd like to retrieve for later. Hashing is not an option as I need the password for connecting to a remote server for later.
The following code works well, but it creates a different output each time even though the key is the same. This is bad as when the application shuts down and restarts I won't be able to retrieve my password any more. How can I store passwords in a file and retrieve them later?
public class EncyptDecrypt {
static System.Security.Cryptography.TripleDESCryptoServiceProvider keyProv = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
public static System.Security.Cryptography.TripleDESCryptoServiceProvider KeyProvider {
get {
keyProv.Key = new byte[] { /* redacted with prejudice */ };
return keyProv;
}
}
public static string Encrypt(string text, SymmetricAlgorithm key) {
if (text.Equals(string.Empty)) return text;
// Create a memory stream.
MemoryStream ms = new MemoryStream();
// Create a CryptoStream using the memory stream and the
// CSP DES key.
CryptoStream encStream = new CryptoStream(ms, key.CreateEncryptor(), CryptoStreamMode.Write);
// Create a StreamWriter to write a string
// to the stream.
StreamWriter sw = new StreamWriter(encStream);
// Write the plaintext to the stream.
sw.WriteLine(text);
// Close the StreamWriter and CryptoStream.
sw.Close();
encStream.Close();
// Get an array of bytes that represents
// the memory stream.
byte[] buffer = ms.ToArray();
// Close the memory stream.
ms.Close();
// Return the encrypted byte array.
return System.Convert.ToBase64String(buffer);
}
// Decrypt the byte array.
public static string Decrypt(string cypherText, SymmetricAlgorithm key) {
if (cypherText.Equals(string.Empty)) return cypherText;
string val;
try {
// Create a memory stream to the passed buffer.
MemoryStream ms = new MemoryStream(System.Convert.FromBase64String(cypherText));
// Create a CryptoStream using the memory stream and the
// CSP DES key.
CryptoStream encStream = new CryptoStream(ms, key.CreateDecryptor(), CryptoStreamMode.Read);
// Create a StreamReader for reading the stream.
StreamReader sr = new StreamReader(encStream);
// Read the stream as a string.
val = sr.ReadLine();
// Close the streams.
sr.Close();
encStream.Close();
ms.Close();
}
catch (System.Exception) {
return string.Empty;
}
return val;
}
}
I believe that what's happening is that the crypto provider is randomly generating an IV. Specify this and it should no longer differ.
Edit: You can do this in your 'keyProvider' by setting the IV property.
According to the docs of CreateEncryptor:
If the current IV property is a null
reference (Nothing in Visual Basic),
the GenerateIV method is called to
create a new random IV.
This will make the ciphertext different every time.
Note: a way around this is discussed here where I suggest you can prepend the plaintext with a mac ... then the first block of ciphertext is effectively the IV, but it's all repeatable
You need to specify an IV (initialization vector), even if you generate a random one. If you use random IV then you must store it along with the ciphertext so you can use it later on decryption, or you can derive an IV from some other data (for example if you're encrypting a password, you can derive the IV from the username).