C# Encrypt Text Output
Asked Answered
N

9

3

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.

Novgorod answered 22/8, 2011 at 14:9 Comment(0)
K
14

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
Kanya answered 22/8, 2011 at 14:14 Comment(6)
Thanks. I will use the Base-64 encode/decode for this as it sounds as though it will achieve what I need to do. Could I read a file with this - static public string DecodeFrom64(string pathoffile) using the path of the file as the argument rather than a variable? I am used to using StreamReader so wondered if this would apply?Novgorod
Also if I were to use the above mentioned method where would I specify the location of the file and what extension would I use?Novgorod
You can modify the methods to read from a file. The actual methods which do the encoding/decoding for base64 work on array of bytes. Those are just sample code on how to do it from a string. In general, I keep "helper" methods like these in its own Utility class which then can be called from anywhere in the program. You can use this: System.IO.StreamReader myFile = new System.IO.StreamReader("c:\\test.txt"); string myString = myFile.ReadToEnd(); and System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt"); file.WriteLine(lines); file.Close();Kanya
Bali, as @Kanya mentioned, base-64 encoding isn't secure. If you use one of the methods mentioned in this post, use the encryption one.Adjudge
I know this is old, but your Encrypt() method keeps popping up an error for me on byte[] rgbKey saying that the "object reference is not set to an instance of an object".Glenn
I downvoted because using a Base64 string will almost double the file size. This answer is not a proper solution.Maneuver
B
3

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.

Bina answered 22/8, 2011 at 14:12 Comment(0)
G
2

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.

Grimmett answered 22/8, 2011 at 14:11 Comment(4)
You can't even detect such tampering. The user can add the same checksum.Bina
Thanks for the suggetions, my priority is that the data isn't stored in plain text, like when you open a DLL or EXE in notepad, it is all scrambled and un readable (I know this is because of the way it is complied but you get what I mean - just so the user isn't tempted to break anything just because they can read it) thanks.Novgorod
If I encrypted the text would it not show in plain text or not be accessible? I take it the app would still be able to read this though? Do you have some sample code to show me how to encrypt something? Thanks very muchNovgorod
@Bali C - encrypting means that it can't be read without being decrypted first. I suggest you look through the MSDN pages for the System.Security.Cryptography namespace. The topic is large and any "simple" example will be misleading.Grimmett
E
1

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.

Escapade answered 22/8, 2011 at 14:14 Comment(5)
Because the user can still open it with any ZIP program.Bina
not if there's a decent password on it, and choosing a non-zip extension for the file prevents quite some average users from tryingEscapade
True, although the user can decompile the program and find the password.Bina
but that's an altogether different issue.. that depends on how well you protect the password within your own application, or wherever it is storedEscapade
anyway, if a user starts decompiling a program to look for passwords it is probably a somewhat expert user. the question wasn't specific on the level of required protection nor on the expert level of the users so it may not even be necessary to take this kind of protection into account.Escapade
B
1

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.

Bearnard answered 22/8, 2011 at 14:15 Comment(0)
A
1

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.

Adjudge answered 22/8, 2011 at 15:8 Comment(0)
C
1

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;
        }
    }
}
Chris answered 24/8, 2012 at 9:50 Comment(1)
good code, sad one needs to hard code the IVa as this would make it reversible, decompile the code and the key is there.Adolescent
M
1

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.

Meaghan answered 27/10, 2015 at 14:56 Comment(0)
H
0

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.

Hawfinch answered 24/7, 2018 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.