Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / cryptography

Self Driven Cryptographic Operations

4.00/5 (3 votes)
22 Apr 2012CPOL3 min read 13.6K  
Input drives action to perform : Encryption or Decryption - Implementation of user defined encryption signature with standard cryptographic algorithms

Introduction 

It's convenient to have a Crypto library/method in such a way that the input(file or string for crypto operation) drives the cryptographic action to perform(encryption or decryption). This can be done by adding a user defined encryption signature to the data encrypted using standard encryption algorithms. Let's use TripeDES encryption algorithm(available on .Net Platform) and try to implement our concept. 

Using the code 

The whole concept can be summarized as: 

1. Check if we can find encryption signature on the input.

2. If we find encryption signature, go ahead and decrypt it.

3. If we do not find encryption signature go ahead and encrypt it.   

Let's define an encryption signature first. 

C#
 byte[] ENCRYPTION_SIGNATURE = {
                                       85, 66, 67, 127, 128, 248, 92, 152, 15, 252, 175, 38, 158, 218, 22, 141
                                      };   

Decide on Encrypt or Decrypt operation 

Check if input converted to byte array has encryption signature. 

1. In order to check this we need to get block of data from input with same size as encryption    signature.  

2. Then compare the block we have read with encryption signature.

3. If they are equal means,input is the candidate for Decryption operation, else it's for    Encryption operation. 

 The above steps can be written as below: 

C#
//b is the input converted to byte array.

byte[] encryptionSignature = new byte[ENCRYPTION_SIGNATURE.Length];
System.Buffer.BlockCopy(b, 0, encryptionSignature, 0, ENCRYPTION_SIGNATURE.Length);
bool _isEncrypted = encryptionSignature.SequenceEqual(ENCRYPTION_SIGNATURE);

Decryption  

If our input is encrypted, then we will go ahead and decrypt it. So the steps for decryption are

1. Convert string input or file input to byte array.

2. Remove Encryption Signature. 

3. Hash password phrase(converted to bytes)  using MD5. This would be the Key for TripleDES object 

4. Create TripleDESCryptoServiceProvider and set up it by

i. assign key obtained from step 3

ii. assign cipher mode and padding.

5. Try to decrypt it.

6. Return decrypted byte array.

7. Now you can process the result if you want it as a string or write it as a file as per your input type(string or file)

 

Encryption 

If our input is not encrypted yet, then we will go ahead and encrypt it. So the steps for encryption are 

1. Convert string input or file input to byte array. 

2. Hash password phrase(converted to bytes)  using MD5. This would be the Key for TripleDES object 

3. Create TripleDESCryptoServiceProvider and set up it by

i. assign key obtained from step 3

ii. assign cipher mode and padding.

4. Try to encrypt it.

5. Add encryption signature. 

6. Return encrypted byte array.

7. Now you can process the result if you want it as a string or write it as a file as per your input type(string or file) 

 

Complete DES Class 

