【问题标题】:Try/Catch/Finally with Encryption ResourcesTry/Catch/Final 使用加密资源
【发布时间】:2013-10-24 20:47:26
【问题描述】:

我的网站上有以下代码。它将 URL 查询作为名为 commandencrypted 的字符串。我有一个 try/catch/finally 块,它将加密的命令复制到内存流,然后将其解密回字符串。如果 try 块中出现任何错误,它们将在 catch 块中处理。但是,我想确保在 finally 块中关闭所有使用的资源。当提供无效的 URL 查询命令时,我在尝试关闭 csencryptedcommand CryptoStream 时收到异常。异常详情跟在代码后面。

    // Resources used for storing and decrypting the encrypted client command
    MemoryStream msencryptedcommand = null;
    RijndaelManaged rmencryptedcommand = null;
    CryptoStream csencryptedcommand = null;
    StreamReader srdecryptedcommand = null;
    MemoryStream msdecryptedcommand = null;

    try
    {
        // Copy the encrypted client command (where two characters represent a byte) to a memorystream
        msencryptedcommand = new MemoryStream();
        for (int i = 0; i < encryptedcommand.Length; )
        {
            msencryptedcommand.WriteByte(Byte.Parse(encryptedcommand.Substring(i, 2), NumberStyles.HexNumber));
            i = i + 2;
        }
        msencryptedcommand.Flush();
        msencryptedcommand.Position = 0;

        // Define parameters used for decryption
        byte[] key = new byte[] { //bytes hidden// };
        byte[] iv = new byte[] { //bytes hidden// };
        rmencryptedcommand = new RijndaelManaged();
        csencryptedcommand = new CryptoStream(msencryptedcommand, rmencryptedcommand.CreateDecryptor(key, iv), CryptoStreamMode.Read);
        msdecryptedcommand = new MemoryStream();

        // Decrypt the client command
        int decrytptedbyte;
        while ((decrytptedbyte = csencryptedcommand.ReadByte()) != -1)
        {
            msdecryptedcommand.WriteByte((byte)decrytptedbyte);
        }

        // Store the decrypted client command as a string
        srdecryptedcommand = new StreamReader(msdecryptedcommand);
        srdecryptedcommand.BaseStream.Position = 0;
        string decryptedcommand = srdecryptedcommand.ReadToEnd();
    }
    catch (Exception ex)
    {
        ErrorResponse("Invalid URL Query", context, ex.ToString());
        return;
    }
    finally
    {
        // If any resources were used, close or clear them
        if (srdecryptedcommand != null)
        {
            srdecryptedcommand.Close();
        }

        if (msdecryptedcommand != null)
        {
            msdecryptedcommand.Close();
        }

        if (csencryptedcommand != null)
        {
            csencryptedcommand.Close();
        }

        if (rmencryptedcommand != null)
        {
            rmencryptedcommand.Clear();
        }

        if (msencryptedcommand != null)
        {
            msencryptedcommand.Close();
        }
    }

发生以下未处理的异常:System.Security.Cryptography.CryptographicException:要解密的数据长度无效。在 System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 在 System.Security.Cryptography.CryptoStream.FlushFinalBlock() 在 System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 在 System update.ashx 中 dongleupdate.ProcessRequest(HttpContext context) 处的 .IO.Stream.Close():System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 处 System.Web.HttpApplication 的第 92 行。 ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) URL Referrer: User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36 Request URL: update.ashx?aa

编辑:

我将代码更改为使用 using 语句。这也是确保关闭所有资源的有效方法吗?

    try
    {
        // Copy the encrypted client command (where two characters represent a byte) to a memorystream
        using (MemoryStream msencryptedcommand = new MemoryStream())
        {
            for (int i = 0; i < encryptedcommand.Length; )
            {
                msencryptedcommand.WriteByte(Byte.Parse(encryptedcommand.Substring(i, 2), NumberStyles.HexNumber));
                i = i + 2;
            }
            msencryptedcommand.Flush();
            msencryptedcommand.Position = 0;

            // Define parameters used for decryption
            byte[] key = new byte[] { //bytes hidden// };
            byte[] iv = new byte[] { //bytes hidden// };
            using (RijndaelManaged rmencryptedcommand = new RijndaelManaged())
            {
                using (CryptoStream csencryptedcommand = new CryptoStream(msencryptedcommand, rmencryptedcommand.CreateDecryptor(key, iv), CryptoStreamMode.Read))
                {
                    using (MemoryStream msdecryptedcommand = new MemoryStream())
                    {

                        // Decrypt the client command
                        int decrytptedbyte;
                        while ((decrytptedbyte = csencryptedcommand.ReadByte()) != -1)
                        {
                            msdecryptedcommand.WriteByte((byte)decrytptedbyte);
                        }

                        // Store the decrypted client command as a string
                        using (StreamReader srdecryptedcommand = new StreamReader(msdecryptedcommand))
                        {
                            srdecryptedcommand.BaseStream.Position = 0;
                            string decryptedcommand = srdecryptedcommand.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        ErrorResponse("Invalid URL Query", context, ex.ToString());
        return;
    }

【问题讨论】:

  • 我发现这样的编码风格在很大程度上是不可读的。无论哪种方式,您都应该对任何您知道可能无效的输入进行输入验证,然后再将它们提交给上述方法。我还会考虑使用“使用”语句来确保正确处置和释放资源。
  • 哪一行报错了?
  • Blam:如上所述,我在尝试关闭 csencryptedcommand CryptoStream 时收到异常。 csencryptedcommand.Close();
  • daveL:在尝试解密之前如何对加密数据执行输入验证?
  • 对于您在三个 using 语句之后的更新代码,您不需要在前两个 using 语句之后使用 {,您可以将它们全部链接起来,只需使用最后一个{

标签: c# asp.net .net encryption try-catch


【解决方案1】:

将您的CryptoStream 包装在using 块中,然后它将自动关闭并通过Dispose Pattern 为您处理,如下所示:

using(csencryptedcommand = new CryptoStream(msencryptedcommand, 
    rmencryptedcommand.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
    // Do things with crypto stream here
}

阅读using Statement (C# Reference) 文档了解更多信息。

更新:

使用Reflector,这里是CryptoStreamDispose方法的代码:

protected override void Dispose(bool disposing)
{
    try
    {
        if (disposing)
        {
            if (!this._finalBlockTransformed)
            {
                this.FlushFinalBlock();
            }
            this._stream.Close();
        }
    }
    finally
    {
        try
        {
            this._finalBlockTransformed = true;
            if (this._InputBuffer != null)
            {
                Array.Clear(this._InputBuffer, 0, this._InputBuffer.Length);
            }
            if (this._OutputBuffer != null)
            {
                Array.Clear(this._OutputBuffer, 0, this._OutputBuffer.Length);
            }
            this._InputBuffer = null;
            this._OutputBuffer = null;
            this._canRead = false;
            this._canWrite = false;
        }
        finally
        {
            base.Dispose(disposing);
        }
    }
}

注意:如您所见,通过此行有一个显式调用来关闭流:

this._stream.Close();

【讨论】:

  • 卡尔:谢谢。那行得通。您是否建议为其他资源(srdecryptedcommand、msdecryptedcommand、rmencryptedcommand、msencryptedcommand)创建 using 块并删除 finally 块?
  • 我更改了代码。在确保资源关闭的情况下,这也是一种有效的方法吗?请参阅上述问题中的新代码。
  • @bbs_chad - 是的,我建议在任何实现 IDisposable 接口的对象周围实现一个 using 块,代码中的所有类型都可以直接或通过它们的继承来实现链。
  • @bbs_chad - 请参阅UPDATE 以了解using 块是否确保资源已关闭。
【解决方案2】:

我认为您使用的所有对象都实现了 IDisposable,所以如果我是您,我会将它们全部包装在 using 语句中,以便它们会自动为您清理,即

using(msencryptedcommand = new MemoryStream())
{
    ....
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 2015-12-07
    • 2021-11-26
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 2012-06-19
    相关资源
    最近更新 更多