I'm trying to save a serialized object to an encrypted file. This isn't production quality and I am aware of the security risks with the way that I am doing this, but ignoring those I will have a key in a resource (data.Settings.key) that wont change and I have a salt that is also a constant.
My encryption seems to work, but decryption returns me an Exception saying that padding is invalid and cannot be closed when I try to close my CryptoStream.
private static byte[] decrypt(byte[] bytes)
{
var decryptor = algorithm.CreateDecryptor();
using (var sMemoryStream = new MemoryStream())
using (var sCryptoStream = new CryptoStream(sMemoryStream, decryptor, CryptoStreamMode.Write))
{
sCryptoStream.Write(bytes, 0, bytes.Length);
sCryptoStream.Close();
return sMemoryStream.ToArray();
}
}
The algorithm variable is the same one that the encrypt method uses and is built by this method which is called in the classes constructor:
private static SymmetricAlgorithm GetAlgorithm()
{
var algorithm = Rijndael.Create();
// Create key from salt and password in config
var rdb = new Rfc2898DeriveBytes(data.Settings.key, new byte[] {
0x44,0x61,0x79,0x6e,0x65,0x44,0x6f,0x75,0x67,0x61,0x6e
});
algorithm.Padding = PaddingMode.PKCS7;
// Set key and IV from rdb
algorithm.Key = rdb.GetBytes(32);
algorithm.IV = rdb.GetBytes(16);
return algorithm;
}
I've tried changing the padding mode in the algorithm but I can't understand why it's fine with this padding when encrypting, but now when decrypting.
If it helps here is the method that calls the decrypt method:
private static User OpenFile(String sUserName)
{
Console.WriteLine("Opening file...");
using (Stream sFileStream = new FileStream(data.Settings.dir + "data\\accounts\\" + sUserName + ".dat",
FileMode.Open, FileAccess.Read, FileShare.None))
using (Stream sMemoryStream = new MemoryStream())
{
// Read from File to memory stream
sFileStream.CopyTo(sMemoryStream);
// Decrypt data and store in new memory stream
byte[] bytes = new byte[sMemoryStream.Length];
Console.WriteLine("\tb:" + bytes.Length);
bytes = decrypt(bytes);
Console.WriteLine("\ta:" + bytes.Length);
Stream stream = new MemoryStream(bytes);
Console.WriteLine("\ts:" + bytes.Length);
// Deserialise memory stream and return as User object
User user = (User)bfFormatter.Deserialize(stream);
stream.Close();
return user;
}
}
Related
I have encryption decryption code which is perfectly working fine in local. From local system using code first approach I created database in aws which created successfully with seeds values in which I have decrypted the password.
Now, I have published the .net6 application in aws ec2 instance. On logging it is giving error of incorrect credentials.
I have logged the username and passwords and rechecked the scenario. The issue I have found is the encryption is changed.
I have updated the password and successfully logged in. But now the problem is with roles. I have applied checks on encrypted role ids which are not maching now.
Can anyone please help me here on this issue please?
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public static class EncyptionDcryption
{
static string key = "85OIbnI9";
static string vector = "eH90BDl0";
////////////////////////////////////////////////////////////////////////////////
// Decryption
////////////////////////////////////////////////////////////////////////////////
public static string GetDecryptedText(string txt)
{
txt = txt.Replace(' ', '+');
DESCryptoServiceProvider key = new DESCryptoServiceProvider();
key.Key = ASCIIEncoding.ASCII.GetBytes(key); // decryption key
key.IV = ASCIIEncoding.ASCII.GetBytes(vector);// initialization vector
int length = txt.Length;
byte[] buffer = new byte[length];
buffer = Convert.FromBase64String(txt);
string decText = Decrypt(buffer, key);
return decText;
}
public static string Decrypt(byte[] CypherText, SymmetricAlgorithm key)
{
// Create a memory stream to the passed buffer.
MemoryStream ms = new MemoryStream(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.
string val = sr.ReadLine();
// Close the streams.
sr.Close();
encStream.Close();
ms.Close();
return val;
}
////////////////////////////////////////////////////////////////////////////////
// Encryption
////////////////////////////////////////////////////////////////////////////////
public static byte[] Encrypt(string PlainText, SymmetricAlgorithm key)
{
// 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(PlainText);
// 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 buffer;
}
public static string GetEncryptedText(string txt)
{
DESCryptoServiceProvider key = new DESCryptoServiceProvider();
key.Key = ASCIIEncoding.ASCII.GetBytes(key); // decryption key
key.IV = ASCIIEncoding.ASCII.GetBytes(vector);// initialization vector
// Encrypt a string to a byte array.
byte[] buffer = Encrypt(txt, key);
string encText;
encText = Convert.ToBase64String(buffer);
return encText;
}
}
}
`
Why it behaves differently on server and local? But no clue.
I have decrypted and saved an mp3 file into a blob storage.
However, when I decrypt and download the file I cant play it. I used an Mp3 validation tool which says "unknown file format". I believe it is the decryption that does not work since it works to download an unencrypted Mp3 file. Below I first show the encryption code within its Azure webjob function. The I show the decryption method and the method using it. I have removed handling of keys and such or clarity.
Encrypt
public static void EncryptBlob(
[BlobTrigger("callstest/{name}")]
[Blob("callstest/{name}", FileAccess.Read)] Stream blobInput,
[Blob("encryptedcalls/{name}.vega", FileAccess.Write)] Stream blobOutput)
{
try
{
var password = "myKey123";
var ue = new UnicodeEncoding();
var key = ue.GetBytes(password);
var rmCrypto = new RijndaelManaged {Padding = PaddingMode.None};
using (var cs = new CryptoStream(blobOutput,
rmCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write))
{
int data;
while ((data = blobInput.ReadByte()) != -1)
cs.WriteByte((byte)data);
}
}
catch
{
Trace.TraceError("an error occured during encryption of the file-get the name?");
}
}
AdminController
public async Task<ActionResult> DownloadMp3FromUrl()
{
var file = await _recordingService.GetRecordingFromUrl();
var fileName = "filetest.mp3";
return File(file,"audio/mpeg", fileName);
}
Recording Service handler
public async Task<byte[]> GetRecordingFromUrl()
{
var container = _blobClient.GetContainerReference("encryptedcalls");
var blockBlob = container.GetBlockBlobReference("SearchFiles.mp3.vega");
try
{
var password = "myKey123";
var ue = new UnicodeEncoding();
var key = ue.GetBytes(password);
var rmCrypto = new RijndaelManaged { Padding = PaddingMode.None };
using (var stream = new MemoryStream())
{
blockBlob.FetchAttributes();
blockBlob.DownloadToStream(stream, null, null);
using (var cs = new CryptoStream(stream, rmCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read))
{
int data;
while ((data = stream.ReadByte()) != -1)
cs.WriteByte((byte)data);
return stream.ToArray();
}
}
}
catch
{
Trace.TraceError("an error occured during encryption of the file-get the name?");
}
return null;
}
You're trying to write the decrypted data back into the source-stream in your Recording Service handler. This will never work. I'm amazed this doesn't throw an exception.
You need to set up your input stream, pass it into a decrypting CryptoStream, then write that to another output stream:
using (var inStream = new MemoryStream())
using (var outStream = new MemoryStream())
{
blockBlob.FetchAttributes();
blockBlob.DownloadToStream(inStream, null, null);
using (var cryptoStream = new CryptoStream(
inStream, rmCrypto.CreateDecryptor(key, key), CryptoStreamMode.Read))
{
cryptoStream.CopyTo(outStream);
return outStream.ToArray();
}
}
As an aside, the implementation as you've presented it here is full of security issues:
Don't use a non-padded cipher. You can leak information this way.
Don't generate your key from a password. Use a cryptographically secure RNG to generate your keys.
If you must use a string as your key's password, use Rfc2898DeriveBytes to generate a cryptographically secure random key from the password.
Absolutely do not use your symmetric key as your IV. This is really, really bad practice. The IV is used to randomize the cipher's output - it is not a secret in the same way as the key, and should be unique to each 'message' (or file) being encrypted.
I have created a few little programs that export data to a text file using StreamWriter and then I read them back in using StreamReader. This works great and does what I need it to do but I was wondering if there was a way that I could save this information without the user being able to access or modify it either intentionally or unintentionally. An example of something I would have in a text file would be if a checkbox was ticked, when you tick it it outputs "Ticked" to a text file, when the program is re - opened I know what state the form was in when it was closed. I obviously don't want to keep using text files. Does anyone have any ideas on how I can easily store this information without the user being able to modify it? Thank you very much.
The simplest way is to Base-64 encode/decode this text. This is not secure, but will prevent a casual user from modifying the data.
static public string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes
= System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
string returnValue
= System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
static public string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.ASCIIEncoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
EDIT: Real encryption
#region Encryption
string passPhrase = "Pasword"; // can be any string
string saltValue = "sALtValue"; // can be any string
string hashAlgorithm = "SHA1"; // can be "MD5"
int passwordIterations = 7; // can be any number
string initVector = "~1B2c3D4e5F6g7H8"; // must be 16 bytes
int keySize = 256; // can be 192 or 128
private string Encrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write);
stream2.Write(buffer, 0, buffer.Length);
stream2.FlushFinalBlock();
byte[] inArray = stream.ToArray();
stream.Close();
stream2.Close();
return Convert.ToBase64String(inArray);
}
private string Decrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream(buffer);
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read);
byte[] buffer5 = new byte[buffer.Length];
int count = stream2.Read(buffer5, 0, buffer5.Length);
stream.Close();
stream2.Close();
return Encoding.UTF8.GetString(buffer5, 0, count);
}
#endregion
You should call ProtectedData.Protect to encrypt the data using a per-user key.
Note that it wouldn't be very hard for a skilled user to decrypt and modify the data.
Anything that your program does on the user's machine can be done by the user too.
You can add a checksum or hash to the file - if the file contents doesn't agree with the checksum, you know it was tampered with.
If it is important that users can't read the contents of the file, you can encrypt it.
I don't believe you can make a file that can't be tampered with (a savvy user could use a hex editor and change it, for example) - the best you can do is detect such tampering.
You can use the Ionic zip libraries to zip those text files. If necessary you could also use features of Ionic zip like password protection and encryption. And you'll still be able to open the file (with zipping applications like, for example, 7zip) manually yourself using the same settings you used to create it in the first place.
If a program can access the information, a user usually can too. However you can produce data the user will not immediately understand.
I would start by creating a class that holds all state information you want to save, isolating the problem. Coincidentally, the BinaryFormatter class will then allow you to easily save and load this class to/from a file. I don't know if it's results are "unreadable enough" - if not, apply Base64 encoding like Leon mentioned.
While you could base64 encode or even fully encrypt your configuration data (with SHA1 or MD5) as already suggested, I think good practice would be to work with the framework classes dealing with configuration data (Configuration under the System.Configuration namespace) and it's built in ability to encrypt data (via the ProtectSection method of the ConfigurationSection class).
First of all you should declare and initialize an instance:
using System.Configuration;
...
static void Main(string[] args)
{
Configuration config;
config = ConfigurationManager.OpenExeConfiguration(/*path to config file*/); //Use ConfigurationManager.OpenMachineConfiguration(/*path to config file*/) when opening machine configuration
...
After that you need to define a custom configuration section that defines your configuration (msdn example)
Once you've done that you just need to initialize an instance of your custom configuration section and add it to the configuration file using this code:
isTicked = config.Sections.Add("isTicked", customSection);
To encrypt the section you just added use this code (with further examples in both VB.NET and C# found here):
config.Sections["isTicked"].SectionInformation.ProtectSection("protection provider");
The "DPAPIProtectedConfigurationProvider" and "RSAProtectedConfigurationProvider" are built in by default.
Once you want to decrypt the section use this code:
config.Sections["isTicked"].SectionInformation.UnprotectSection();
To stress a point - encryption and decryption both take effect only after you save the configuration file
To save the file, use the code:
config.Save(); //config.SaveAs("string") is also available
Further information about the relevant classes and methods can be found in the msdn, starting with the Configuration class page linked above.
Try this code to encrypt and decrypt your text!
It is quite easy and strong I think...
public static class Crypto
{
private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
public static string Encrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream encryptionStream = new MemoryStream())
{
using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] cleanText = Encoding.UTF8.GetBytes(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption byte data size: ", cleanText.Length.ToString()));
encrypt.Write(cleanText, 0, cleanText.Length);
encrypt.FlushFinalBlock();
}
byte[] encryptedData = encryptionStream.ToArray();
string encryptedText = Convert.ToBase64String(encryptedData);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", encryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
return encryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
public static string Decrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream decryptionStream = new MemoryStream())
{
using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] encryptedData = Convert.FromBase64String(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
decrypt.Write(encryptedData, 0, encryptedData.Length);
decrypt.Flush();
}
byte[] decryptedData = decryptionStream.ToArray();
string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption text data size: ", decryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption byte data size: ", decryptedData.Length.ToString()));
return decryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
}
Just to add another implementation of Leon's answer, and following the
Microsoft docs
Here a class example that encrypts and decrypts strings
public static class EncryptionExample
{
#region internal consts
internal const string passPhrase = "pass";
internal const string saltValue = "salt";
internal const string hashAlgorithm = "MD5";
internal const int passwordIterations = 3; // can be any number
internal const string initVector = "0123456789abcdf"; // must be 16 bytes
internal const int keySize = 64; // can be 192 or 256
#endregion
#region public static Methods
public static string Encrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
byte[] inArray = null;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Write))
{
csEncrypt.Write(buffer, 0, buffer.Length);
csEncrypt.FlushFinalBlock();
inArray = msEncrypt.ToArray();
res = Convert.ToBase64String(inArray);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Encrypt " + ex);
}
return res;
}
public static string Decrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
using (MemoryStream msEncrypt = new MemoryStream(buffer))
{
using (CryptoStream csDecrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
res = srDecrypt.ReadToEnd();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Decrypt " + ex);
}
return res;
}
}
By the way, here is the "salt value" definition that I had googled to find out what it was.
Salt value
If an attacker does not know the password, and is trying to guess it with a brute-force attack, then every password he tries has to be tried with each salt value. So, for a one-bit salt (0 or 1), this makes the encryption twice as hard to break in this way.
Preventing unintentional string modification can be done using a checksum, as pointed in this answer.
However, it's quite easy to generate such a checksum, as they are not that many widely used algorithms.
Thus that doesn't protect you against intentional modification.
To prevent that, people use digital signatures. That allows anyone to verify your data hasn't be tampered, but only you (the owner of the private secret) can generate the signature.
Here is an example in C#.
However, as others pointed out, you need to embed your private key somewhere in your binary, and a (not so) skilled programmer will be able to retrieve it, even if you obfuscate your .net dll or you make that in a separate native process.
That would be enough for most concerns though.
If you are really concerned by security, then you need to move on the cloud, and execute the code on a machine you own.
I'm getting a bit desperate here. I'm trying to write an encrypted file with a serialized object to disk and later retrieve the file, decrypt it and deserialize the object back.
UPDATE:
I refactored the code to this:
using (Stream innerStream = File.Create(this.GetFullFileNameForUser(securityContext.User, applicationName)))
{
using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateEncryptor(), CryptoStreamMode.Write))
{
// 3. write to the cryptoStream
//BinaryFormatter bf = new BinaryFormatter();
//bf.Serialize(cryptoStream, securityContext);
XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
xs.Serialize(cryptoStream, securityContext);
}
}
using (Stream innerStream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open))
{
using (Stream cryptoStream = new CryptoStream(innerStream, GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read))
{
//BinaryFormatter bf = new BinaryFormatter();
//return (SecurityContextDTO)bf.Deserialize(cryptoStream);
XmlSerializer xs = new XmlSerializer(typeof(SecurityContextDTO));
//CryptographicException here
return (SecurityContextDTO)xs.Deserialize(cryptoStream);
}
}
Now I'm getting a cryptographic exception on deserialize: Bad Data
ORIGINAL:
I'm doing this:
public void StoreToFile(SecurityContextDTO securityContext, string applicationName)
{
if (securityContext.LoginResult.IsOfflineMode == false)
{
Stream stream = null;
CryptoStream crStream = null;
try
{
TripleDESCryptoServiceProvider cryptic = GetCryptoProvider();
stream = File.Open(this.GetFullFileNameForUser(securityContext.User, applicationName), FileMode.Create);
crStream = new CryptoStream(stream,
cryptic.CreateEncryptor(), CryptoStreamMode.Write);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(crStream, securityContext);
}
catch(Exception)
{
throw;
}
finally
{
if (crStream != null)
crStream.Close();
}
}
}
public SecurityContextDTO RetrieveFromFile(UserDTO user,string applicationName)
{
SecurityContextDTO objectToSerialize;
Stream stream = null;
CryptoStream crStream=null;
try
{
stream = File.Open(this.GetFullFileNameForUser(user, applicationName), FileMode.Open);
crStream= new CryptoStream(stream,
GetCryptoProvider().CreateDecryptor(), CryptoStreamMode.Read);
BinaryFormatter bFormatter = new BinaryFormatter();
//Exception here
objectToSerialize = (SecurityContextDTO)bFormatter.Deserialize(crStream);
}
catch (Exception)
{
objectToSerialize = null;
}
finally
{
if (crStream!=null)
crStream.Close();
}
return objectToSerialize;
}
private static TripleDESCryptoServiceProvider GetCryptoProvider()
{
TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
try
{
cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);
Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
cryptic.IV = db.GetBytes(8);
}
catch (Exception)
{
throw;
}
finally
{
cryptic.Dispose();
}
return cryptic;
}
Encrypting and writing works fine, the file appears on the disk and the content is there (encrypted of course). But when I call the retrieve method I always get a SerializationException
Binary stream '30' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization.
When I leave the cryptographic methods out everything works fine.
So,
You realize that in this code
private static TripleDESCryptoServiceProvider GetCryptoProvider()
{
TripleDESCryptoServiceProvider cryptic = new TripleDESCryptoServiceProvider();
try
{
cryptic.Key = ASCIIEncoding.ASCII.GetBytes(CrypKey);
Rfc2898DeriveBytes db = new Rfc2898DeriveBytes("sdddsdsd", 8);
cryptic.IV = db.GetBytes(8);
}
catch (Exception)
{
throw;
}
finally
{
cryptic.Dispose(); // <------- Don't do this until you are done decrypting.
}
return cryptic;
}
you will ALWAYS dispose of the provider meaning you are always using a random key and iv
You are close. However, the stream you pass into the creation of CryptoStream is always, always, always the buffer that will hold your end result. It is not the stream that holds the data you want to encrypt or decrypt. I put the emphasis in there because I remember learning this for the first time and I did exactly what you were doing. So here:
// this is for encryption
var memStreamEncryptedData = new MemoryStream();
var encryptStream = new CryptoStream(memStreamEncryptedData,
transform, CryptoStreamMode.Write);
// this is for decryption
var memStreamDecryptedData = new MemoryStream();
var decryptStream = new CryptoStream(memStreamDecryptedData,
transform, CryptoStreamMode.Write);
Notice in both cases, CryptoStream is being initialized with a blank output stream. Your stream does not enter into the picture until later. So, during a write, you will do the following:
encryptStream.Write(dataToBeEncrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamEncryptedData now safely holds your encrypted data
And during the read, you will:
decryptStream.Write(dataToBeDecrypted);
encryptStream.FlushFinalBlock();
encryptStream.Close();
// memStreamDecryptedData now safely holds your decrypted data
So, to save you some trouble, here's a nice simple Symmetric method that will perform both encryption and decryption. The only difference between this and yours is that I am working directly on byte arrays, but perhaps that augmentation can be an exercise:
public static byte[] Symmetric(bool encrypt, byte[] plaintext, string ikey)
{
if (plaintext.Length == 0) return plaintext;
// setting up the services can be very expensive, so I'll cache them
// into a static dictionary.
SymmetricSetup setup;
if (!_dictSymmetricSetup.TryGetValue(ikey, out setup))
{
setup = new SymmetricSetup();
setup.des = new DESCryptoServiceProvider { Mode = CipherMode.CBC,
Padding = PaddingMode.Zeros };
setup.hash = Hash(Encoding.ASCII.GetBytes(ikey));
setup.key = setup.hash.ForceLength(8, 0);
setup.IV = Encoding.ASCII.GetBytes("init vec");
setup.des.Key = setup.key;
setup.des.IV = setup.IV;
setup.encrypt = setup.des.CreateEncryptor(setup.des.Key, setup.des.IV);
setup.decrypt = setup.des.CreateDecryptor(setup.des.Key, setup.des.IV);
_dictSymmetricSetup[ikey] = setup;
}
var transform = encrypt ? setup.encrypt : setup.decrypt;
var memStreamEncryptedData = new MemoryStream();
var encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Write);
if (encrypt)
encStream.Write(new[] {(byte) ((8 - (plaintext.Length + 1)%8)%8)}, 0, 1);
encStream.Write(plaintext, 0, plaintext.Length);
encStream.FlushFinalBlock();
encStream.Close();
memStreamEncryptedData.Flush();
var ciphertext = memStreamEncryptedData.ToArray();
byte b;
if (!encrypt)
if (byte.TryParse("" + ciphertext[0], out b))
ciphertext = ciphertext.Skip(1).Take(ciphertext.Length - b - 1).ToArray();
return ciphertext;
}
And to call it, you might do something like this:
static public byte[] DecryptData(this byte[] source, string password) {
return Symmetric(false, source, password);
}
static public byte[] EncryptData(this byte[] source, string password) {
return Symmetric(true, source, password);
}
Again, you'll do something slightly different to work with streams, but hopefully you get the gist. Instead of MemoryStream, it will be whatever stream you need to feed into your serializer.
Some previous posts that can be of use:
How do I encrypt a string in vb.net using RijndaelManaged, and using PKCS5 padding?
Does BinaryFormatter apply any compression?
In later, you can see how I stacked compression with encryption with serialization. And it works.
I'm using DES-ECB + base64 encryption in my application. That's the code of the class I called "Crypto"
public class Crypto
{
public static string Decrypt(string encryptedString)
{
DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider();
desProvider.Mode = CipherMode.ECB;
desProvider.Padding = PaddingMode.PKCS7;
desProvider.Key = Encoding.ASCII.GetBytes("e5d66cf8");
using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(encryptedString)))
{
using (CryptoStream cs = new CryptoStream(stream, desProvider.CreateDecryptor(), CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs, Encoding.ASCII))
{
return sr.ReadToEnd();
}
}
}
}
public static string Encrypt(string decryptedString)
{
DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider();
desProvider.Mode = CipherMode.ECB;
desProvider.Padding = PaddingMode.PKCS7;
desProvider.Key = Encoding.ASCII.GetBytes("e5d66cf8");
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(stream, desProvider.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] data = Encoding.Default.GetBytes(decryptedString);
cs.Write(data, 0, data.Length);
return Convert.ToBase64String(stream.ToArray());
}
}
}
}
but when I encrypt a string, then decrypt it again and encrypt one more time, the encrypted string is not the same as previous encrypted was. So that's the first encrypted string:
kEN0HUp/dqz8kXA7nYivJG6Jl3haLJjhBq1UfEtQTwaPwizW//03M0UxF8dBuYZo2BoZ5vsVcXRJF1LpFZLWxDsdeKAC43L2K2OoYRxTn/dA6KmM13YS9xOezGiROQfVj5qrkdokJRCvj0gYfFoH2oeDGyN+EAw5Dgzsp697kj4=
and here comes the second encrypted string:
kEN0HUp/dqz8kXA7nYivJG6Jl3haLJjhBq1UfEtQTwaPwizW//03M0UxF8dBuYZo2BoZ5vsVcXRJF1LpFZLWxDsdeKAC43L2K2OoYRxTn/dA6KmM13YS9xOezGiROQfVj5qrkdokJRCvj0gYfFoH2oeDGyN+EAw5
They are almost same, except this "Dgzsp697kj4=" in the first string.
What's wrong?
Thanks in advance.
You are losing data. In your Encrypt() method you need to call EncryptFinalBlock() to let the padding algorithm know that you are done so that it can add the padding:
using (CryptoStream cs = new CryptoStream(stream, desProvider.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] data = Encoding.Default.GetBytes(decryptedString);
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock(); // <-- Add this
return Convert.ToBase64String(stream.ToArray());
}
I had a similar problem. You should check that white space is not getting appended to the end of the decrypted string. You might need to trim the white space off.