Resolved Detecting encryption

Donal23

Member
Joined
Dec 28, 2022
Messages
6
Programming Experience
Beginner
Hi. I was taught in another thread about extensions, and I find that awesome. I was just going through my utility class making some stuff extensions so I can simplify my code, and it reminded me of a question I had. I needed the ability to encrypt strings for safe data storage. I'm a beginner, so no way was I going to tackle that myself. Ergo, I found and lifted some code on the net for encryption:
C#:
public static string EncryptString(string Message, string Passphrase = "(my default value here)") {
  byte[] Results;
  UTF8Encoding UTF8 = new UTF8Encoding();
  MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
  byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
  TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
  TDESAlgorithm.Key = TDESKey;
  TDESAlgorithm.Mode = CipherMode.ECB;
  TDESAlgorithm.Padding = PaddingMode.PKCS7;
  byte[] DataToEncrypt = UTF8.GetBytes(Message);
  try {
    ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
    Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
  } finally {
    TDESAlgorithm.Clear();
    HashProvider.Clear();
  }
  return Convert.ToBase64String(Results);
}

public static string DecryptString(string Message, string Passphrase = "(my default value here)") {
  try {
    byte[] Results;
    UTF8Encoding UTF8 = new UTF8Encoding();
    MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
    byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
    TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
    TDESAlgorithm.Key = TDESKey;
    TDESAlgorithm.Mode = CipherMode.ECB;
    TDESAlgorithm.Padding = PaddingMode.PKCS7;
    byte[] DataToDecrypt = Convert.FromBase64String(Message);
    try {
      ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
      Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
    } finally {
      TDESAlgorithm.Clear();
      HashProvider.Clear();
    }
    return UTF8.GetString(Results);
  } catch {
    return Message;
  }
}
This works great. I needed the ability though to check if a string has already been encrypted by the method. So if I edit one of my config files and put a password in manually, when the program loads it will check and if the pwd is plain text, encrypt and rewrite config. So I did this:
C#:
public static bool IsEncrypted(this string value) {
  return DecryptString(value) != value;
}
It works cuz if it's not encrypted, DecryptString throws an exception and returns the input value. Problem is, when I am debugging something and set the error handling mode to ignore try/catch so I can debug other code, it's annoying cuz every time I read something plaintext the IDE breaks at the exception in DecryptString too. Is there a way to check if a string was encrypted without relying on throwing exceptions? Or is there a way to tell Visual Studio 2017 to not break execution in a certain method regardless of my error handling settings?

Thanks!
 
Well, that's the point of encryption... To make data indistinguishable from data which might not be encrypted. The data maybe just random data, it maybe compressed data, it maybe image data, etc.

In general, you want the debugger to stop on uncaught exceptions, not on when exceptions are thrown.

Furthermore, more it is generally bad practice to do Pokemon exception handling like you are doing on line 40. Just catch the exceptions that you are expecting and can handle. Don't catch-them-all.

You can fine tune which exceptions that VS will notify you. I'm nowhere close to a PC right now to do screenshots.

Please note that Triple DES was not recommended for new code since 2017, and legacy applications should stop using Triple DES by the end of 2023. It looks like you are writing new code and therefore should not be using it. Good job though opting to use an existing encryption solution instead of trying to roll your own if you are unsure about what you are doing.

Also every language has a specific style associated with it. C# style is to use Allman indent style. You should respect that convention and not use that Java/JavaScript OTBS indent style. Maybe it's from the code that you found, but it's something to be aware of.
 
Anyway, here is my recommendation. In your config file prefix the value string with "Cipher:" or "Clear:". The rest of the string is then either the cipher text or the clear text. You can then easily do a check if the string starts with a particular prefix and know if you need to do any decryption or not.
 
Well, that's the point of encryption... To make data indistinguishable from data which might not be encrypted. The data maybe just random data, it maybe compressed data, it maybe image data, etc.

In general, you want the debugger to stop on uncaught exceptions, not on when exceptions are thrown.

Furthermore, more it is generally bad practice to do Pokemon exception handling like you are doing on line 40. Just catch the exceptions that you are expecting and can handle. Don't catch-them-all.

You can fine tune which exceptions that VS will notify you. I'm nowhere close to a PC right now to do screenshots.

Please note that Triple DES was not recommended for new code since 2017, and legacy applications should stop using Triple DES by the end of 2023. It looks like you are writing new code and therefore should not be using it. Good job though opting to use an existing encryption solution instead of trying to roll your own if you are unsure about what you are doing.

