0

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;
}
  • On the byte array convert to a string so you can examine the xml using : string xml = Encoding.UTF8.GetBytes(byte[] data); – jdweng Jul 22 '20 at 10:09
  • This is what I am doing on the read part, not on the same encoding. I tryed yours but no luck. But the encrypted data show blanck space instead of wierd caracters – Emmanuel Michaud Jul 22 '20 at 12:03
  • XML is UTF-8 and using ASCII encoding will change some of the data. So replace : toEncrypt = UnicodeEncoding.ASCII.GetBytes(doc.ToString()); with Encoding.UTF-8.GetBytes(doc.ToString()); and Encoding.UTF-8.GetString(decryptData2) – jdweng Jul 22 '20 at 12:13
  • Thank you, I didn't changed UnicodeEncoding to Encoding I think that was my mistake from your first answer :) – Emmanuel Michaud Jul 29 '20 at 12:29

1 Answers1

0

Thanks to @jdweng I was from with the encoding. Got to change UnicodeEncoding.ASCII to Encoding.UTF-8