3

So I'm trying to encrypt / decrypt a word document using a Caesar cipher shift. My Caesar class works for images(.png) and (.txt) documents. However when I encrypt a word document (.docx) and decrypt that word document again the last byte get's modified. The following picture shows what I mean: enter image description here

All but the last byte get changed correctly. Instead of a NUL byte the modified document contains an SOH byte which is apparently a "Start of Header".

This is my caeser cipher class containing the enciper and decipher function:

        static char ciphChar(char ch, int key)
    {
        if (!char.IsLetter(ch))
        {
            return ch;
        }
        char d = char.IsUpper(ch) ? 'A' : 'a';
        return (char)((((ch + key) - d) % 26) + d);
    }

    public static string Cipher(string message, int key, bool cipherMode) 
    {
        if (cipherMode == false)
        {
            key = 26 - key;
        }
        string output = string.Empty;
        foreach (char ch in message)
            output += ciphChar(ch, key);
        return output;
    }

The encipher & decipher functions are also below (the Caeser cipher shifts the message by the number from the key. So for example if the message is 'a' and the key is 3, the message becomes 'd', because 'a' shifted 3 letters across the alphabet is 'd'):

        public static string Encipher(string input, int key)
    {
        return Cipher(input, key, true);
    }

    public static string Decipher(string input, int key)
    {
        return Cipher(input, key, false);
    }

To use the encipher function on a document I convert the document bytes to a string using Convert.ToBase64String. After I read in the bytes as a string I use my enciphe / deciphe function and convert the string back to bytes using the following code Convert.FromBase64String.

My code successfully encrypts & decrypts .txt files and .png files. However for .docx files the last byte doesn't decode back correctly.. I appreciate any guidance to solve my problem, thanks.

Edit 1: I've added some code so you can reconstruct the problem i'm dealing with.

  1. Create a class called caesarShift and copy the code from the caesar cipher and enciper/decipher functions written above in my original post.

  2. Create a main form with 3 buttons and 1 textbox. Copy the following code below:

        public static byte[] EncryptFile(string filePath)
    {
        byte[] fileBytes = File.ReadAllBytes(filePath);
        byte[] bytesEncrypted = Enciphe(fileBytes);
        File.WriteAllBytes(filePath, bytesEncrypted);
        return bytesEncrypted;
    }
    
    public static byte[] DecryptFile(string filePath)
    {
        byte[] fileBytes = File.ReadAllBytes(filePath);
        byte[] bytesDecrypted = Deciphe(fileBytes);
        File.WriteAllBytes(filePath, bytesDecrypted);
        return bytesDecrypted;
    }
    
    static byte[] Enciphe(byte[] file)
    {
        var fileToString = Convert.ToBase64String(file);
        string caeser;
        caeser = caesarShift.Encipher(fileToString, 3);
        file = Convert.FromBase64String(caeser); 
        return file;
    }
    
    static byte[] Deciphe(byte[] file)
    {
        var fileToString = Convert.ToBase64String(file);
        string caeser;
        caeser = caesarShift.Decipher(fileToString, 3);
        file = Convert.FromBase64String(caeser);
        return file;
    }
    
    private void button1_Click(object sender, EventArgs e)
    {
        OpenFileDialog openFileDialog1 = new OpenFileDialog();
        openFileDialog1.Filter = "encryptable files|*.rtf;*.docx;*.jpg;*.txt;*.png;";
        openFileDialog1.Title = "Select File";
        if (Directory.Exists(textBox1.Text))
        {
            openFileDialog1.InitialDirectory = textBox1.Text;
        }
        else
        {
            openFileDialog1.InitialDirectory = @"C:\";
        }
    
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            textBox1.Text = openFileDialog1.FileName;
        }
    }
    
    private void button2_Click(object sender, EventArgs e)
    {
       EncryptFile(textBox1.Text);
    }
    
    private void button3_Click(object sender, EventArgs e)
    {
        DecryptFile(textBox1.Text);
    }
    
  3. Create a dummy word document and test the encrypt / decrypt function. You can also test .jpg and .txt files (they are working).
