Padding is invalid and cannot be removed?
Asked Answered
W

19

184

I have looked online for what this exception means in relation to my program but can't seem to find a solution or the reason why it's happening to my specific program. I have been using the example provided my msdn for encrypting and decrypting an XmlDocument using the Rijndael algorithm. The encryption works fine but when I try to decrypt, I get the following exception:

Padding is invalid and cannot be removed

Can anyone tell me what I can do to solve this issue? My code below is where I get the key and other data. If the cryptoMode is false, it will call the decrypt method, which is where the exception occurs:

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

}
Wool answered 20/12, 2011 at 23:2 Comment(2)
Can you give it a try by explicitly setting the padding mode to be identical on both the encryption and decryption to be indentical. For example: alg.Padding = PaddingMode.NONE;Volt
What does the Encrypt() method look like?Idelia
E
99

Rijndael/AES is a block cypher. It encrypts data in 128 bit (16 character) blocks. Cryptographic padding is used to make sure that the last block of the message is always the correct size.

Your decryption method is expecting whatever its default padding is, and is not finding it. As @NetSquirrel says, you need to explicitly set the padding for both encryption and decryption. Unless you have a reason to do otherwise, use PKCS#7 padding.

Equidistant answered 21/12, 2011 at 12:56 Comment(6)
@AhmadHajjar No padding has security implications, don't use it.Mediterranean
Hi, I set the padding explicitly, but not work. I don't know what steps I did wrong. Please help. alg.Padding = PaddingMode.PKCS7;Nagy
The padding needs to be the same on both sides. You should explicitly set the padding twice, once for encryption and once for decryption. There are other possible causes of padding errors. We will need to see your code and the exact error message to diagnose the problem.Equidistant
@Nagy can you make sure you are not "trimming" your outputs? I saw my output results of encrypted string are starting with some blank space. Other wise the examples i have seen of encrypting are working okay. I used the examples at winform-net.blogspot.co.uk/2011/05/…Seethrough
I realize this is an old thread. But, for those visiting, make sure you flush the final block when encrypting the data.Slipway
@Slipway It appears that flushfinalblock is called automatically when the stream is closed. Calling it explicitly from within the "using (CryptoStream csEncrypt..." construct results in an "FlushFinalBlock method was called twice" exception (even the first time it is explicitly called).Eviaevict
A
86

Make sure that the keys you use to encrypt and decrypt are the same. The padding method even if not explicitly set should still allow for proper decryption/encryption (if not set they will be the same). However if you for some reason are using a different set of keys for decryption than used for encryption you will get this error:

Padding is invalid and cannot be removed

If you are using some algorithm to dynamically generate keys that will not work. They need to be the same for both encryption and decryption. One common way is to have the caller provide the keys in the constructor of the encryption methods class, to prevent the encryption/decryption process having any hand in creation of these items. It focuses on the task at hand (encrypting and decrypting data) and requires the iv and key to be supplied by the caller.

Antitoxic answered 31/7, 2013 at 16:8 Comment(3)
This tip was very useful, because, sometimes the keys are stored on app.config and we must always be sure that the keys used to encrypt are the same as used to decrypt.Browning
I'd suggest that in day-to-day use, this is probably the most likely reason people will encounter this error. Especially if you're not messing with the padding settings.Bonnibelle
Yep and that includes iterations on DeriveBytes() has to match also.Cobbs
T
34

For the benefit of people searching, it may be worth checking the input being decrypted. In my case, the info being sent for decryption was (wrongly) going in as an empty string. It resulted in the padding error.

This may relate to rossum's answer, but thought it worth mentioning.

Tussock answered 28/8, 2013 at 15:8 Comment(3)
I agree, happened me the same, checking the input being decrypted BEFORE doing other checks. I was getting 1 byte more than what I encypted...Motch
My case was the passphrase was not set (yeah I know), but this answer got me to the right direction.Posting
My issue was that the string to be decrypted was being converted to lower case before I tried to decrypt it. I was obsessing with the padding and ciphers and all that stuff, but it turned out it was just bad input. Sometimes you just need to take a step back!Arakawa
S
34

I came across this as a regression bug when refactoring code from traditional using blocks to the new C# 8.0 using declaration style, where the block ends when the variable falls out of scope at the end of the method.

Old style:

//...
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());
}

New, less indented style:

//...
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());

With the old style, the using block for the CryptoStream terminated and the finalizer was called before memory stream gets read in the return statement, so the CryptoStream was automatically flushed.

With the new style, the memory stream is read before the CryptoStream finalizer gets called, so I had to manually call FlushFinalBlock() before reading from the memory stream in order to fix this issue. I had to manually flush the final block for both the encrypt and the decrypt methods, when they were written in the new using style.

Stator answered 21/10, 2020 at 17:49 Comment(4)
what is rawCipherText?Jiffy
That's the byte array containing the encrypted data to be decrypted.Stator
You saved my day! In my case, I changed encryption code from new declaration style to tradition style and it worked. The encrypted data can be decrypted successfully without changing decryption code.Rhinelandpalatinate
I too hit this, thanks for the heads up!Tamica
T
32

If the same key and initialization vector are used for encoding and decoding, this issue does not come from data decoding but from data encoding.

After you called Write method on a CryptoStream object, you must ALWAYS call FlushFinalBlock method before Close method.

MSDN documentation on CryptoStream.FlushFinalBlock method says:
"Calling the Close method will call FlushFinalBlock ..."
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs.110).aspx
This is wrong. Calling Close method just closes the CryptoStream and the output Stream.
If you do not call FlushFinalBlock before Close after you wrote data to be encrypted, when decrypting data, a call to Read or CopyTo method on your CryptoStream object will raise a CryptographicException exception (message: "Padding is invalid and cannot be removed").

This is probably true for all encryption algorithms derived from SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES), although I just verified that for AesManaged and a MemoryStream as output Stream.

So, if you receive this CryptographicException exception on decryption, read your output Stream Length property value after you wrote your data to be encrypted, then call FlushFinalBlock and read its value again. If it has changed, you know that calling FlushFinalBlock is NOT optional.

And you do not need to perform any padding programmatically, or choose another Padding property value. Padding is FlushFinalBlock method job.

.........

Additional remark for Kevin:

Yes, CryptoStream calls FlushFinalBlock before calling Close, but it is too late: when CryptoStream Close method is called, the output stream is also closed.

If your output stream is a MemoryStream, you cannot read its data after it is closed. So you need to call FlushFinalBlock on your CryptoStream before using the encrypted data written on the MemoryStream.

If your output stream is a FileStream, things are worse because writing is buffered. The consequence is last written bytes may not be written to the file if you close the output stream before calling Flush on FileStream. So before calling Close on CryptoStream you first need to call FlushFinalBlock on your CryptoStream then call Flush on your FileStream.

Transilluminate answered 12/11, 2016 at 15:4 Comment(4)
Why do you say it's wrong? The code for Stream.Close() calls this.Dispose(true). The code for CryptoStream.Dispose(bool) is: if (disposing) { if (!this._finalBlockTransformed) { this.FlushFinalBlock(); } this._stream.Close(); }Quaternity
This resolved my issue. I was disposing the cryptoStream correctly, but the dispose call was happening "too late", just as you say. This resulted in the "invalid padding" error, as described. By adding cryptoStream.FlushFinalBlock(), the invalid padding error was resolved. Thanks!Sholeen
note however that if using a StreamWriter you need to call Flush() on the streamWriter before calling FlushFinalBlock() on the cryptoStream, at least this is my experience.Dunne
I was using PowerShell to do the encryption/decryption and not using a using so was relying on the Close() to do the necessary for me and, as it transpires, it was not doing the right do. Adding the call to FlushFinalBlock after a call to Flush and before Close made the decryption happyHellman
N
18

A serval times of fighting, I finally solved the problem.
(Note: I use standard AES as symmetric algorithm. This answer may not suitable for everyone.)

  1. Change the algorithm class. Replace the RijndaelManaged class to AESManaged one.
  2. Do not explicit set the KeySize of algorithm class, left them default.
    (This is the very important step. I think there is a bug in KeySize property.)

Here is a list you want to check which argument you might have missed:

  • Key
    (byte array, length must be exactly one of 16, 24, 32 byte for different key size.)
  • IV
    (byte array, 16 bytes)
  • CipherMode
    (One of CBC, CFB, CTS, ECB, OFB)
  • PaddingMode
    (One of ANSIX923, ISO10126, None, PKCS7, Zeros)
Nagy answered 19/2, 2016 at 9:42 Comment(3)
Not explicitly setting KeySize fixed it for me straight away. Oh the quirks of .NET :-(Flabellate
Note that this seems to be a regression in the .NET Framework itself. I have code that used to work with RijndaelManaged, but stopped working, and by simply changing it to AesManaged / AesCryptoServiceProvider, it works again. I didn't even have any code explicitly setting the KeySize. So if you're bitten by this, feel better - the fault might not lie with you, but with the .NET Framework itself.Cite
I use this check algorithm.ValidKeySize(key.Length * 8);Sejant
M
7

My issue was that the encrypt's passPhrase didn't match the decrypt's passPhrase... so it threw this error .. a little misleading.

Maulmain answered 25/1, 2017 at 15:0 Comment(3)
Actually it is true we use PaddingMode.PKCS7 for Encrypt and Decrypt but I got the same error message. Also we have Stage and Dev environment with different key values. When I used the proper -environment specific - key this exception was solved...Fling
Although all above answers are good and you must use the same padding for for Encrypt and Decrypt (none is NOT recommended!) actually this answer also can be true. When I used the proper -environment specific - key the exception "System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed." was solved. So yes it can be misleading.Fling
If by "passPhrase" you are talking about the exact value to encrypt/decrypt (not an issue with using the wrong key), then yes, this was my problem. My case was the original encrypted value was longer than my database table field allowed so it was being truncated to fit without me realizing it. Then when decrypting that truncated value this exception was thrown.Guam
K
7

This will fix the problem:

aes.Padding = PaddingMode.Zeros;
Kermit answered 30/11, 2020 at 22:46 Comment(1)
Hi Adam. Can you please give a brief explanation of what this code does and why it is preferable to the many answers that already exist for this question? Kind Regards.Forwarder
S
5

The solution that fixed mine was that I had inadvertently applied different keys to Encryption and Decryption methods.

Send answered 9/3, 2017 at 15:1 Comment(1)
This fixed my issue. Recommend double-checking the key being used before moving to the more complex solutions here.Etherealize
A
5

I had the same problem trying to port a Go program to C#. This means that a lot of data has already been encrypted with the Go program. This data must now be decrypted with C#.

The final solution was PaddingMode.None or rather PaddingMode.Zeros.

The cryptographic methods in Go:

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
}

