当前位置: 首页 > 编程语言 > C#

StopdecryptionbeforeEOFthrowsexception-Invalidpaddingandcannotdeleteshare

时间:2023-04-11 00:09:43 C#

StopdecryptionbeforeEOFthrowsexception:Invalidpaddingandcannotbedeleted我们有这样的场景:我们有大量的加密文件,在千兆字节的顺序,如果我们读到最后我们就可以正确地解密它们。问题发生在我们正在读取并检测到文件中的某个标志时,然后我们停止读取并调用reader.Close(),发生的是CryptographicException:“填充无效且无法删除”。被抛出。我有这个重现此行为的小控制台应用程序,测试它只是运行它,它会在你的C:驱动器中创建一个文件,然后当你按任何键时,它会逐行读取,当你按“q”时,它会停止。使用系统;使用System.IO;使用System.Security.Cryptography;namespaceencryptSample{classProgram{staticvoidMain(string[]args){vartransform=CreateCryptoTransform(true);//首先使用(FileStreamdestination=newFileStream("c:\test_enc.txt",FileMode.OpenOrCreate,FileAccess.Write,FileShare.ReadWrite))创建加密文件{using(CryptoStreamcryptoStream=newCryptoStream(destination,transform,CryptoStreamMode).Write)){using(StreamWritersource=newStreamWriter(cryptoStream)){for(inti=0;i=1000.conststringpassword="123456";AesManagedaes=newAesManaged();aes.BlockSize=aes.LegalBlockSizes[0].MaxSize;aes.KeySize=aes.LegalKeySizes[0].MaxSize;//注意:Rfc2898DeriveBytes初始化和随后对GetBytes的调用在加密和解密端必须完全相同,包括顺序。Rfc2898DeriveBytes密钥=newRfc2898DeriveBytes(密码、盐、迭代);aes.Key=key.GetBytes(aes.KeySize/8);aes.IV=key.GetBytes(aes.BlockSize/8);aes.Mode=CipherMode.CBC;aes.Padding=PaddingMode.PKCS7;ICryptoTransform变换=加密?aes.CreateEncryptor(aes.Key,aes.IV):aes.CreateDecryptor(aes.Key,aes.IV);返回变换;在我们原来的类中,我们让阅读器在Dispose()期间关闭。我的问题是,检查reader.EndOfStream是否为假然后捕获CryptographicException是否有效?还是加密/解密方式有问题?也许我们错过了什么。问候!更新2:我不知道为什么我在之前的更新中认为链接代码解决了这里的主要问题。显然,如果FlushFinalBlock()抛出,则不会调用_stream.Close()。有关我之前提到的错误的更新,请参阅此答案的修订历史。在Dispose期间抛出此异常(true)。从Dispose中抛出已经是一个设计缺陷(https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1065-do-not-raise-exceptions-in-unexpected-locations#dispose-methods),但更糟糕的是,因为这个异常甚至在底层流关闭之前就被抛出。这意味着任何接收到可能是CryptoStream的Stream的东西都需要解决这个问题并在“catch”块中关闭底层Stream本身(本质上需要引用完全不相关的东西),或者以某种方式提醒所有监听浏览器可能仍然打开(例如,“不要尝试删除底层文件-它仍然打开!”)。不,这是我书中的一个很大的疏忽,其他答案似乎没有解决根本问题。CryptoStream取得传入流的所有权,因此它需要在控制权离开Dispose(true)之前关闭底层流,故事结束。理想情况下,它也不应该抛出不是真正异常的东西(例如“我们提前停止阅读,因为解密数据格式错误,继续阅读是浪费时间”)。我们的解决方案基本上是这样的(更新:但要注意-正如WillKrause在评论中指出的那样,这可能会在私有_InputBuffer和_OutputBuffer字段中留下敏感信息,这些字段可以通过反射访问。版本4.5及更高版本。.NETFramework不有这个问题。):internalsealedclassSilentCryptoStream:CryptoStream{privatereadonlyStreamunderlyingStream;publicSilentCryptoStream(Streamstream,ICryptoTransformtransform,CryptoStreamModemodemode):base(stream){//流已经在基本构造函数中隐式验证为非空。this.underlyingStream=流;}protectedoverridevoidDispose(booldisposing){try{base.Dispose(disposing);}catch(CryptographicException){if(disposing){this.underlyingStream.Dispose();据我所知,当读取的最后一个字节不是有效的填充字节时,它会抛出异常。当您有意提前关闭流时,最后读取的字节很可能被视为“无效填充”并抛出异常。由于您是故意结束的,因此您应该安全地忽略该异常。Close调用Dispose(true)调用FlushFinalBlock抛出异常,因为这不是最终块。您可以通过重写Close方法使其不调用FlushFinalBlock来防止这种情况:Close(){this.Dispose(false);GC.SuppressFinalize(这个);(您还需要手动关闭底层流。)如果有效检查reader.EndOfStream为false,则捕获CryptographicException我认为没问题。你能关掉填充物吗?//aes.Padding=PaddingMode.PKCS7;aes.Padding=PaddingMode.None;我的解决方案是,在我的派生类中,将其添加到我的Dispose(bool)覆盖中:如果读取器没有一直读到流的末尾,它会在尝试//在Dispose()期间读取最后一个块时抛出异常。我们将通过为他们移动到流的末尾来解决这个问题。这避免了抛出的异常并//允许正确清理所有内容(处置、从内存中擦除等)。if((disposing)&&(CanRead)&&(m_TransformMode==CryptoStreamMode.Read)){constintBUFFER_SIZE=32768;byte[]buffer=newbyte[BUFFER_SIZE];while(Read(buffer,0,BUFFER_SIZE)==BUFFER_SIZE){}}base.Dispose(disposing);...通过确保始终读取流来避免CryptStream.Dispose中的内部问题。当然,你需要权衡一下你正在阅读的内容的性质,以确保它不会产生负面影响。仅将其用于已知有限长度的源。以上是C#学习教程:在EOF抛出异常前停止解密:padding无效,无法删除所有共享内容。如果对大家有用,需要了解更多C#学习教程,希望大家多多关注——本文来自网络合集,不代表立场,如涉及侵权,请点击右转联系管理员删除。如需转载请注明出处: