0

Some of my app users report an error when decrypt the serialize file.

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)

Here is the code I'm using:

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;
    }
}

I don't set MODE and PADDING so it would be the default value CipherMode.CBC and PaddingMode.PKCS7. I also check the SerialKEY and SerialIV to confirm that it wouldn't change.in fact , there are several serialize files but only one of them faced the issue.

I tried to reduproduce the exception.I have tried :

  1. edit the serialize file by notepad and change a little
  2. use different PADDING or MODE to encrypt/decrypt
  3. use different SerialKEY or SerialIV to encrypt/decrypt

But I cannot get the exception:

 CryptographicException: Bad PKCS7 padding. Invalid length 137.

Only get another error like:

 Unexpected binary element: 100

I also searched by Google , found some similar issues in stackoverflow:

CryptographicException: Bad PKCS7 padding

But I did't get useful suggestions.

Community
  • 1
  • 1
yang
  • 9
  • 2

2 Answers2

2

There is a rather subtle bug in how you creating the serialized file. You're opening the file to create like this

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

If you do this instead, your code will work:

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

By opening the file in OpenOrCreate to open the file, if your data set shrinks at all you'll have some extra data at the end of the file. This normally isn't a problem for serialized objects, but the combination of CBC and PKCS7 makes this fatal.

You'll decode most of the data without problems, but the final block of the smaller file that you wrote will decode OK, but the padding won't be removed. That's OK, but then the next segment, of the previously larger file, will be read and decoded. Because the CBC updated IV will almost certainly be wrong to decode this block, it will decode to nonsense. BinaryFormatter will probably be OK with this garbage data, but when it comes time to decode the final block of the file, the PKCS7 padding will kick in, and since the decoded value is garbage, it's rather likely it's not valid padding data, and the decoder throws the exception you're seeing.

Anon Coward
  • 9,784
  • 3
  • 26
  • 37
  • Thanks so much. It seems to be the reason.So I do a test , first create a large file and then shrink it.Yes , the extra data is still at the end of the file.But I did't get any error when I tried to deserialize the smaller file. It works fine. Is there anything I'm missing? – yang Jul 13 '16 at 06:30
0

I'm sorry if i post here, but my rep is low and i cannot add comments. I believe there might be something wrong with your encryption mechanism.

Have you checked this: Rijndael padding or length is invalid

Community
  • 1
  • 1
J11
  • 426
  • 1
  • 6
  • 17
  • Thanks for your help.I have checked that , it seems that his problem is using diffrent IV when decrypting and encrypting.Not the same as me. for my issue , I wander if the serialize file is broken. I have got the origin file which decrypted fail , but I don't know how to check what's the problem with it. – yang Jul 13 '16 at 04:00
  • No problem, since your issue seems to appear in a sporadic fashion, i thought it might be the encryption. I wonder why the downvote... I guess i needed to post a lecture about cryptography to get an upvote. – J11 Jul 13 '16 at 04:06
  • Didn't mean to come across as a lecture, just attempting to quickly explain the problem and why it happened. Sorry to all if it came off condescending. – Anon Coward Jul 13 '16 at 04:30
  • @AnonCoward Not your response. It was in general. Yours is quite good. – J11 Jul 13 '16 at 04:58