AlbatrossCafe
AlbatrossCafe

Reputation: 1842

How to prevent exception when code cannot be decrypted?

I have a public web hook that a service is calling in order to send my website notifications. In this web hook, I am expecting an encrypted token. When I get the token, I decrypt it using a predefined key and check that the token is what I was expecting. This works fine.

When an unencrypted or bad token is passed into the function, the decryption will of course fail. This is OK, but I don't want an exception to be generated when this happens. If some hacker creates 1,000,000 bad requests against my web hook in a second and each request takes 1 second to process a huge exception, it will crash my server.

Here is my decryption code so far:

public static string Decrypt(string cipherText, string key)
    {
        string EncryptionKey = key;
        cipherText = cipherText.Replace(" ", "+");

        //I added this to prevent exception when trying to Convert.FromBase64String()
        if (cipherText.Length % 4 != 0)
        {
            //cipherText must be a length that is a multiple of 4, otherwise it will fail
            return null;
        }

        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();  //currently giving exception HERE
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

It is giving me an exception at cs.Close() The input data is not a complete block when I intentionally pass in a non-encrypted string. I am not sure what exactly is being checked here, so I don't know how to prevent it.

How can I refactor this code so that if a string cannot be decrypted it won't throw me an exception?

Upvotes: 0

Views: 1339

Answers (2)

Artjom B.
Artjom B.

Reputation: 61952

When you decrypt a ciphertext with a non-authenticated mode like CBC, an padding error can detect a wrong key or wrong final block with a probability of roughly 255 times of 256 times. This happens when the final block is corrupted and no valid padding can be found. PKCS#7 padding is used by default and it has a special structure that can be validated.

You can request the decryptor not to attempt unpadding in any way with:

encryptor.Padding = PaddingMode.None;

but then you need to unpad yourself (padding bytes can be only in the range of 1 to 16 for AES):

var ctBytes = ms.ToArray();
var last = ctBytes[ctBytes.Length-1];
if (last < 17 && last > 0) {
    cipherText = Encoding.Unicode.GetString(ctBytes.Take(ctBytes.Length - last));
} else {
    ciphertext = null;
}

Upvotes: 0

T. Frick
T. Frick

Reputation: 136

You can catch the exception and do with it what you want. (log, reroute, ignore, etc). Try/catch documentation can be found here: https://msdn.microsoft.com/en-us/library/0yd65esw.aspx

Upvotes: 3

Related Questions