无法解密代码时如何防止异常?

How to prevent exception when code cannot be decrypted?

我有一个 public 网络挂钩,服务正在调用该网络挂钩以发送我的网站通知。在这个网络钩子中,我期待一个加密的令牌。当我获得令牌时,我使用预定义的密钥对其进行解密并检查令牌是否符合我的预期。这很好用。

当一个未加密或错误的令牌被传递到函数中时,解密当然会失败。这没关系,但我不想在发生这种情况时生成异常。如果某些黑客在一秒钟内针对我的 Web 钩子创建了 1,000,000 个错误请求,并且每个请求需要 1 秒来处理一个巨大的异常,它会使我的服务器崩溃。

到目前为止,这是我的解密代码:

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;
    }

当我故意传入一个未加密的字符串时,它在 cs.Close() The input data is not a complete block 处给了我一个异常。我不确定这里到底检查了什么,所以我不知道如何防止它。

如何重构此代码,以便在无法解密字符串时不会抛出异常?

您可以 catch 异常并根据需要对其进行处理。 (记录、重新路由、忽略等)。 Try/catch 文档可在此处找到:https://msdn.microsoft.com/en-us/library/0yd65esw.aspx

当您使用 CBC 等非认证模式解密密文时,填充错误可以检测到错误的密钥或错误的最终块,概率大约为 255 次 256 次。当最后一个块被损坏并且找不到有效的填充时,就会发生这种情况。 PKCS#7 padding默认使用,它有一个可以验证的特殊结构。

您可以请求解密器不要以任何方式尝试取消填充:

encryptor.Padding = PaddingMode.None;

但是你需要自己取消填充(填充字节只能在 AES 的 1 到 16 范围内):

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;
}