You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#使用MS库实现XML加密时AES256解密出现填充无效错误

XML加密解密时出现“Padding is invalid and cannot be removed”错误

我尝试使用微软库执行XML加密操作,但解密时持续报错:

"Padding is invalid and cannot be removed."

加密代码:

public static string Encrypt(string plainText, string key)
{
    try
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException("key");

        // Derive a new Salt and IV from the Key
        using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, _saltSize))
        {
            var saltBytes = keyDerivationFunction.Salt;
            var keyBytes = keyDerivationFunction.GetBytes(32);
            var ivBytes = keyDerivationFunction.GetBytes(16);

            // Create an encryptor to perform the stream transform.
            // Create the streams used for encryption.
            using (var aesManaged = new AesManaged())
            {
                aesManaged.Padding = PaddingMode.PKCS7;
                aesManaged.Mode = CipherMode.CBC;
                using (var encryptor = aesManaged.CreateEncryptor(keyBytes, ivBytes))
                using (var memoryStream = new MemoryStream())
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                    using (var streamWriter = new StreamWriter(cryptoStream))
                    {
                        // Send the data through the StreamWriter, through the CryptoStream, to the underlying MemoryStream
                        streamWriter.Write(plainText);
                    }

                    // Return the encrypted bytes from the memory stream, in Base64 form so we can send it right to a database (if we want).
                    var cipherTextBytes = memoryStream.ToArray();
                    Array.Resize(ref saltBytes, saltBytes.Length + cipherTextBytes.Length);
                    Array.Copy(cipherTextBytes, 0, saltBytes, _saltSize, cipherTextBytes.Length);

                    return Convert.ToBase64String(saltBytes);
                }
            }
        }
    }
    catch (Exception e)
    {
        LOGGER.Error(String.Format("Got error : \"{0}\" trying to encrypt \"{1}\"", e.Message, plainText));
        return "";
    }
}

/**
 */
public static void Encrypt(XmlDocument Doc, string ElementName, string Password)
{
    // Check the arguments.
    if (null == Doc) throw new ArgumentNullException();
    if (string.IsNullOrEmpty(ElementName)) throw new ArgumentNullException();
    if (string.IsNullOrEmpty(Password)) throw new ArgumentNullException();

    ////////////////////////////////////////////////
    // Find the specified element in the XmlDocument
    // object and create a new XmlElement object.
    ////////////////////////////////////////////////
    XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementName)[0] as XmlElement;
    // Throw an XmlException if the element was not found.
    if (elementToEncrypt == null)
    {
        throw new XmlException("The specified element was not found");
    }

    //////////////////////////////////////////////////
    // Create a new instance of the EncryptedXml class
    // and use it to encrypt the XmlElement with the
    // symmetric key.
    //////////////////////////////////////////////////

    EncryptedXml eXml = new EncryptedXml();

    string ciphertext = CryptoUtil.Encrypt(elementToEncrypt.OuterXml, Password);
    byte[] encryptedElement = System.Text.UTF8Encoding.UTF8.GetBytes(ciphertext);


    ////////////////////////////////////////////////
    // Construct an EncryptedData object and populate
    // it with the desired encryption information.
    ////////////////////////////////////////////////

    EncryptedData edElement = new EncryptedData()
    {
        Type = EncryptedXml.XmlEncElementUrl
    };

    // Create an EncryptionMethod element so that the
    // receiver knows which algorithm to use for decryption.
    // Determine what kind of algorithm is being used and
    // supply the appropriate URL to the EncryptionMethod element.
    string encryptionMethod = EncryptedXml.XmlEncAES256Url;

    edElement.EncryptionMethod = new EncryptionMethod(encryptionMethod);

    // Add the encrypted element data to the
    // EncryptedData object.
    edElement.CipherData.CipherValue = encryptedElement;

    ////////////////////////////////////////////////////
    // Replace the element from the original XmlDocument
    // object with the EncryptedData element.
    ////////////////////////////////////////////////////
    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}

解密代码:

