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:
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<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();
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;
try
{
ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
}
catch (Exception ex)
{
this.AddError(ex);
throw ex;
}
finally
{
TDESAlgorithm.Clear();
HashProvider.Clear();
}
return WriteEncryptionSignature(Results);
}
private byte[] Decrypt(byte[] DataToDecrypt, string Passphrase)
{
byte[] Results;
System.Text.UTF8Encoding UTF8 = new System.Text.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[] DataToDecryptNew = StripEncryptionSignature(DataToDecrypt);
try
{
ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
Results = Decryptor.TransformFinalBlock(DataToDecryptNew, 0, DataToDecryptNew.Length);
}
catch (Exception ex)
{
this.AddError(ex);
throw ex;
}
finally
{
TDESAlgorithm.Clear();
HashProvider.Clear();
}
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)
{
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))
{
byte[] bytes = new byte[fsSource.Length];
int numBytesToRead = (int)fsSource.Length;
int numBytesRead = 0;
while (numBytesToRead > 0)
{
int n = fsSource.Read(bytes, numBytesRead, numBytesToRead);
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.
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);
Console.WriteLine(s2);
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");
Console.WriteLine("Operation Result : " + (suc?"Success": "Failed"));
foreach (Exception ex in des2.ErrorList)
{
Console.WriteLine(ex.Message);
}
DES des3 = new DES();
suc = des3.Crypto(pathNew, pathDecrypted, "myverynewpassword");
Console.WriteLine("Operation Result : " + (suc ? "Success" : "Failed"));
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.