Reputation: 1842
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
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
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