C# 填充无效,无法删除?

C# 填充无效,无法删除?,c#,cryptography,C#,Cryptography,我在网上查找了这个异常对于我的程序意味着什么,但似乎找不到解决方案,也找不到它发生在我的特定程序上的原因。我一直在使用msdn提供的示例,使用Rijndael算法对XML文档进行加密和解密。加密工作正常,但当我尝试解密时,会出现以下异常: 填充无效,无法删除 有人能告诉我我能做些什么来解决这个问题吗?下面的代码是我获取密钥和其他数据的地方。如果cryptoMode为false,它将调用decrypt方法,这是发生异常的地方: public void Cryptography(XmlDocumen

我在网上查找了这个异常对于我的程序意味着什么,但似乎找不到解决方案,也找不到它发生在我的特定程序上的原因。我一直在使用msdn提供的示例,使用Rijndael算法对XML文档进行加密和解密。加密工作正常,但当我尝试解密时,会出现以下异常:

填充无效,无法删除

有人能告诉我我能做些什么来解决这个问题吗?下面的代码是我获取密钥和其他数据的地方。如果cryptoMode为false,它将调用decrypt方法,这是发生异常的地方:

public void Cryptography(XmlDocument doc, bool cryptographyMode)
{
    RijndaelManaged key = null;
    try
    {
    // Create a new Rijndael key.
    key = new RijndaelManaged();
    const string passwordBytes = "Password1234"; //password here 

    byte[] saltBytes = Encoding.UTF8.GetBytes("SaltBytes");
    Rfc2898DeriveBytes p = new Rfc2898DeriveBytes(passwordBytes, saltBytes);
    // sizes are devided by 8 because [ 1 byte = 8 bits ] 
    key.IV = p.GetBytes(key.BlockSize/8);
    key.Key = p.GetBytes(key.KeySize/8);

    if (cryptographyMode)
    {
        Ecrypt(doc, "Content", key);
    }
    else
    {
        Decrypt(doc, key);
    }

    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.Message);
    }
    finally
    {
    // Clear the key.
    if (key != null)
    {
        key.Clear();
    }
    }

}