... and ...

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

Now, decryption in C#:

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

How can the issue with the padding be explained? Just before encryption the Go program checks the padding:

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

The important part is this:

enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)

A new array is created with an appropriate length, so that the length is a multiple of the block size. This new array is filled with zeros. The copy method then copies the existing data into it. It is ensured that the new array is larger than the existing data. Accordingly, there are zeros at the end of the array.

Thus, the C# code can use PaddingMode.Zeros. The alternative PaddingMode.None just ignores any padding, which also works. I hope this answer is helpful for anyone who has to port code from Go to C#, etc.

Allx answered 27/3, 2019 at 11:20 Comment(0)
K
3

I had the old data which I was trying to decrypt. After a lot of debugging, I've identified that we get this error when the decryption key is different from the encryption key. When I tried encryption-decryption with new data, I didn't get this error again. If you try to resolve Padding error by using PaddingMode.None OR PaddingMode.Zeros, this will only skip the exception and will give you garbage data ex:"9\u0002ަ�_\u001f�5��\t��\u0006�~3!����ژ��"

Kittenish answered 12/5, 2023 at 12:14 Comment(0)
D
2

This can also happen if you have the wrong encryption key with a padding mode set.

I saw this when I was testing concurrency issues and messed up my testbed. I created a new instance of the AES class for each transform (encrypt/decrypt) without setting the key, and this got thrown when I was trying to decrypt the result.

Dissatisfactory answered 19/3, 2021 at 21:49 Comment(0)
E
1

I came across this error while attempting to pass an un-encrypted file path to the Decrypt method.The solution was to check if the passed file is encrypted first before attempting to decrypt

if (Sec.IsFileEncrypted(e.File.FullName))
{
    var stream = Sec.Decrypt(e.File.FullName);
} 
else
{
    // non-encrypted scenario  
}
Edmiston answered 9/1, 2018 at 17:15 Comment(1)
+1 because this exception is raised when you decrypt twice or you decrypt something not encrypted. So I read this answer as "are you sure that the data is actually encrypted?".Impolite
M
1

For anyone trying to encrypt with Dart (https://pub.dev/packages/encrypt), and decrypt with C# (System.Security.Cryptography.Aes) using the AES algorithm keep in mind the Dart library sets the default mode as:

 AESMode.sic

But the C# library sets the default mode to

CipherMode.CBC

I was receiving the padding error too, but both libraries have the same default (PKCS7)...To fix this, create the AES instance on your Dart side with CBC mode:

Encrypter(AES(key, mode: encrypt.AESMode.cbc))
Murmuration answered 1/5, 2023 at 18:18 Comment(0)
F
0

Another scenario, again for the benefit of people searching.

For me this error occurred during the Dispose() method which masked a previous error unrelated to encryption.

Once the other component was fixed, this exception went away.

Forgery answered 14/7, 2015 at 14:31 Comment(1)
What was the previous error unrelated to encryption?Byelaw
S
0

I encountered this padding error when i would manually edit the encrypted strings in the file (using notepad) because i wanted to test how decryption function will behave if my encrypted content was altered manually.

The solution for me was to place a

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

Like i said my padding error was because i was manually typing over the decrypted text using notepad. May be my answer may guide you to your solution.

Seethrough answered 13/2, 2016 at 19:25 Comment(0)
B
0

I had the same error. In my case it was because I have stored the encrypted data in a SQL Database. The table the data is stored in, has a binary(1000) data type. When retreiving the data from the database, it would decrypt these 1000 bytes, while there where actually 400 bytes. So removing the trailing zero's (600) from the result it fixed the problem.

Betimes answered 22/2, 2018 at 9:0 Comment(0)
B
0

I had this error and was explicitly setting the blocksize: aesManaged.BlockSize = 128;

Once I removed that, it worked.

Backwoods answered 22/1, 2019 at 21:4 Comment(0)
K
0

This happened to me when I chaneged from PlayerPrefs to CPlayerPrefs, all I did is clear previous PlayerPrefs and let CPlayerPrefs make the new ones.

Kummerbund answered 10/2, 2023 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.