【问题标题】:CryptographicException: Bad PKCS7 padding. Invalid length 137CryptographicException:错误的 PKCS7 填充。长度无效 137
【发布时间】:2016-11-15 11:13:09
【问题描述】:

我的一些应用用户在解密序列化文件时报错。

Exception   LocalTime: 07/08/2016 21:22:16 ServerTime: 07/08/2016 21:22:16  508 CryptographicException: Bad PKCS7 padding. Invalid length 137.
Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (PaddingMode padding, Int32 length, Int32 position)
Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
System.Security.Cryptography.CryptoStream.Read (System.Byte[] buffer, Int32 offset, Int32 count)
System.IO.BinaryReader.FillBuffer (Int32 numBytes)
System.IO.BinaryReader.ReadInt32 ()
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadArrayOfPrimitiveType (System.IO.BinaryReader reader, System.Int64& objectId, System.Object& val)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders,System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream)

这是我正在使用的代码:

public void Serialiable(){
    if (!Directory.Exists (DirectoryPath)) {
        Directory.CreateDirectory(DirectoryPath);
    }
    FileStream fs = new FileStream (FilePath, FileMode.OpenOrCreate);

    CryptoStream cryptStream = new CryptoStream(fs, Encryptor, CryptoStreamMode.Write); 

    BinaryFormatter formatter = new BinaryFormatter ();
    try{
        formatter.Serialize(cryptStream,this);
    }catch(System.Exception e){
        Debug.LogError("Failed to serialize. Reason:  "+e.Message);
    }finally{
        cryptStream.Close();
        fs.Close();
    }
}

public SerializableBase Deserialize(){
    SerializableBase t = null;
    if (File.Exists (FilePath)) {
        FileStream fs = new FileStream (FilePath, FileMode.Open);
        CryptoStream cryptStream  = new CryptoStream(fs, Decryptor,CryptoStreamMode.Read);

        try {
            BinaryFormatter formatter = new BinaryFormatter ();
            t = (SerializableBase)formatter.Deserialize (cryptStream);
            t.Refresh();
        } catch(System.Exception e){
            Debug.LogError("Failed to deserialize. Reason: "+e.Message);
            t = null;
        }
        finally {
            if(cryptStream!=null){
                cryptStream.Close();
            }
            fs.Close ();
        }
    } 
    return t;
}

[NonSerialized]
ICryptoTransform _Encryptor;
ICryptoTransform Encryptor {
    get{
        if(_Encryptor==null){
            _Encryptor = RMCrypto.CreateEncryptor(RuntimeGlobalVariables.SerialKEY,RuntimeGlobalVariables.SerialIV);
        }
        return _Encryptor;
    }
}
[NonSerialized]
ICryptoTransform _Decryptor ;
ICryptoTransform Decryptor {
    get{
        if(_Decryptor==null){
            _Decryptor = RMCrypto.CreateDecryptor(RuntimeGlobalVariables.SerialKEY,RuntimeGlobalVariables.SerialIV);
        }
        return _Decryptor;
    }
}
[NonSerialized]
RijndaelManaged _RMCrypto;
RijndaelManaged RMCrypto
{
    get
    {
        if (_RMCrypto == null)
        {
            _RMCrypto = new RijndaelManaged();
        }
        return _RMCrypto;
    }
}
string DirectoryPath
{
    get
    {
        return Application.persistentDataPath + "/dat";
    }
}
string FilePath {
    get{
        return DirectoryPath + "/" + FileName;
    }
}

我没有设置 MODE 和 PADDING,所以它将是默认值 CipherMode.CBC 和 PaddingMode.PKCS7。我还检查了 SerialKEY 和 SerialIV 以确认它不会改变。事实上,有几个序列化文件但只有一个遇到了问题。

我试图减少异常。我已经尝试过:

  1. 用记事本编辑序列化文件并稍作改动
  2. 使用不同的 PADDING 或 MODE 来加密/解密
  3. 使用不同的 SerialKEY 或 SerialIV 进行加密/解密

但我无法得到异常:

 CryptographicException: Bad PKCS7 padding. Invalid length 137.

只会得到另一个错误,例如:

 Unexpected binary element: 100

我也用谷歌搜索,在stackoverflow中发现了一些类似的问题:

CryptographicException: Bad PKCS7 padding

但我没有得到有用的建议。

【问题讨论】:

  • 最好显式设置所有参数。

标签: c# android encryption


【解决方案1】:

在创建序列化文件的方式中存在一个相当微妙的错误。您正在打开文件以像这样创建

FileStream fs = new FileStream(FilePath, FileMode.OpenOrCreate);

如果您改为这样做,您的代码将起作用:

FileStream fs = new FileStream(FilePath, FileMode.Create);

通过打开OpenOrCreate 中的文件来打开文件,如果您的数据集完全缩小,您将在文件末尾有一些额外的数据。对于序列化的对象,这通常不是问题,但 CBC 和 PKCS7 的组合使这变得致命。

您将毫无问题地解码大部分数据,但您编写的较小文件的最后一个块将解码正常,但不会删除填充。没关系,但随后将读取和解码先前较大文件的下一段。因为 CBC 更新的 IV 解码这个块几乎肯定是错误的,它会解码成废话。 BinaryFormatter 可能对这些垃圾数据没问题,但是当需要解码文件的最后一个块时,PKCS7 填充将启动,并且由于解码的值是垃圾,它很可能不是有效的填充数据,并且解码器会抛出您看到的异常。

【讨论】:

  • 非常感谢。好像是这个原因。所以我做了一个测试,先创建一个大文件,然后缩小它。是的,额外的数据仍然在文件末尾。但是我尝试反序列化时没有任何错误较小的文件。它工作正常。有什么我想念的吗?
【解决方案2】:

如果我在这里发帖,我很抱歉,但我的代表很低,我无法添加 cmets。 我认为您的加密机制可能有问题。

你检查过这个吗:Rijndael padding or length is invalid

【讨论】:

  • 感谢您的帮助。我已经检查过了,似乎他的问题是在解密和加密时使用了不同的IV。和我不一样。对于我的问题,如果序列化文件损坏,我会徘徊。我有解密失败的原始文件,但我不知道如何检查它有什么问题。
  • 没问题,因为您的问题似乎以零星的方式出现,我认为可能是加密。我想知道为什么要投反对票……我想我需要发布一个关于密码学的讲座才能获得赞成票。
  • 无意冒充演讲,只是试图快速解释问题及其发生的原因。对不起,如果它居高临下。
  • @AnonCoward 不是您的回复。一般情况下。你的很不错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-28
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多