private void Decrypt(XmlDocument doc, SymmetricAlgorithm alg)
{
    // Check the arguments.  
    if (doc == null)
    throw new ArgumentNullException("Doc");
    if (alg == null)
    throw new ArgumentNullException("alg");

    // Find the EncryptedData element in the XmlDocument.
    XmlElement encryptedElement = doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;

    // If the EncryptedData element was not found, throw an exception.
    if (encryptedElement == null)
    {
    throw new XmlException("The EncryptedData element was not found.");
    }


    // Create an EncryptedData object and populate it.
    EncryptedData edElement = new EncryptedData();
    edElement.LoadXml(encryptedElement);

    // Create a new EncryptedXml object.
    EncryptedXml exml = new EncryptedXml();


    // Decrypt the element using the symmetric key.
    byte[] rgbOutput = exml.DecryptData(edElement, alg); <----  I GET THE EXCEPTION HERE
    // Replace the encryptedData element with the plaintext XML element.
    exml.ReplaceData(encryptedElement, rgbOutput);

}
public void加密(XmlDocument doc,bool cryptographyMode)
{
RijndaelManaged key=null;
尝试
{
//创建一个新的Rijndael密钥。
key=new RijndaelManaged();
const string passwordBytes=“Password1234”//此处输入密码
byte[]saltBytes=Encoding.UTF8.GetBytes(“saltBytes”);
Rfc2898DeriveBytes p=新的Rfc2898DeriveBytes(passwordBytes,saltBytes);
//由于[1字节=8位]
key.IV=p.GetBytes(key.BlockSize/8);
key.key=p.GetBytes(key.KeySize/8);
if(加密模式)
{
电子文件(文档,“内容”,密钥);
}
其他的
{
解密(文档、密钥);
}
}
捕获(例外情况除外)
{
MessageBox.Show(例如Message);
}
最后
{
//清除钥匙。
if(key!=null)
{
键。清除();
}
}
}
私有无效解密(XmlDocument文档,对称算法alg)
{
//检查参数。
如果(doc==null)
抛出新的ArgumentNullException(“Doc”);
如果(alg==null)
抛出新的ArgumentNullException(“alg”);
//在XmlDocument中查找EncryptedData元素。
XmlElement encryptedElement=doc.GetElementsByTagName(“EncryptedData”)[0]作为XmlElement;
//如果未找到EncryptedData元素,则引发异常。
如果(encryptedElement==null)
{
抛出新的XmlException(“未找到EncryptedData元素”);
}
//创建EncryptedData对象并填充它。
EncryptedData edElement=新的EncryptedData();
LoadXml(encryptedElement);
//创建一个新的EncryptedXml对象。
EncryptedXml exml=新的EncryptedXml();
//使用对称密钥解密元素。

byte[]rgbOutput=exml。DecryptData(edElement,alg);Rijndael/AES是一个块密码。它加密128位(16个字符)块中的数据。用于确保消息的最后一个块始终大小正确


您的解密方法需要默认的填充,但找不到。正如@NetSquirrel所说,您需要显式设置加密和解密的填充。除非您有其他原因,否则请使用PKCS#7填充。

确保用于加密和解密的密钥相同填充方法即使未显式设置,也应允许正确的解密/加密(如果未设置,它们将是相同的)。但是,如果出于某种原因使用与加密不同的密钥集进行解密,则会出现以下错误:

填充无效,无法删除


如果您正在使用某些算法动态生成不起作用的密钥。加密和解密的密钥必须相同。一种常见的方法是让调用方在encryption methods类的构造函数中提供密钥,以防止加密/解密过程参与这些项的创建。重点是s在手头的任务中(加密和解密数据),并且要求调用方提供
iv
密钥

为了便于人们搜索,可能需要检查解密的输入。在我的情况下,发送解密的信息是(错误的)作为空字符串进入。这导致填充错误


这可能与rossum的答案有关,但认为值得一提。

另一种情况,也是为了人们搜索的利益

对我来说,这个错误发生在Dispose()方法中,该方法掩盖了以前与加密无关的错误


一旦修复了另一个组件,这个异常就消失了。

我在手动编辑文件中的加密字符串(使用记事本)时遇到了这个填充错误,因为我想测试如果我的加密内容被手动更改,解密函数将如何运行

对我来说,解决办法是放置一个

        try
            decryption stuff....
        catch
             inform decryption will not be carried out.
        end try

就像我说的,我的填充错误是因为我使用记事本手动键入解密文本。也许我的答案可以引导您找到解决方案。

经过几次战斗,我终于解决了问题。
(注:我使用标准AES作为对称算法。此答案可能不适用 对每个人来说。)

  • 更改算法类。将
    RijndaelManaged
    类替换为
    AES Managed
    one
  • 不要显式设置算法类的
    KeySize
    ,将其保留为默认值。
    (这是非常重要的一步。我认为KeySize属性中存在错误。)

  • 以下是您要检查哪些参数可能遗漏的列表:


    • (字节数组,对于不同的密钥大小,长度必须正好是16、24、32字节中的一个。)
    • IV
      (字节数组,16字节)
    • 密码模式
      (CBC、CFB、CTS、ECB、OFB中的一个)
    • 填充模式
      (ANSIX923、ISO10126、无、PKCS7、零中的一个)

    如果编码和解码使用相同的密钥和初始化向量,则此问题不是来自数据解码,而是来自数据编码

    在对CryptoStream对象调用Write方法之后,必须始终在Close方法之前调用FlushFinalBlock方法

    CryptoStream.FlushFinalBlock方法上的MSDN文档说明:
    “调用Close方法将调用FlushFinalBlock…”

    这是错误的
    if (Sec.IsFileEncrypted(e.File.FullName))
    {
        var stream = Sec.Decrypt(e.File.FullName);
    } 
    else
    {
        // non-encrypted scenario  
    }
    
    import (
        "crypto/aes"
        "crypto/cipher"
        "crypto/sha1"
        "encoding/base64"
        "io/ioutil"
        "log"
    
        "golang.org/x/crypto/pbkdf2"
    )
    
    func decryptFile(filename string, saltBytes []byte, masterPassword []byte) (artifact string) {
    
        const (
            keyLength         int = 256
            rfc2898Iterations int = 6
        )
    
        var (
            encryptedBytesBase64 []byte // The encrypted bytes as base64 chars
            encryptedBytes       []byte // The encrypted bytes
        )
    
        // Load an encrypted file:
        if bytes, bytesErr := ioutil.ReadFile(filename); bytesErr != nil {
            log.Printf("[%s] There was an error while reading the encrypted file: %s\n", filename, bytesErr.Error())
            return
        } else {
            encryptedBytesBase64 = bytes
        }
    
        // Decode base64:
        decodedBytes := make([]byte, len(encryptedBytesBase64))
        if countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64); decodedErr != nil {
            log.Printf("[%s] An error occur while decoding base64 data: %s\n", filename, decodedErr.Error())
            return
        } else {
            encryptedBytes = decodedBytes[:countDecoded]
        }
    
        // Derive key and vector out of the master password and the salt cf. RFC 2898:
        keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
        keyBytes := keyVectorData[:keyLength/8]
        vectorBytes := keyVectorData[keyLength/8:]
    
        // Create an AES cipher:
        if aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
            log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
            return
        } else {
    
            // CBC mode always works in whole blocks.
            if len(encryptedBytes)%aes.BlockSize != 0 {
                log.Printf("[%s] The encrypted data's length is not a multiple of the block size.\n", filename)
                return
            }
    
            // Reserve memory for decrypted data. By definition (cf. AES-CBC), it must be the same lenght as the encrypted data:
            decryptedData := make([]byte, len(encryptedBytes))
    
            // Create the decrypter:
            aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
    
            // Decrypt the data:
            aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
    
            // Cast the decrypted data to string:
            artifact = string(decryptedData)
        }
    
        return
    }
    
    import (
        "crypto/aes"
        "crypto/cipher"
        "crypto/sha1"
        "encoding/base64"
        "github.com/twinj/uuid"
        "golang.org/x/crypto/pbkdf2"
        "io/ioutil"
        "log"
        "math"
        "os"
    )
    
    func encryptFile(filename, artifact string, masterPassword []byte) (status bool) {
    
        const (
            keyLength         int = 256
            rfc2898Iterations int = 6
        )
    
        status = false
        secretBytesDecrypted := []byte(artifact)
    
        // Create new salt:
        saltBytes := uuid.NewV4().Bytes()
    
        // Derive key and vector out of the master password and the salt cf. RFC 2898:
        keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
        keyBytes := keyVectorData[:keyLength/8]
        vectorBytes := keyVectorData[keyLength/8:]
    
        // Create an AES cipher:
        if aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
            log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
            return
        } else {
    
            // CBC mode always works in whole blocks.
            if len(secretBytesDecrypted)%aes.BlockSize != 0 {
                numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
                enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
                copy(enhanced, secretBytesDecrypted)
                secretBytesDecrypted = enhanced
            }
    
            // Reserve memory for encrypted data. By definition (cf. AES-CBC), it must be the same lenght as the plaintext data:
            encryptedData := make([]byte, len(secretBytesDecrypted))
    
            // Create the encrypter:
            aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
    
            // Encrypt the data:
            aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
    
            // Encode base64:
            encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
            base64.StdEncoding.Encode(encodedBytes, encryptedData)
    
            // Allocate memory for the final file's content:
            fileContent := make([]byte, len(saltBytes))
            copy(fileContent, saltBytes)
            fileContent = append(fileContent, 10)
            fileContent = append(fileContent, encodedBytes...)
    
            // Write the data into a new file. This ensures, that at least the old version is healthy in case that the
            // computer hangs while writing out the file. After a successfully write operation, the old file could be
            // deleted and the new one could be renamed.
            if writeErr := ioutil.WriteFile(filename+"-update.txt", fileContent, 0644); writeErr != nil {
                log.Printf("[%s] Was not able to write out the updated file: %s\n", filename, writeErr.Error())
                return
            } else {
                if renameErr := os.Rename(filename+"-update.txt", filename); renameErr != nil {
                    log.Printf("[%s] Was not able to rename the updated file: %s\n", fileContent, renameErr.Error())
                } else {
                    status = true
                    return
                }
            }
    
            return
        }
    }
    
    public static string FromFile(string filename, byte[] saltBytes, string masterPassword)
    {
        var iterations = 6;
        var keyLength = 256;
        var blockSize = 128;
        var result = string.Empty;
        var encryptedBytesBase64 = File.ReadAllBytes(filename);
    
        // bytes -> string:
        var encryptedBytesBase64String = System.Text.Encoding.UTF8.GetString(encryptedBytesBase64);
    
        // Decode base64:
        var encryptedBytes = Convert.FromBase64String(encryptedBytesBase64String);
        var keyVectorObj = new Rfc2898DeriveBytes(masterPassword, saltBytes.Length, iterations);
        keyVectorObj.Salt = saltBytes;
        Span<byte> keyVectorData = keyVectorObj.GetBytes(keyLength / 8 + blockSize / 8);
        var key = keyVectorData.Slice(0, keyLength / 8);
        var iv = keyVectorData.Slice(keyLength / 8);
    
        var aes = Aes.Create();
        aes.Padding = PaddingMode.Zeros;
        // or ... aes.Padding = PaddingMode.None;
        var decryptor = aes.CreateDecryptor(key.ToArray(), iv.ToArray());
        var decryptedString = string.Empty;
    
        using (var memoryStream = new MemoryStream(encryptedBytes))
        {
            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            {
                using (var reader = new StreamReader(cryptoStream))
                {
                    decryptedString = reader.ReadToEnd();
                }
            }
        }
    
        return result;
    }
    
    // CBC mode always works in whole blocks.
    if len(secretBytesDecrypted)%aes.BlockSize != 0 {
        numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
        enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
        copy(enhanced, secretBytesDecrypted)
        secretBytesDecrypted = enhanced
    }
    
    enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
    copy(enhanced, secretBytesDecrypted)
    
    //...
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aesCrypto.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(rawCipherText, 0, rawCipherText.Length);
        }
    
        return Encoding.Unicode.GetString(ms.ToArray());
    }
    
    //...
    using MemoryStream ms = new MemoryStream();
    using CryptoStream cs = new CryptoStream(ms, aesCrypto.CreateDecryptor(), CryptoStreamMode.Write);
    
    cs.Write(rawCipherText, 0, rawCipherText.Length);
    cs.FlushFinalBlock();
    
    return Encoding.Unicode.GetString(ms.ToArray());
    
    aes.Padding = PaddingMode.Zeros;