I have a WPF application contains this class:*
{[Serializable]
public class Parametres
{
private string admin_login;
private string admin_pwd;
private string server;
private string db;
private string user;
private string pwd;}
i serialize an object with this function:
public static void Serialize_Parametres(string filename, Parametres obj)
{
using (FileStream fs = File.Open(filename, FileMode.OpenOrCreate))
{
using (CryptoStream cs = new CryptoStream(fs, key.CreateEncryptor(), CryptoStreamMode.Write))
{
XmlSerializer xmlser = new XmlSerializer(typeof(Parametres));
xmlser.Serialize(cs, obj);
}
}
}
it's works fine and it generates a file .txt , but when i try to deserialize this file and get the object parametres whith this function:
public static Parametres DeSerialize_Parametres(string filename)
{
using (FileStream fs = File.Open(filename, FileMode.Open))
{
using (CryptoStream cs = new CryptoStream(fs, key.CreateDecryptor(), CryptoStreamMode.Read))
{
XmlSerializer xmlser = new XmlSerializer(typeof(Parametres));
return (Parametres)xmlser.Deserialize(cs);
}
}
}
i got this error Length of the data to decrypt is invalid in the line return (Parametres)xmlser.Deserialize(cs);
What is exactly the reason of this error? how can i fix it?
When using this techique to serialize an object you have to do in two parts. The length encrypted stream must be stored as part of the final stream and its up to you to do this. However, you should break this up into a more resuable form.
For example, first serialze the graph you want into a byte stream; then
Encrypte the byte stream; then
Save it to a file.
Below is an example on how to Serialize to a file using AES:
public class ObjectXmlSerializer
{
//---------------------------------------------------------------------
public override Byte[] Serialize(Object obj)
{
using (MemoryStream ms = new MemoryStream())
{
new XmlSerializer(obj.GetType()).Serialize(ms, obj);
return ms.ToArray();
}
}
//---------------------------------------------------------------------
public override T Deserialize<T>(Byte[] bObj)
{
using (MemoryStream ms = new MemoryStream(bObj))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(ms);
}
}
//---------------------------------------------------------------------
public override T Deserialize<T>(Stream iostream)
{
return (T)new XmlSerializer(typeof(T)).Deserialize(iostream);
}
}
// next
public static class CryptoSerivces
{
//---------------------------------------------------------------------
public static Byte[] AesEncrypt(Byte[] src, Byte[] key, Byte[] IV)
{
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
try
{
myRijndael.Mode = CipherMode.CBC;
myRijndael.Key = key;
myRijndael.IV = IV;
myRijndael.Padding = PaddingMode.PKCS7;
using (ICryptoTransform encryptor = myRijndael.CreateEncryptor())
using (MemoryStream msEncrypt = new MemoryStream())
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(src, 0, src.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
}
finally
{
myRijndael.Clear();
}
}
}
//---------------------------------------------------------------------
public static Byte[] AesDecrypt(Byte[] src, Byte[] key, Byte[] IV)
{
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
try
{
myRijndael.Mode = CipherMode.CBC;
myRijndael.Key = key;
myRijndael.IV = IV;
myRijndael.Padding = PaddingMode.PKCS7;
using (ICryptoTransform decryptor = myRijndael.CreateDecryptor())
using (MemoryStream msDecrypt = new MemoryStream())
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
csDecrypt.Write(src, 0, src.Length);
csDecrypt.FlushFinalBlock();
return msDecrypt.ToArray();
}
}
finally
{
myRijndael.Clear();
}
}
}
}
// put all the peices together
void SaveToFile(String fileName, Parametres obj)
{
ObjectXmlSerializer oxs = new ObjectXmlSerializer();
Byte[] bObj = oxs.Serialize(obj);
Byte[] bEncObj = CryptoSerivces.AesEncrypt(bObj, SomeKey, SomeIV);
using (FileStream fs = File.Open(filename, FileMode.OpenOrCreate))
{
fs.Write(bEncObj, 0, bEncObj.Length);
}
}
// I'll leave the reading up to you.
Related
i write actual a Game in Unity and i want to secure the GameData with this Code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;
using System.Text;
using System.Security.Cryptography;
using System.Linq;
public class Data : MonoBehaviour {
static readonly string PasswordHash = "P##Sw0rd";
static readonly string SaltKey = "S#LT&KEY";
static readonly string VIKey = "#1B2c3D4e5F6g7H8";
public GameObject dirtBlock;
public void Save(){
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerData.dat",FileMode.Open);
PlayerData data = new PlayerData();
data.dirtCount = dirtBlock.GetComponent<BlockHandler>().blockCount;
data.stoneCount = 0;
data.ironCount = 0;
bf.Serialize(file,Encrypt(ToByteArray(data)));
file.Close();
}
public void Load(){
if(File.Exists(Application.persistentDataPath + "/playerData.dat")){
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerData.dat",FileMode.Open);
PlayerData data = FromByteArray<PlayerData>(Decrypt(bf.Deserialize(file).ToString()));
file.Close();
dirtBlock.GetComponent<BlockHandler>().blockCount = data.dirtCount;
} else {
FileStream file = File.Create(Application.persistentDataPath + "/playerData.dat");
file.Close();
this.Save();
this.Load();
}
}
public static string Encrypt(byte[] plainTextBytes){
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
byte[] cipherTextBytes;
using (var memoryStream = new MemoryStream()){
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)){
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memoryStream.ToArray();
cryptoStream.Close();
}
memoryStream.Close();
}
return Convert.ToBase64String(cipherTextBytes);
}
public static byte[] Decrypt(string encryptedText){
byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
memoryStream.Close();
cryptoStream.Close();
return plainTextBytes;
}
public byte[] ToByteArray<T>(T obj){
if(obj == null){
return null;
}
BinaryFormatter bf = new BinaryFormatter();
using(MemoryStream ms = new MemoryStream()){
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
public T FromByteArray<T>(byte[] data){
if(data == null){
return default(T);
}
BinaryFormatter bf = new BinaryFormatter();
using(MemoryStream ms = new MemoryStream(data)){
object obj = bf.Deserialize(ms);
return (T)obj;
}
}
}
[System.Serializable]
public class PlayerData {
public float dirtCount;
public float stoneCount;
public float ironCount;
}
yesterday it work fine but today and i dont know why, i get this error message
SerializationException: Unexpected binary element: 0
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:254)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (BinaryElement element, System.IO.BinaryReader reader) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:130)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:104)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:179)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:136)
Data.FromByteArray[PlayerData] (System.Byte[] data) (at Assets/Scripts/Player/Data.cs:119)
Data.Load () (at Assets/Scripts/Player/Data.cs:40)
i searched know 4 hours in the web but i cannot find a solution for this problem. i hope anyone can help me.
After some testing with your code i ended up with these two methods.
I think the main issue with your code was the Decrypt that never did anything with the plainTextBytes-array.
private static string Encrypt(byte[] plainTextBytes)
{
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
byte[] cipherTextBytes;
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memoryStream.ToArray();
}
}
return Convert.ToBase64String(cipherTextBytes);
}
Decrypt
public static byte[] Decrypt(string base64)
{
byte[] cipherTextBytes = Convert.FromBase64String(base64);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (BinaryReader srDecrypt = new BinaryReader(cryptoStream))
{
return srDecrypt.ReadBytes(cipherTextBytes.Length);
}
}
}
}
I'm studying encryption in C# and I'm having trouble. I have some Rijndael encryption code and it's working perfectly with strings. But now I'm studying serialization and the BinaryWriter writes the data of classes without any protection. I'm using this code to test; is there a way to "encrypt the class", or something similar?
To clarify the question, here is my code:
FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create);
using (BinaryWriter sw = new BinaryWriter(file))
{
byte[] byt = ConverteObjectEmByte(myVarClass);
sw.Write(byt);
}
And this is how I read it:
MyClass newMyVarClass;
FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open);
using (BinaryReader sr = new BinaryReader(file))
{
// 218 is the size of the byte array that I've tested (byt)
myNewVarClass = (MyClass)ConverteByteEmObject(sr.ReadBytes(218));
}
Thanks!
Rather than converting to byte[] as an intermediate step when passing to different stream objects you can chain multiple streams together, passing the output from one to the input of another.
This approach makes sense here, as you are chaining together
Binary Serialization => Encryption => Writing to File.
With this in mind, you can change ConvertObjectEmByte to something like:
public static void WriteObjectToStream(Stream outputStream, Object obj)
{
if (object.ReferenceEquals(null, obj))
{
return;
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
and similarly, ConvertByteEmObject can become:
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter binForm = new BinaryFormatter();
object obj = binForm.Deserialize(inputStream);
return obj;
}
To add in the encryption/decryption, we can write functions that create CryptoStream objects that we can chain with these binary serialization functions. My example functions below look a bit different from the Encrypt/Decrypt functions in the article you linked to because the IV (Initialization Vector) is now generated randomly and written to the stream (and read from the stream on the other end). It's important that the IV is unique for each chunk of data you encrypt for security, and you should also use a random number generator intended for cryptographic purposes like RNGCryptoServiceProvider, rather than a pseudo-random number generator like Random.
public static CryptoStream CreateEncryptionStream(byte[] key, Stream outputStream)
{
byte[] iv = new byte[ivSize];
using (var rng = new RNGCryptoServiceProvider())
{
// Using a cryptographic random number generator
rng.GetNonZeroBytes(iv);
}
// Write IV to the start of the stream
outputStream.Write(iv, 0, iv.Length);
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream encryptor = new CryptoStream(
outputStream,
rijndael.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
return encryptor;
}
public static CryptoStream CreateDecryptionStream(byte[] key, Stream inputStream)
{
byte[] iv = new byte[ivSize];
if (inputStream.Read(iv, 0, iv.Length) != iv.Length)
{
throw new ApplicationException("Failed to read IV from stream.");
}
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream decryptor = new CryptoStream(
inputStream,
rijndael.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
return decryptor;
}
Finally, we can glue it together:
byte[] key = Convert.FromBase64String(cryptoKey);
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create))
using (CryptoStream cryptoStream = CreateEncryptionStream(key, file))
{
WriteObjectToStream(cryptoStream, myVarClass);
}
MyClass newMyVarClass;
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open))
using (CryptoStream cryptoStream = CreateDecryptionStream(key, file))
{
newMyVarClass = (MyClass)ReadObjectFromStream(cryptoStream);
}
Note that we pass the file stream object to CreateEncryptionStream (and CreateDecryptionStream), and then pass the cryptoStream object to WriteObjectToStream (and ReadObjectfromStream). You'll also notice that the streams are scoped inside using blocks, so that they'll automatically be cleaned up when we're finished with them.
Here's the full test program:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
namespace CryptoStreams
{
class Program
{
[Serializable]
public class MyClass
{
public string TestValue
{
get;
set;
}
public int SomeInt
{
get;
set;
}
}
public static void WriteObjectToStream(Stream outputStream, Object obj)
{
if (object.ReferenceEquals(null, obj))
{
return;
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter binForm = new BinaryFormatter();
object obj = binForm.Deserialize(inputStream);
return obj;
}
private const string cryptoKey =
"Q3JpcHRvZ3JhZmlhcyBjb20gUmluamRhZWwgLyBBRVM=";
private const int keySize = 256;
private const int ivSize = 16; // block size is 128-bit
public static CryptoStream CreateEncryptionStream(byte[] key, Stream outputStream)
{
byte[] iv = new byte[ivSize];
using (var rng = new RNGCryptoServiceProvider())
{
// Using a cryptographic random number generator
rng.GetNonZeroBytes(iv);
}
// Write IV to the start of the stream
outputStream.Write(iv, 0, iv.Length);
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream encryptor = new CryptoStream(
outputStream,
rijndael.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
return encryptor;
}
public static CryptoStream CreateDecryptionStream(byte[] key, Stream inputStream)
{
byte[] iv = new byte[ivSize];
if (inputStream.Read(iv, 0, iv.Length) != iv.Length)
{
throw new ApplicationException("Failed to read IV from stream.");
}
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream decryptor = new CryptoStream(
inputStream,
rijndael.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
return decryptor;
}
static void Main(string[] args)
{
MyClass myVarClass = new MyClass
{
SomeInt = 1234,
TestValue = "Hello"
};
byte[] key = Convert.FromBase64String(cryptoKey);
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create))
{
using (CryptoStream cryptoStream = CreateEncryptionStream(key, file))
{
WriteObjectToStream(cryptoStream, myVarClass);
}
}
MyClass newMyVarClass;
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open))
using (CryptoStream cryptoStream = CreateDecryptionStream(key, file))
{
newMyVarClass = (MyClass)ReadObjectFromStream(cryptoStream);
}
Console.WriteLine("newMyVarClass.SomeInt: {0}; newMyVarClass.TestValue: {1}",
newMyVarClass.SomeInt,
newMyVarClass.TestValue);
}
}
}
I'm not sure whether the .Net Library had changed or just the code is wrong. I can't directly run the code written by softwariness.
Since that, I changed the code based on the answer so that it can be used correctly. Here's an example.
public class CryptoSerialization
{
public static void WriteObjectToStream(Stream outputStream, object obj)
{
if (obj is null) throw new ArgumentNullException("obj can't be null");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(inputStream);
}
public static CryptoStream CreateEncryptionStream(Stream outputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(outputStream, rijndael.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
}
public static CryptoStream CreateDecryptionStream(Stream inputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(inputStream, rijndael.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
}
public static void EncryptObjectToFile(object obj, string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Create);
using (CryptoStream cryptoStream = CreateEncryptionStream(file, Key, IV))
{
WriteObjectToStream(cryptoStream, obj);
}
}
public static object DecryptObjectFromFile(string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Open);
using (CryptoStream cryptoStream = CreateDecryptionStream(file, Key, IV))
{
return ReadObjectFromStream(cryptoStream);
}
}
}
[Serializable]
public class Student
{
public string Name;
public int Age;
}
static async Task Main(string[] args)
{
// the original string "[This is an example key string!]";
// I don't know if the length of the string has to be 32, but when I tried 64, it went wrong.
string cryptoKey = "W1RoaXMgaXMgYW4gZXhhbXBsZSBrZXkgc3RyaW5nIV0=";
byte[] Key = Convert.FromBase64String(cryptoKey);
byte[] IV = new byte[16];
using (RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//same as
//Rijndael rijndael = new RijndaelManaged();
//rijndael.GenerateIV();
//byte[] iv = rijndael.IV;
List<Student> students = new List<Student>() { new Student { Name = "John", Age = 10 }, new Student { Name = "Marry", Age = 15 } };
CryptoSerialization.EncryptObjectToFile(students, Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
List<Student> newStudents = (List<Student>)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
newStudents.ForEach((stu) =>
{
Console.WriteLine(stu.Name + ", " + stu.Age);
});
Console.ReadKey();
}
I am building a application that can encrypt and decrypt files, in c#. The encryption seems like its working (I think its working) however the decryption gives me the exception "The input data is not a complete block." I have tried almost everything I could find online so I thought I should come here. Here is the decryption code:
static void decryptAES (/*byte[] buffer,*/string filePath,byte[] key, byte[] IV)
{
Console.WriteLine("1");
using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
{
AES.Padding = PaddingMode.PKCS7;
AES.Key = key;
AES.BlockSize = 128;
AES.KeySize = 128;
AES.IV = IV;
using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
{
using (ICryptoTransform crypt = AES.CreateDecryptor(key, IV))
{
using (CryptoStream crStream = new CryptoStream(fStream, crypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(crStream))
{
//breaks here
string data = reader.ReadToEnd();
File.WriteAllText(filePath, data);
File.Move(filePath, filePath.Replace(".encrypted", ""));
}
}
}
}
}
}
It breaks on the line with the comment //breaks here by the way.
Thanks.
Here is the encrypt function
static void encryptAES(byte[] filesBytes,string
filePath,AesCryptoServiceProvider aes)
{
using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
{
using (ICryptoTransform crypt = aes.CreateEncryptor(aes.Key, aes.IV))
{
using (CryptoStream csStream = new CryptoStream(fStream, crypt, CryptoStreamMode.Write))
{
csStream.Write(filesBytes, 0, filesBytes.Length);
}
try { File.Move(filePath, filePath + ".encrypted"); }
catch (UnauthorizedAccessException)
{
}
}
}
}
And here is the key generation:
static AesCryptoServiceProvider generateAES()
{
AesCryptoServiceProvider a = new AesCryptoServiceProvider();
a.Padding = PaddingMode.PKCS7;
a.BlockSize =128;
a.KeySize = 128;
a.GenerateIV();
a.GenerateKey();
return a;
}
Here is how I stored the IV and AES Key:
private static void dumpKeys(AesCryptoServiceProvider aes)
{
foreach (byte b in aes.Key)
{
Console.Write(b);
}
Console.WriteLine();
foreach (byte b in aes.IV)
{
Console.Write(b);
}
byte[] encryptedKey = encryptRSA(aes.Key);
byte[] encryptedIV = encryptRSA(aes.IV);
Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Keys");
File.WriteAllBytes(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Keys\0000000000000.Key", encryptedKey);
File.WriteAllBytes(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Keys\1111111111111.IV", encryptedIV);
}
And here is how I retrieve the key and iv data:
byte[] AESKey = decrypt(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Keys\0000000000000.Key");
byte[] AESIV = decrypt(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Keys\1111111111111.IV");
static byte[] decrypt(string path)
{
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.FromXmlString(Properties.Resources.privateKey);
byte[] unencrypted = RSA.Decrypt(File.ReadAllBytes(path), true);
foreach (byte b in unencrypted)
{
Console.Write(b);
}
Console.WriteLine();
return unencrypted;
}
}
Btw I store a public RSA key in my resources file and I use that to decrypt the key.
Thanks
So far, so good. Do you extract the generated Key and IV? I modified your decryption function to an encryption function...
public static void Main()
{
var f = #"q:\test.txt";
AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CBC;
AES.BlockSize = 128;
AES.KeySize = 128;
AES.GenerateKey();
AES.GenerateIV();
var key = AES.Key;
var iv = AES.IV;
encryptAES(AES, f, key, iv);
decryptAES(AES, f + ".encrypted", key, iv);
}
static void encryptAES(SymmetricAlgorithm algo, string filePath, byte[] key, byte[] IV)
{
using (FileStream fin = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (FileStream fout = new FileStream(filePath + ".encrypted", FileMode.OpenOrCreate, FileAccess.Write))
{
using (ICryptoTransform crypt = algo.CreateEncryptor(key, IV))
{
using (CryptoStream crStream = new CryptoStream(fout, crypt, CryptoStreamMode.Write))
{
fin.CopyTo(crStream);
}
}
}
}
}
static void decryptAES(SymmetricAlgorithm algo, string filePath, byte[] key, byte[] IV)
{
using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (ICryptoTransform crypt = algo.CreateDecryptor(key, IV))
{
using (CryptoStream crStream = new CryptoStream(fStream, crypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(crStream))
{
string data = reader.ReadToEnd();
File.WriteAllText(filePath.Replace(".encrypted", ".restored"), data);
}
}
}
}
}
Tested it with your code, slightly modified (changed FileMode.Open to FileMode.OpenOrCreate, Added File.Delete before File.Move and merged File.WriteAllText with File.Move) and it works. So a) please review your code. b) If it still fails, provide more details, a single codeblock with every function and a Main-method that calls your functions to reproduce your failure...
static AesCryptoServiceProvider generateAES()
{
AesCryptoServiceProvider a = new AesCryptoServiceProvider();
a.Padding = PaddingMode.PKCS7;
a.BlockSize = 128;
a.KeySize = 128;
a.GenerateIV();
a.GenerateKey();
return a;
}
static void encryptAES(byte[] filesBytes, string filePath, AesCryptoServiceProvider aes)
{
using (FileStream fStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
using (ICryptoTransform crypt = aes.CreateEncryptor(aes.Key, aes.IV))
{
using (CryptoStream csStream = new CryptoStream(fStream, crypt, CryptoStreamMode.Write))
{
csStream.Write(filesBytes, 0, filesBytes.Length);
}
try {
File.Delete(filePath + ".encrypted");
File.Move(filePath, filePath + ".encrypted");
}
catch (UnauthorizedAccessException)
{
}
}
}
}
static void decryptAES(/*byte[] buffer,*/string filePath, byte[] key, byte[] IV)
{
using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
{
AES.Padding = PaddingMode.PKCS7;
AES.Key = key;
AES.BlockSize = 128;
AES.KeySize = 128;
AES.IV = IV;
using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
{
using (ICryptoTransform crypt = AES.CreateDecryptor(key, IV))
{
using (CryptoStream crStream = new CryptoStream(fStream, crypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(crStream))
{
//breaks here
string data = reader.ReadToEnd();
File.WriteAllText(filePath.Replace(".encrypted", ""), data);
}
}
}
}
}
}
I'm studying encryption in C# and I'm having trouble. I have some Rijndael encryption code and it's working perfectly with strings. But now I'm studying serialization and the BinaryWriter writes the data of classes without any protection. I'm using this code to test; is there a way to "encrypt the class", or something similar?
To clarify the question, here is my code:
FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create);
using (BinaryWriter sw = new BinaryWriter(file))
{
byte[] byt = ConverteObjectEmByte(myVarClass);
sw.Write(byt);
}
And this is how I read it:
MyClass newMyVarClass;
FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open);
using (BinaryReader sr = new BinaryReader(file))
{
// 218 is the size of the byte array that I've tested (byt)
myNewVarClass = (MyClass)ConverteByteEmObject(sr.ReadBytes(218));
}
Thanks!
Rather than converting to byte[] as an intermediate step when passing to different stream objects you can chain multiple streams together, passing the output from one to the input of another.
This approach makes sense here, as you are chaining together
Binary Serialization => Encryption => Writing to File.
With this in mind, you can change ConvertObjectEmByte to something like:
public static void WriteObjectToStream(Stream outputStream, Object obj)
{
if (object.ReferenceEquals(null, obj))
{
return;
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
and similarly, ConvertByteEmObject can become:
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter binForm = new BinaryFormatter();
object obj = binForm.Deserialize(inputStream);
return obj;
}
To add in the encryption/decryption, we can write functions that create CryptoStream objects that we can chain with these binary serialization functions. My example functions below look a bit different from the Encrypt/Decrypt functions in the article you linked to because the IV (Initialization Vector) is now generated randomly and written to the stream (and read from the stream on the other end). It's important that the IV is unique for each chunk of data you encrypt for security, and you should also use a random number generator intended for cryptographic purposes like RNGCryptoServiceProvider, rather than a pseudo-random number generator like Random.
public static CryptoStream CreateEncryptionStream(byte[] key, Stream outputStream)
{
byte[] iv = new byte[ivSize];
using (var rng = new RNGCryptoServiceProvider())
{
// Using a cryptographic random number generator
rng.GetNonZeroBytes(iv);
}
// Write IV to the start of the stream
outputStream.Write(iv, 0, iv.Length);
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream encryptor = new CryptoStream(
outputStream,
rijndael.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
return encryptor;
}
public static CryptoStream CreateDecryptionStream(byte[] key, Stream inputStream)
{
byte[] iv = new byte[ivSize];
if (inputStream.Read(iv, 0, iv.Length) != iv.Length)
{
throw new ApplicationException("Failed to read IV from stream.");
}
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream decryptor = new CryptoStream(
inputStream,
rijndael.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
return decryptor;
}
Finally, we can glue it together:
byte[] key = Convert.FromBase64String(cryptoKey);
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create))
using (CryptoStream cryptoStream = CreateEncryptionStream(key, file))
{
WriteObjectToStream(cryptoStream, myVarClass);
}
MyClass newMyVarClass;
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open))
using (CryptoStream cryptoStream = CreateDecryptionStream(key, file))
{
newMyVarClass = (MyClass)ReadObjectFromStream(cryptoStream);
}
Note that we pass the file stream object to CreateEncryptionStream (and CreateDecryptionStream), and then pass the cryptoStream object to WriteObjectToStream (and ReadObjectfromStream). You'll also notice that the streams are scoped inside using blocks, so that they'll automatically be cleaned up when we're finished with them.
Here's the full test program:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
namespace CryptoStreams
{
class Program
{
[Serializable]
public class MyClass
{
public string TestValue
{
get;
set;
}
public int SomeInt
{
get;
set;
}
}
public static void WriteObjectToStream(Stream outputStream, Object obj)
{
if (object.ReferenceEquals(null, obj))
{
return;
}
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter binForm = new BinaryFormatter();
object obj = binForm.Deserialize(inputStream);
return obj;
}
private const string cryptoKey =
"Q3JpcHRvZ3JhZmlhcyBjb20gUmluamRhZWwgLyBBRVM=";
private const int keySize = 256;
private const int ivSize = 16; // block size is 128-bit
public static CryptoStream CreateEncryptionStream(byte[] key, Stream outputStream)
{
byte[] iv = new byte[ivSize];
using (var rng = new RNGCryptoServiceProvider())
{
// Using a cryptographic random number generator
rng.GetNonZeroBytes(iv);
}
// Write IV to the start of the stream
outputStream.Write(iv, 0, iv.Length);
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream encryptor = new CryptoStream(
outputStream,
rijndael.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
return encryptor;
}
public static CryptoStream CreateDecryptionStream(byte[] key, Stream inputStream)
{
byte[] iv = new byte[ivSize];
if (inputStream.Read(iv, 0, iv.Length) != iv.Length)
{
throw new ApplicationException("Failed to read IV from stream.");
}
Rijndael rijndael = new RijndaelManaged();
rijndael.KeySize = keySize;
CryptoStream decryptor = new CryptoStream(
inputStream,
rijndael.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
return decryptor;
}
static void Main(string[] args)
{
MyClass myVarClass = new MyClass
{
SomeInt = 1234,
TestValue = "Hello"
};
byte[] key = Convert.FromBase64String(cryptoKey);
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Create))
{
using (CryptoStream cryptoStream = CreateEncryptionStream(key, file))
{
WriteObjectToStream(cryptoStream, myVarClass);
}
}
MyClass newMyVarClass;
using (FileStream file = new FileStream(Environment.CurrentDirectory + #"\class.dat", FileMode.Open))
using (CryptoStream cryptoStream = CreateDecryptionStream(key, file))
{
newMyVarClass = (MyClass)ReadObjectFromStream(cryptoStream);
}
Console.WriteLine("newMyVarClass.SomeInt: {0}; newMyVarClass.TestValue: {1}",
newMyVarClass.SomeInt,
newMyVarClass.TestValue);
}
}
}
I'm not sure whether the .Net Library had changed or just the code is wrong. I can't directly run the code written by softwariness.
Since that, I changed the code based on the answer so that it can be used correctly. Here's an example.
public class CryptoSerialization
{
public static void WriteObjectToStream(Stream outputStream, object obj)
{
if (obj is null) throw new ArgumentNullException("obj can't be null");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(outputStream, obj);
}
public static object ReadObjectFromStream(Stream inputStream)
{
BinaryFormatter bf = new BinaryFormatter();
return bf.Deserialize(inputStream);
}
public static CryptoStream CreateEncryptionStream(Stream outputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(outputStream, rijndael.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
}
public static CryptoStream CreateDecryptionStream(Stream inputStream, byte[] Key, byte[] IV)
{
Rijndael rijndael = new RijndaelManaged();
return new CryptoStream(inputStream, rijndael.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
}
public static void EncryptObjectToFile(object obj, string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Create);
using (CryptoStream cryptoStream = CreateEncryptionStream(file, Key, IV))
{
WriteObjectToStream(cryptoStream, obj);
}
}
public static object DecryptObjectFromFile(string path, byte[] Key, byte[] IV)
{
using FileStream file = new FileStream(path, FileMode.Open);
using (CryptoStream cryptoStream = CreateDecryptionStream(file, Key, IV))
{
return ReadObjectFromStream(cryptoStream);
}
}
}
[Serializable]
public class Student
{
public string Name;
public int Age;
}
static async Task Main(string[] args)
{
// the original string "[This is an example key string!]";
// I don't know if the length of the string has to be 32, but when I tried 64, it went wrong.
string cryptoKey = "W1RoaXMgaXMgYW4gZXhhbXBsZSBrZXkgc3RyaW5nIV0=";
byte[] Key = Convert.FromBase64String(cryptoKey);
byte[] IV = new byte[16];
using (RNGCryptoServiceProvider rngcsp = new RNGCryptoServiceProvider())
{
rngcsp.GetBytes(IV);
}
//same as
//Rijndael rijndael = new RijndaelManaged();
//rijndael.GenerateIV();
//byte[] iv = rijndael.IV;
List<Student> students = new List<Student>() { new Student { Name = "John", Age = 10 }, new Student { Name = "Marry", Age = 15 } };
CryptoSerialization.EncryptObjectToFile(students, Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
List<Student> newStudents = (List<Student>)CryptoSerialization.DecryptObjectFromFile(Environment.CurrentDirectory + #"\testCrypto.dat", Key, IV);
newStudents.ForEach((stu) =>
{
Console.WriteLine(stu.Name + ", " + stu.Age);
});
Console.ReadKey();
}
I have a Security class that Encode and Decode string, but when I try do decode - something going wrong.
Here is my Security class:
class Security
{
public static String encrypt(String imput, String key)
{
String cipherText;
var rijndael = new RijndaelManaged()
{
Key = Encoding.Unicode.GetBytes(key),
Mode = CipherMode.ECB,
BlockSize = 128,
Padding = PaddingMode.Zeros,
};
ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, null);
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write(imput);
streamWriter.Flush();
}
cipherText = Convert.ToBase64String(memoryStream.ToArray());
}
}
return cipherText;
}
public static String decrypt(String imput, String key)
{
byte[] data = Convert.FromBase64String(imput);
String decrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Encoding.UTF8.GetBytes(key);
rijAlg.Mode = CipherMode.ECB;
rijAlg.BlockSize = 128;
rijAlg.Padding = PaddingMode.Zeros;
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
using (MemoryStream msDecrypt = new MemoryStream(data))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return decrypted;
}
}
In program.cs:
String A = Security.encrypt("text", "1234567891234567");
A = Security.decrypt(A, "1234567891234567");
Finaly it return something like that: �%����;\0�\a��f6 , but I need original string. Where I made a mistake?
Use the same encoding in both methods, either Encoding.Unicode or Encoding.UTF8