/// <summary>
/// Decrypts the ciphertext using the Key.
/// </summary>
/// <param name="ciphertext">The ciphertext to decrypt.</param>
/// <param name="key">The plain text encryption key.</param>
/// <returns>The decrypted text.</returns>
public static String Decrypt(string ciphertext, string key)
{
    if (string.IsNullOrEmpty(ciphertext))
        throw new ArgumentNullException("cipherText");
    if (string.IsNullOrEmpty(key))
        throw new ArgumentNullException("key");

    // Extract the salt from our ciphertext
    var allTheBytes = Convert.FromBase64String(ciphertext);
    var saltBytes = allTheBytes.Take(_saltSize).ToArray();
    var ciphertextBytes = allTheBytes.Skip(_saltSize).Take(allTheBytes.Length - _saltSize).ToArray();

    using (var keyDerivationFunction = new Rfc2898DeriveBytes(key, saltBytes))
    {
        // Derive the previous IV from the Key and Salt
        var keyBytes = keyDerivationFunction.GetBytes(32);
        var ivBytes = keyDerivationFunction.GetBytes(16);

        // Create a decrytor to perform the stream transform.
        // Create the streams used for decryption.
        // The default Cipher Mode is CBC and the Padding is PKCS7 which are both good
        using (var aesManaged = new AesManaged())
        {
            aesManaged.Padding = PaddingMode.PKCS7;
            aesManaged.Mode = CipherMode.CBC;
            using (var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes))
            using (var memoryStream = new MemoryStream(ciphertextBytes))
            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (var streamReader = new StreamReader(cryptoStream))
            {
                // Return the decrypted bytes from the decrypting stream.
                cryptoStream.FlushFinalBlock();
                return streamReader.ReadToEnd();
            }
        }
    }
}

public static void Decrypt(XmlDocument Doc, string Password)
{
    // Check the arguments.
    if (null == Doc && null == Password) throw new ArgumentNullException();
    if (null == Doc) throw new ArgumentNullException();
    if (null == Password) throw new ArgumentNullException();

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

    // If the EncryptedData element was not found, quietly return.
    if (encryptedElement == null)
    {
        LOGGER.Warn("The EncryptedData element was not found.");
        return;
    }
    XmlElement cipherText = Doc.GetElementsByTagName("CipherValue")[0] as XmlElement;
    if (cipherText == null)
    {
        throw new ArgumentException($"Missing CipherValue in \n {Doc.InnerXml}");
    }

    // 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.
    string plaintext = CryptoUtil.Decrypt(cipherText.InnerText, Password);
    byte[] rgbOutput = System.Text.UTF8Encoding.UTF8.GetBytes(plaintext);
    // Replace the encryptedData element with the plaintext XML element.
    exml.ReplaceData(encryptedElement, rgbOutput);
}

单独使用重载的加密/解密方法处理纯文本Payload时无报错,问题仅出现在XML加密场景中。


问题解决

核心错误点

解密方法Decrypt(string ciphertext, string key)中,在读取模式的CryptoStream上调用了FlushFinalBlock(),这是错误操作:

  • FlushFinalBlock()仅适用于CryptoStreamMode.Write模式,用于将最后一块加密数据写入并处理填充;
  • 在读取模式下调用该方法会干扰解密流程,导致填充验证失败,触发"Padding is invalid and cannot be removed"错误。

修复步骤

  1. 移除解密方法中的错误调用
    修改Decrypt(string ciphertext, string key)方法,删除cryptoStream.FlushFinalBlock();这一行:
using (var decryptor = aesManaged.CreateDecryptor(keyBytes, ivBytes))
using (var memoryStream = new MemoryStream(ciphertextBytes))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream))
{
    // 移除 cryptoStream.FlushFinalBlock();
    return streamReader.ReadToEnd();
}
  1. 验证参数一致性
    确保_saltSize是固定常量(比如private const int _saltSize = 16;),且加密、解密流程中该值保持一致,避免密钥/IV推导错误。

额外优化建议

  • 优先使用EncryptedXml内置的EncryptData方法处理XML元素加密,减少自定义流程带来的编码、填充类错误;
  • 加密方法中异常返回空字符串的逻辑可能隐藏问题,建议直接抛出异常或返回null,便于排查错误。

内容的提问来源于stack exchange,提问作者Andrew Tyson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:50:58