1

I have a minimal class to encrypt and decrypt DateTime objects without a time component. The test below would work for 1988 Jan 01, but fail for 1988 Jan 02, ie. the first iteration passes but the second fails already.

It seems my problem is definitely outside of the encryption and decryption of bytes. When debugging the second iteration (1988 Jan 02), encrypted in DateTime2EncryptedString has the following value:

{byte[16]} [0]: 147 [1]: 1 [2]: 22 [3]: 250 [4]: 74 [5]: 227 [6]: 225 [7]: 91 [8]: 157 [9]: 202 [10]: 138 [11]: 246 [12]: 91 [13]: 131 [14]: 42 [15]: 217

While encrypted in EncryptedString2DateTime with DateTime2EncryptedString's output string as parameter has the following value:

{byte[16]} [0]: 147 [1]: 1 [2]: 22 [3]: 250 [4]: 74 [5]: 227 [6]: 225 [7]: 91 [8]: 157 [9]: 202 [10]: 138 [11]: 246 [12]: 91 [13]: 131 [14]: 253 [15]: 255

The problem would come from my misunderstanding of bytes to string (and vice versa) operations ??

The test

        public void Test1()
        {
            for (int year = 1988; year < 2010; year++)
            {
                for (int month = 1; month < 12; month++)
                {
                    for (int day = 1; day < 28; day++)
                    {
                        var dt = new DateTime(year, month, day);
                        TestDate(dt);
                    }
                }
            }
        }

        private void TestDate(DateTime dt)
        {
            var encryptedString = DateEncryption.DateTime2EncryptedString(dt);
            var output = DateEncryption.EncryptedString2DateTime(encryptedString);
            Assert.AreEqual(dt, output);
        }

And here is the small utility class

public static class DateEncryption
{
    private static readonly byte[] Key = new byte[]
    {
        32, 29, 124, 21, 92, 18, 28,34, 74, 85, 14, 91, 51, 28, 73, 49, 54, 99, 1, 192, 211, 253, 251, 252,
        237, 142, 161, 178, 199, 208, 97, 98
    };
    private static readonly byte[] Iv = new byte[] { 19, 28, 33, 77, 131, 178, 192, 200, 215, 148, 247, 192, 184, 127, 3, 7};

    private static byte[] Decrypt(byte[] cipherData)
    {
        byte[] decryptedData;
        using (MemoryStream ms = new MemoryStream())
        {
            using (Rijndael alg = Rijndael.Create())
            {
                alg.Padding = PaddingMode.None;
                alg.Key = Key;
                alg.IV = Iv;
                using (CryptoStream cs = new CryptoStream(ms,
                    alg.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherData, 0, cipherData.Length);
                }
                decryptedData = ms.ToArray();
            }
        }
        return decryptedData;
    }

    private static byte[] Encrypt(byte[] clearData)
    {
        byte[] encryptedData;
        using (MemoryStream ms = new MemoryStream())
        {
            using (Rijndael alg = Rijndael.Create())
            {
                alg.Padding = PaddingMode.None;
                alg.Key = Key;
                alg.IV = Iv;
                using (CryptoStream cs = new CryptoStream(ms,
                   alg.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearData, 0, clearData.Length);
                }
                encryptedData = ms.ToArray();
            }
        }
        return encryptedData;
    }


    #region DateTimeEncryption

    public static string DateTime2EncryptedString(DateTime dt)
    {
        var dt2str = string.Format("{0:D4}{1:D2}{2:D2}", dt.Year, dt.Month, dt.Day);
        var str2bytes = System.Text.Encoding.Unicode.GetBytes(dt2str);
        var encrypted = Encrypt(str2bytes);
        return System.Text.Encoding.Unicode.GetString(encrypted);
    }

    public static DateTime EncryptedString2DateTime(string s)
    {
        var encrypted = System.Text.Encoding.Unicode.GetBytes(s);
        var decrypted = Decrypt(encrypted);
        var bytes2str = System.Text.Encoding.Unicode.GetString(decrypted);
        return new DateTime(int.Parse(bytes2str.Substring(0, 4)),
            int.Parse(bytes2str.Substring(4, 2)),
            int.Parse(bytes2str.Substring(6, 2)));
    }

    #endregion
}
BuZz
  • 16,318
  • 31
  • 86
  • 141
  • 1
    Try to store encrypted bytes with Convert.ToBase64String method. Encrypted bytes can contain sequences, which does not exist in unicode. – Atomosk Jun 22 '14 at 17:16
  • That worked, many thanks. Could you add an answer with some further details so that I can accept it ? Or some link on the concept of sequence in byte arrays. – BuZz Jun 22 '14 at 17:24
  • Rather than encrypt the string representation, you could try encrypting the long representation of the `DateTime.Date` property of the original `DateTime` value you are encrypting by calling `DateTime.ToBinary()`. Then you won't need to worry about string encoding issues or any other issues that could arise from using formatting methods, such as localisation-based representation issues. – Martin Costello Jun 22 '14 at 17:30

1 Answers1

1

Not every sequence of bytes are valid in unicode. System.Text.Encoding.Unicode.GetString ignores such sequences. Base64 strings was designed to convert any sequence of bytes in string. In .net you work with base64 strings via Convert class through methods such as ToBase64String(Byte[]) and FromBase64String(string).

Atomosk
  • 1,871
  • 2
  • 22
  • 26