Harry
  • 301
  • 1
  • 4
  • 9
  • I wonder something is missing during Base64 conversion, for example handling the padding equal sign during encryption/decryption? – Geno Chen Dec 02 '18 at 13:13
  • I'm not sure. All the other NUL bytes are being correctly encoded / decoded back to NUL. It's only the last byte that is converted wrong. My conversion code is literally just this: var fileToString = Convert.ToBase64String(file); where file equals the bytes[]. I then apply Encipher(fileToString, 3) and convert it back to bytes using file = Convert.FromBase64String(fileToString); – Harry Dec 02 '18 at 13:17
  • In regards to the encryption / decryption it would have to be a problem with either the ciphChar function I posted or the Cipher function. However I'm struggling to identify where the problem lies especially because my breakpoints aren't working in VS! – Harry Dec 02 '18 at 13:22
  • I tried your code with an online compiler, and it runs as expected for all the cases I can think about. So I wonder if there is some miracle there. Or if you can share this .docx file? – Geno Chen Dec 02 '18 at 13:30
  • Any word document I create results in the same error. My word document contains this text: Hello 123 testingABCDEFIKHJDSFLKIH repeated around 30 times. – Harry Dec 02 '18 at 13:42
  • Can you share executable code that demonstrates the issue? Hardcode file contents with `new byte[] { ... }`. – usr Dec 02 '18 at 18:06
  • I noticed you haven't shown any I/O. For most programming languages the general rule is: don't treat binary files as if they consist of characters/strings. Read them in and process them and write them out as bytes. – President James K. Polk Dec 02 '18 at 20:12
  • Thanks for your comments. @usr The problem definitely lies with the code above. The rest of my code literally just converts the file from bytes to string and vice-versa. – Harry Dec 02 '18 at 20:32
  • @James K Polk I understand that, but sometimes strings will also have to be encrypted/decrypted thus I want to do it using one function. The problem lies with the code above because when I convert the file bytes to string and back to bytes (without encrypting/decrypting) everything seems fine. That means the problem lies in the encryption / decryption code above, but I can't seem to figure out what. – Harry Dec 02 '18 at 20:32
  • Instead of arguing about it just show the code that reads the data in and converts it to a string prior to decryption. Also show the code that writes the result after en-decryption to a file. That is what we mean by "complete" in [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) – President James K. Polk Dec 02 '18 at 20:41
  • At this point it is unlikely that someone can stare at this code and see the issue (or the issue is not here). Therefore, we need executable code. Clone your solution and cut the code down to a few lines. I am meanwhile voting to close because no further progress can be made at this time. – usr Dec 02 '18 at 21:18
  • @usr I've added an edit with some code you can use to quickly create your own project and test my problem. If there's anything else you need please comment! – Harry Dec 03 '18 at 11:45
  • @JamesKPolk I've added an edit with some code you can quickly reconstruct the problem with. – Harry Dec 03 '18 at 11:53
  • It is strange that only the last byte is affected and it flips from 0 to 1. This might have to do with the fact that base64 has a special ending depending on file size. Try testing with the data `new byte[] { 0 }`, `new byte[] { 0, 0 }`, `new byte[] { 0, 0, 0 }`. Note, that you can still reduce the sample code by a lot and make it executable. I am not going to take the time to create word files etc. when you could just provide code that executes as is. – usr Dec 03 '18 at 16:19
  • @usr Thanks for your reply. I've done the three tests and for the first one the "NULL" came back as "SOH". On the second test, "NULL NULL" came back as "NULL SOH" and on the third test, "NULL NULL NULL" came back as "NULL NULL SOH". So it's always the last byte which is decrypted wrong. I also compared the base 64 strings. Original "AAAAAA==". Encrypted & decrypted it becomes "AAAAAX==" – Harry Dec 03 '18 at 17:33

0 Answers0