RijndaelManaged"填充无效且无法删除"仅在生产中解密时才会发生

Nic*_*len 15 .net encryption cryptography aes rijndaelmanaged

我知道其他问题已经被问到,但到目前为止还没有提供解决方案,或者正是我的问题.

下面的类处理字符串的加密和解密,传入的密钥和向量总是相同的.

加密和解密的字符串总是数字,大多数工作但偶尔会在解密时失败(但仅在生产服务器上).我应该提到本地和生产环境都在Windows Server 2003上的IIS6中,使用该类的代码位于.ashx处理程序中.生产服务器上失败的示例是"0000232668"

错误消息是

System.Security.Cryptography.CryptographicException:填充无效,无法删除.在System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte [] inputBuffer,Int32 inputOffset,Int32 inputCount,Byte []&outputBuffer,Int32 outputOffset,PaddingMode paddingMode,Boolean fLast)

并为代码

 public class Aes
    {
        private byte[] Key;
        private byte[] Vector;

        private ICryptoTransform EncryptorTransform, DecryptorTransform;
        private System.Text.UTF8Encoding UTFEncoder;

        public Aes(byte[] key, byte[] vector)
        {
            this.Key = key;
            this.Vector = vector;

            // our encyption method
            RijndaelManaged rm = new RijndaelManaged();

            rm.Padding = PaddingMode.PKCS7;

            // create an encryptor and decyptor using encryption method. key and vector
            EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
            DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

            // used to translate bytes to text and vice versa
            UTFEncoder = new System.Text.UTF8Encoding();
        }

        /// Encrypt some text and return a string suitable for passing in a URL. 
        public string EncryptToString(string TextValue)
        {
            return ByteArrToString(Encrypt(TextValue));
        }

        /// Encrypt some text and return an encrypted byte array. 
        public byte[] Encrypt(string TextValue)
        {
            //Translates our text value into a byte array. 
            Byte[] bytes = UTFEncoder.GetBytes(TextValue);
            Byte[] encrypted = null;

            //Used to stream the data in and out of the CryptoStream. 
            using (MemoryStream memoryStream = new MemoryStream())
            {                
                using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
                {
                    cs.Write(bytes, 0, bytes.Length);                    
                }

                encrypted = memoryStream.ToArray();                
            }

            return encrypted;
        }

        /// The other side: Decryption methods 
        public string DecryptString(string EncryptedString)
        {
            return Decrypt(StrToByteArray(EncryptedString));
        }

        /// Decryption when working with byte arrays.     
        public string Decrypt(byte[] EncryptedValue)
        {
            Byte[] decryptedBytes = null;

            using (MemoryStream encryptedStream = new MemoryStream())
            {
                using (CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write))
                {
                    decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
                }

                decryptedBytes = encryptedStream.ToArray();
            }

            return UTFEncoder.GetString(decryptedBytes);
        }

        /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so). 
        //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); 
        //      return encoding.GetBytes(str); 
        // However, this results in character values that cannot be passed in a URL.  So, instead, I just 
        // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100). 
        public byte[] StrToByteArray(string str)
        {
            if (str.Length == 0)
                throw new Exception("Invalid string value in StrToByteArray");

            byte val;
            byte[] byteArr = new byte[str.Length / 3];
            int i = 0;
            int j = 0;
            do
            {
                val = byte.Parse(str.Substring(i, 3));
                byteArr[j++] = val;
                i += 3;
            }
            while (i < str.Length);
            return byteArr;
        }

        // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction: 
        //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); 
        //      return enc.GetString(byteArr);     
        public string ByteArrToString(byte[] byteArr)
        {
            byte val;
            string tempStr = "";
            for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
            {
                val = byteArr[i];
                if (val < (byte)10)
                    tempStr += "00" + val.ToString();
                else if (val < (byte)100)
                    tempStr += "0" + val.ToString();
                else
                    tempStr += val.ToString();
            }
            return tempStr;
        }
Run Code Online (Sandbox Code Playgroud)

编辑:谢谢你的所有帮助,但你的答案没有解决问题,结果证明这是一个非常简单的事情.我在一台服务器上生成一个加密的字符串,并将其交给另一台服务器上的处理程序进行decrpytion和处理,但事实证明,当在不同的服务器上运行时,加密结果会有所不同,因此接收服务器无法对其进行解密.其中一个答案偶然发现了这个暗示,这就是我接受它的原因

Dav*_*d M 12

当加密和解密因任何原因未使用相同的密钥或初始化向量时,有时会收到有关无效填充的消息.填充是添加到明文末尾的一些字节,以使密码可以处理完整数量的块.在PKCS7填充中,每个字节等于添加的字节数,因此在解密后总是可以删除它.你的解密导致了一个字符串,其中最后n个字节不等于最后一个字节的值n(希望句子有意义).所以我会仔细检查你所有的钥匙.

或者,在您的情况下,我建议您确保RijndaelManagedTransform为每个加密和解密操作创建并配置一个实例,并使用密钥和向量对其进行初始化.重新使用此转换对象可能会导致此问题,这意味着在第一次使用后,它不再处于正确的初始状态.

  • 这里的更新是不正确的,"在不同的服务器上运行时加密的结果不同"是不正确的.对称加密的要点是,当给予相同的源文本,密钥和IV时,加密/解密算法产生相同的结果.对于不同的.NET配置,默认值肯定会有所不同 - 检查两台服务器上的RijndaelManaged实例的Padding属性.还要验证所有编码是否匹配,以便在传递加密和编码的字符串时不会丢失数据. (3认同)

Dav*_*ray 12

我倾向于在关闭它之前在CryptoStream上显式调用FlushFinalBlock方法.这意味着在您的加密方法中执行以下操作:

using (CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write))
{
    cs.Write(bytes, 0, bytes.Length);
    cs.FlushFinalBlock();        
}
Run Code Online (Sandbox Code Playgroud)

如果不这样做,可能是加密数据被截断 - 这将导致"无效填充"方案.使用PKCS7时始终存在填充,即使加密的数据与密码的块长度对齐.

  • 好吧,不知怎的,我添加了FlushFinalBlock语句,这有助于我在解密时摆脱无效的填充错误. (3认同)
  • FlushFinalBlock() 在 CryptoStream 被释放时被调用,这会通过 using 语句自动发生 (2认同)
  • 除了使用块之外,显式添加FlushFinalBlock还解决了我在.NET 4.5上的问题...... (2认同)