Self Driven Cryptographic Operations





4.00/5 (3 votes)
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.
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:
//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
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 _errorList ;
public List ErrorList
{
get { return _errorList; }
}
private void AddError(Exception ex)
{
_errorList.Add(ex);
}
public DES()
{
_errorList = new List();
}
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;
}
}
}
}
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.
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();
}
}
}
I have consumed the library and created a windows application to protect my sensitive files.
History
Added more detail description on methods and operations.