I am new to C# and cryptography but I want to secure some data like an account with DPAPI in a C# project. I tryed some ways to do it but the data passed is XDocument and have to stay as it.
I tryed to pass a string and modify it with no problem but when it comes to the XML Data it is broken.
I am using the sample of MS dotnet standard.
This code works (initalization of the file)
byte[] toEncrypt = null;
byte[] entropy = CreateRandomEntropy();
FileStream fStream = new FileStream("Data.dat", FileMode.OpenOrCreate);
var length = new System.IO.FileInfo("Data.dat").Length;
if (length == 0)
{
XDocument doc =
new XDocument(
new XElement("data",
new XElement("global"),
new XElement("accounts")
)
);
toEncrypt = UnicodeEncoding.ASCII.GetBytes(doc.ToString());
EncryptDataToStream(toEncrypt, entropy, DataProtectionScope.CurrentUser, fStream);
}
fStream.Close();
Then I update this previous sample with some data:
fStream = new FileStream("Data.dat", FileMode.OpenOrCreate);
byte[] decryptData = DecryptDataFromStream(entropy, DataProtectionScope.CurrentUser, fStream, 2);
string xml = UnicodeEncoding.ASCII.GetString(decryptData);
XDocument xmlData = XDocument.Parse(xml);
int maxId = 0;
if (xmlData.Descendants("account").Any())
{
maxId = xmlData.Descendants("account")
.Max(x => (int)x.Attribute("id"));
}
maxId++;
var compteElement = new XElement("account",
new XAttribute("id", maxId),
new XElement("login", "monemail@home.fr"),
new XElement("label", "compte TEST")
);
xmlData.Element("data").Element("accounts").Add(compteElement);
MemoryStream ms = new MemoryStream();
var settings = new XmlWriterSettings()
{
Indent = true
};
using (var writer = XmlWriter.Create(ms, settings))
{
xmlData.WriteTo(writer);
writer.Flush();
StreamReader sr = new StreamReader(ms);
ms.Seek(0, SeekOrigin.Begin);
String content = sr.ReadToEnd();
byte[] BytedxmlData = UnicodeEncoding.ASCII.GetBytes(content);
int bytesWritten = EncryptDataToStream(BytedxmlData, entropy, DataProtectionScope.CurrentUser, fStream);
}
fStream.Flush();
fStream.Close();
And I try to read the data:
fStream = new FileStream("Data.dat", FileMode.Open);
byte[] decryptData2 = DecryptDataFromStream(entropy, DataProtectionScope.CurrentUser, fStream, 2);
Console.WriteLine("Decrypted data: " + UnicodeEncoding.ASCII.GetString(decryptData2));
fStream.Close();
Data.dat grown each times of the byte it isi being added at each update. Some I think it is being populated correctly but when I read it, I get only the first record the initialize the file and anyupdate.
Here are the encrypt and decrypt methods:
public static int EncryptDataToStream(byte[] Buffer, byte[] Entropy, DataProtectionScope Scope, Stream S)
{
if (Buffer == null)
throw new ArgumentNullException("Buffer");
if (Buffer.Length <= 0)
throw new ArgumentException("Buffer");
if (Entropy == null)
throw new ArgumentNullException("Entropy");
if (Entropy.Length <= 0)
throw new ArgumentException("Entropy");
if (S == null)
throw new ArgumentNullException("S");
int length = 0;
byte[] encryptedData = ProtectedData.Protect(Buffer, Entropy, Scope);
if (S.CanWrite && encryptedData != null)
{
S.Write(encryptedData, 0, encryptedData.Length);
length = encryptedData.Length;
}
return length;
}
public static byte[] DecryptDataFromStream(byte[] Entropy, DataProtectionScope Scope, Stream S, int Length)
{
if (S == null)
throw new ArgumentNullException("S");
if (Length <= 0)
throw new ArgumentException("Length");
if (Entropy == null)
throw new ArgumentNullException("Entropy");
if (Entropy.Length <= 0)
throw new ArgumentException("Entropy");
byte[] inBuffer = new byte[S.Length];
byte[] outBuffer;
if (S.CanRead)
{
S.Read(inBuffer, 0, inBuffer.Length);
outBuffer = ProtectedData.Unprotect(inBuffer, Entropy, Scope);
}
else
{
throw new IOException("Could not read the stream.");
}
return outBuffer;
}
Thanks to @jdweng I was from with the encoding. Got to change UnicodeEncoding.ASCII to Encoding.UTF-8