Let's write complete concept using a class  

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Kuthuparakkal.Crypto.Util
{

    public class DES
    {
        
        byte[] ENCRYPTION_SIGNATURE = {
                                       85, 66, 67, 127, 128, 248, 92, 152, 15, 252, 175, 38, 158, 218, 22, 141
                                      };
                                    

        private List<exception> _errorList ;
        public List<exception> ErrorList
        {
            get { return _errorList; }
        }

        private void AddError(Exception ex)
        {
            
            _errorList.Add(ex);
        }

        public DES()
        {
            _errorList = new List<exception>();
        }

        public string Crypto(string Input, string password)
        {
            try
            {
                System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
                bool encrypt = false;
                byte[] ToProcessDecrypt = new byte[1024];
                byte[] ToProcessEncrypt = new byte[1024];
                try
                {
                    ToProcessDecrypt = Convert.FromBase64String(Input);
                    encrypt = true;
                }
                catch { }

                try
                {
                    ToProcessEncrypt = UTF8.GetBytes(Input);
                }
                catch { }


                bool decision = encrypt && IsEncrypted(ToProcessDecrypt);

                if (!decision)
                {
                    byte[] encrypted = Encrypt(ToProcessEncrypt, password);
                    return Convert.ToBase64String(encrypted);

                }
                else
                {
                    byte[] Decr = Decrypt(ToProcessDecrypt, password);
                    return UTF8.GetString(Decr);
                }
            }
            catch(Exception ex)
            {
                this.AddError(ex);
                return null;
            }

        }

        public bool Crypto(string FilePath, string SavePath, string password)
        {
            bool success = true;
            try
            {
                byte[] ToProcess = ReadFile(FilePath);
                bool IsEncry = IsEncrypted(ToProcess);

                if (IsEncry)
                {
                    byte[] decrypted = Decrypt(ToProcess, password);
                    SaveFile(SavePath, decrypted);
                }
                else
                {
                    byte[] encrypted = Encrypt(ToProcess, password);
                    SaveFile(SavePath, encrypted);
                }
            }
            catch (Exception ex)
            {
                success = false;
                this.AddError(ex);
            }

            return success;
        }

        private byte[] Encrypt(byte[] DataToEncrypt, string Passphrase)
        {
            byte[] Results;

            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the encoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            
            // Step 4. Attempt to encrypt the input
            try
            {
                ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
                Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
            }
            catch (Exception ex)
            {
                this.AddError(ex);
                throw ex; 
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 5. Write Encryption Signature and return

            return WriteEncryptionSignature(Results);
        }

        private byte[] Decrypt(byte[] DataToDecrypt, string Passphrase)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the decoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            // Step 4. Strip off Encryption Signature
            byte[] DataToDecryptNew =  StripEncryptionSignature(DataToDecrypt);

            // Step 5. Attempt to decrypt the input
            try
            {
                ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
                Results = Decryptor.TransformFinalBlock(DataToDecryptNew, 0, DataToDecryptNew.Length);
            }
            catch (Exception ex)
            {
                this.AddError(ex);
                throw ex;
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 6. Return the decrypted byte array
            return Results;
        }

        private byte[] WriteEncryptionSignature(byte[] b)
        {
            byte[] c = new byte[ENCRYPTION_SIGNATURE.Length + b.Length];
            try
            {
                System.Buffer.BlockCopy(ENCRYPTION_SIGNATURE, 0, c, 0, ENCRYPTION_SIGNATURE.Length);
                System.Buffer.BlockCopy(b, 0, c, ENCRYPTION_SIGNATURE.Length, b.Length);
            }
            catch(Exception ex)
            {
                this.AddError(ex);
            }
            return c;
        }

        private byte[] StripEncryptionSignature(byte[] b)
        {
            byte[] encryptedData = new byte[b.Length - ENCRYPTION_SIGNATURE.Length];
            try
            {
                System.Buffer.BlockCopy(b, ENCRYPTION_SIGNATURE.Length, encryptedData, 0, b.Length - ENCRYPTION_SIGNATURE.Length);
            }
            catch (Exception ex)
            {
                this.AddError(ex);
            }
            return encryptedData;
        }

        private bool IsEncrypted(byte[] b)
        {
            return ReadEncryptionSignature(b).SequenceEqual(ENCRYPTION_SIGNATURE);
        }

        private byte[] ReadEncryptionSignature(byte[] b)
        {
            byte[] encryptionSignature = new byte[ENCRYPTION_SIGNATURE.Length];
            try
            {
                System.Buffer.BlockCopy(b, 0, encryptionSignature, 0, ENCRYPTION_SIGNATURE.Length);
            }
            catch (Exception ex)
            {
                this.AddError(ex);
            }
            return encryptionSignature;
        }

        private void SaveFile(string filepath, byte[] bytes)
        {
            // Write the byte array to the other FileStream.
            using (FileStream fsNew = new FileStream(filepath, FileMode.Create, FileAccess.Write))
            {
                try
                {
                    fsNew.Write(bytes, 0, bytes.Length);
                }
                catch (Exception ex)
                {
                    this.AddError(ex);
                }
            }
        }

        private byte[] ReadFile(string filepath)
        {
            try
            {
                using (FileStream fsSource = new FileStream(filepath, FileMode.Open, FileAccess.Read))
                {

                    // Read the source file into a byte array.
                    byte[] bytes = new byte[fsSource.Length];
                    int numBytesToRead = (int)fsSource.Length;
                    int numBytesRead = 0;
                    while (numBytesToRead > 0)
                    {
                        // Read may return anything from 0 to numBytesToRead.
                        int n = fsSource.Read(bytes, numBytesRead, numBytesToRead);

                        // Break when the end of the file is reached.
                        if (n == 0)
                            break;

                        numBytesRead += n;
                        numBytesToRead -= n;
                    }
                    return bytes;
                }
            }
            catch(Exception ex)
            {
                this.AddError(ex);
                return (byte[])null;
            }
        }
    }
}
 </exception></exception></exception>

Test/usage DES 

Let's now see how to use/test the DES class 

String as input

1. Instantiate a new DES object.

2. Call method Crypto with input as string and passphrase, this would resturn decrypted    string.  

3.  Verify the decryption by calling the method Crypto with decrypted string obtained from     step2 and use the same passphrase.

4. DES object has  a property ErrorList, this would help you find any error/exception    occured during the Crypto operation 

File as input 

1. Instantiate a new DES object.

2. Call method Crypto with first parameter as path to file for operation, and second for    saving the result and third parameter as passphrase, this would resturn    success/failure(bool) of operation performed 

3.  Verify the decryption by calling the method Crypto appropriately. 

4. DES object has  a property ErrorList, this would help you find any error/exception     occured during the Crypto operation.  

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Kuthuparakkal.Crypto.Util;

namespace Kuthuparakkal.Crypto.Test
{
    class Program
    {
        static void Main(string[] args)
        {

            DES des1 = new DES();

            #region String as input
            string s1 = des1.Crypto("Kuthuparakkal", "mynewpassword");
            string s2 = des1.Crypto(s1, "mynewpassword");
            Console.WriteLine(s1);//Encrypted original string
            Console.WriteLine(s2);//Decrypted original string
            //Print if you have any errors in operation
            foreach (Exception ex in des1.ErrorList)
            {
                Console.WriteLine(ex.Message);
            }

            #endregion

            #region File as input

            DES des2 = new DES();
            string pathSource = @"C:\Test\something.exe";
            string pathNew = @"C:\Test\something_encrypted.exe";
            string pathDecrypted = @"C:\Test\something_decrypted.exe";
            bool suc = des2.Crypto(pathSource, pathNew, "myverynewpassword");

            //Print operation result
            Console.WriteLine("Operation Result : " + (suc?"Success": "Failed"));
            
            //Print if you have any errors in operation
            foreach (Exception ex in des2.ErrorList)
            {
                Console.WriteLine(ex.Message);
            }

            DES des3 = new DES();
            suc = des3.Crypto(pathNew, pathDecrypted, "myverynewpassword");

            //Print operation result
            Console.WriteLine("Operation Result : " + (suc ? "Success" : "Failed"));

            //Print if you have any errors in operation
            foreach (Exception ex in des3.ErrorList)
            {
                Console.WriteLine(ex.Message);
            }

            #endregion
            Console.ReadLine();


        }
    }
}
Points of Interest

I have consumed the library and created a windows application to protect my sensitive files. 

History

Added more detail description on methods and operations. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)