Creating my own substitution cipher app in .NET

This project is JUST FOR FUN and it is totally unsecure for any practical use.

What is cipher?

In cryptography, a cipher is an algorithm for performing encryption or decryption of information into some kind of secret codes. For a substitution cipher, it’s a method of encoding characters from the plaintext message into something called ciphertext. There are few popular cipher algorithms such as steganography, ROT13, Caesar shift cipher, Vigenere, etc. which I can simply use. There are also the modern unbreakable of encryption algorithms available such as Triple DES, RSA, Blowfish, Twofish, AES, etc.

But today I want to create my own cipher program and experiment with it. I decided to use a substitution method with my own designed keys and encoding algorithm. So, I have created my own cipher application in .NET using C# WPF which obviously I have named it as Heiswayi Nrird Cipher Program.

Image: Application UI
Application UI

Here’s how I implemented my encoding method

Encryption and decryption dictionary

First, since this is a substitution cipher, so I have to define two my own dictionaries; one for encryption, another one for decryption.

Encryption dictionary:

var encryptionDictionary = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase)
{
    { "a", "h01" },
    { "b", "e02" },
    { "c", "i03" },
    { "d", "s04" },
    { "e", "w05" },
    { "f", "a06" },
    { "g", "y07" },
    { "h", "i08" },
    { "i", "n09" },
    { "j", "r10" },
    { "k", "i11" },
    { "l", "r12" },
    { "m", "d13" },
    { "n", "h14" },
    { "o", "e15" },
    { "p", "i16" },
    { "q", "s17" },
    { "r", "w18" },
    { "s", "a19" },
    { "t", "y20" },
    { "u", "i21" },
    { "v", "n22" },
    { "w", "r23" },
    { "x", "i24" },
    { "y", "r25" },
    { "z", "d26" },
    { "0", "h27" },
    { "1", "e28" },
    { "2", "i29" },
    { "3", "s30" },
    { "4", "w31" },
    { "5", "a32" },
    { "6", "y33" },
    { "7", "i34" },
    { "8", "n35" },
    { "9", "r36" },
    { "~", "i37" },
    { "`", "r38" },
    { "!", "d39" },
    { "@", "h40" },
    { "#", "e41" },
    { "$", "i42" },
    { "%", "s43" },
    { "^", "w44" },
    { "&", "a45" },
    { "*", "y46" },
    { "(", "i47" },
    { ")", "n48" },
    { "-", "r49" },
    { "_", "i50" },
    { "=", "r51" },
    { "+", "d52" },
    { "[", "h53" },
    { "{", "e54" },
    { "]", "i55" },
    { "}", "s56" },
    { @"\", "w57" },
    { "|", "a58" },
    { ";", "y59" },
    { ":", "i60" },
    { "'", "n61" },
    { "\"", "r62" },
    { ",", "i63" },
    { "<", "r64" },
    { ".", "d65" },
    { ">", "h66" },
    { "/", "e67" },
    { "?", "i68" },
    { " ", "s69" },
    { "\n", "w70" },
};

Decryption dictionary:

var decryptionDictionary = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase)
{
    { "h01", "a" },
    { "e02", "b" },
    { "i03", "c" },
    { "s04", "d" },
    { "w05", "e" },
    { "a06", "f" },
    { "y07", "g" },
    { "i08", "h" },
    { "n09", "i" },
    { "r10", "j" },
    { "i11", "k" },
    { "r12", "l" },
    { "d13", "m" },
    { "h14", "n" },
    { "e15", "o" },
    { "i16", "p" },
    { "s17", "q" },
    { "w18", "r" },
    { "a19", "s" },
    { "y20", "t" },
    { "i21", "u" },
    { "n22", "v" },
    { "r23", "w" },
    { "i24", "x" },
    { "r25", "y" },
    { "d26", "z" },
    { "h27", "0" },
    { "e28", "1" },
    { "i29", "2" },
    { "s30", "3" },
    { "w31", "4" },
    { "a32", "5" },
    { "y33", "6" },
    { "i34", "7" },
    { "n35", "8" },
    { "r36", "9" },
    { "i37", "~" },
    { "r38", "`" },
    { "d39", "!" },
    { "h40", "@" },
    { "e41", "#" },
    { "i42", "$" },
    { "s43", "%" },
    { "w44", "^" },
    { "a45", "&" },
    { "y46", "*" },
    { "i47", "(" },
    { "n48", ")" },
    { "r49", "-" },
    { "i50", "_" },
    { "r51", "=" },
    { "d52", "+" },
    { "h53", "[" },
    { "e54", "{" },
    { "i55", "]" },
    { "s56", "}" },
    { "w57", @"\" },
    { "a58", "|" },
    { "y59", ";" },
    { "i60", ":" },
    { "n61", "'" },
    { "r62", "\"" },
    { "i63", "," },
    { "r64", "<" },
    { "d65", "." },
    { "h66", ">" },
    { "e67", "/" },
    { "i68", "?" },
    { "s69", " " },
    { "w70", "\n" },
};

Reversal of characters

Next, I will convert the input string into character array, reverse it, then declare a new string variable.

char[] charArrayReverse = data1.ToCharArray(); //data1 is input string
Array.Reverse(charArrayReverse);
string data2 = new string(charArrayReverse);

Substitution of characters

Next, I will loop through each input character, replace it with my substitution dictionary, and then create new string using StringBuilder.

StringBuilder sb = new StringBuilder();
for (int i = 0; i < data2.Length; i++)
{
    foreach (KeyValuePair<string, string> kvp in encryptionDictionary)
    {
        if (data2[i].ToString() == kvp.Key)
        {
            sb.Append(kvp.Value);
        }
    }
}
string data3 = sb.ToString();

Append hashed password and generating signature

Finally, I will prepend the hash code of the password, then append the output characters by separating it using $ character, then append again with my signature characters which is $HN.

outputData = hashCode.ToUpper() + "$" + data4.ToUpper() + "$HN";

Example of the encrypted string for “Hello”:

D7640066$80I50W21R21R51E$HN

D7640066 is a password hash, 80I50W21R21R51E is the encrypted string and $HN is my signature characters.

Decrypting the message

When decrypt the encrypted message, I will check for my signature characters $HN in the string, if it’s not present, I will consider it as INVALID data. Then, I will validate the entered password for decryption with the hash code - e.g. D7640066. Once the password is correct, then I will proceed with the decoding method.

Exporting as .HNC file

Another feature that I added to this application is a functionality to export into a compressed file in .HNC file format using GZipStream. This will make the file content unreadable by human, hardly to recognize what kind of content inside that file.

So simple huh?!

Here’s why this cipher is not practically suitable for the real world use case

Substitution cipher is weak, easily broken and the cryptanalyst can deduce the probable meaning of the most common symbols by analyzing the frequency distribution of the ciphertext.

Source code and download

If you like to see the full source code of this application, you can download it from my GitHub repository. If you want to try to play with the compiled version, you can download the binary here or you can compile it by yourself from the source code that is provided.