Also every language has a specific style associated with it. C# style is to use Allman indent style. You should respect that convention and not use that Java/JavaScript OTBS indent style. Maybe it's from the code that you found, but it's something to be aware of.
Yea, my problem with the debugger is probably related to your Pokemon joke (that was pretty funny btw lol). Since I "catch 'em all" when my code throws up my exception error I'm not sure exactly where in the code it died, so I turn on the feature to make it stop no matter what on exception, but that leads to the problem I was posting about. I'll work on changing that mindset, thanks for the advice.

Thanks as well for pointing out the triple DES thing. I'll Google for a more modern approach. However when it comes to Allman, that's one place where I will just have to break the norms lol. I'm not a pro so I won't be contributing to any shared codebases, so I'm going to stick to K&R 1TBS style. I consider myself a beginner as I've never been formally trained, but I've been dabbling with code since I was a teenager (I'm 48 now) so my brain is too fixed on that style to change. Allman putting brackets on their own lines is just too weird to me hehe.

One thing that confuses me about conventions is variables. I broke myself of using camel case when working with C#, as other languages I've used for longer use that but I noticed when looking at C# examples they're usually Pascal case. But in some examples, I see people using camel in their methods. Is there a difference between class scope variable casing and method scope, or are the camel examples just outliers?

Oh, one more thing. (Really getting off the track of this post's original purpose now, hehe). Using Pascal, I ran into a condundrum. I made a custom class like "Entity" for holding data I get back from API calls to a service. Then I have a Result class that holds the info when I make said call with properties like a DateTime for the response, an enum for response code, then the Entity object I create from the data. Originally I was doing like:
C#:
class Result {
  public Entity EntityObject { get; set; } = null;
...since I figured I can't name my var the same as a class. But once I was just typing along without really thinking and did
C#:
class Result {
  public Entity Entity { get; set; } = null;
...and it didn't complain. I guess the IDE understands context. Is this acceptable or should I avoid using the name of a class as the name of a var, and stick to what I did originally? Sorry to pepper you with questions, lol.

Anyway, here is my recommendation. In your config file prefix the value string with "Cipher:" or "Clear:". The rest of the string is then either the cipher text or the clear text. You can then easily do a check if the string starts with a particular prefix and know if you need to do any decryption or not.
Good idea, thanks!
 
Last edited:
One thing that confuses me about conventions is variables. I broke myself of using camel case when working with C#, as other languages I've used for longer use that but I noticed when looking at C# examples they're usually Pascal case. But in some examples, I see people using camel in their methods. Is there a difference between class scope variable casing and method scope, or are the camel examples just outliers?
Pascal case for methods, properties, types, enums, and constants. Note: the last two: Your K&R style will likely want to do the all caps for enums and constants. :) Camel case for local variables and parameters. It used to be that public fields were Camel case, but it looks like the naming standard has changed over to making them Pascal case as well -- likely because of the rise of POCOs, anonymous types, and tuples.


Fire up a new thread for your other question.
 
Thanks!

I was playing with that AI chat bot, ChatGPT. I was told it can provide code, so I asked it "what is a good, modern way to encrypt a string in C#?" Is its answer good to use?

C#:
using System;
using System.IO;
using System.Security.Cryptography;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string original = "Hello, World!";
            string encrypted;
            string decrypted;

            using (Aes aes = Aes.Create())
            {
                // Encrypt the string to an array of bytes.
                byte[] encryptedBytes = EncryptStringToBytes(original, aes.Key, aes.IV);
                encrypted = Convert.ToBase64String(encryptedBytes);

                // Decrypt the bytes to a string.
                decrypted = DecryptStringFromBytes(encryptedBytes, aes.Key, aes.IV);
            }

            Console.WriteLine("Original:   {0}", original);
            Console.WriteLine("Encrypted:  {0}", encrypted);
            Console.WriteLine("Decrypted:  {0}", decrypted);
        }

        static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("iv");

            // Declare the stream used to encrypt to an in memory
            // array of bytes.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // Create a CryptoStream using the MemoryStream 
                // and the passed key and initialization vector (IV).
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
                    new AesManaged().CreateEncryptor(key, iv),
                    CryptoStreamMode.Write))
                {
                    // Create a StreamWriter using the CryptoStream.
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        // Write the plaintext to the stream.
                        swEncrypt.Write(plainText);
                    }
                    return msEncrypt.ToArray();
                }
            }
        }

        static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException("iv");

            // Declare the string used to hold the decrypted text.
            string plaintext = null;

            // Create an Aes object with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = iv;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
            return plaintext;
        }
    }
}
 
Back
Top Bottom