Unity AES解密遇CryptographicException: Bad PKCS7 padding错误求助
Unity游戏数据加密解密中的PKCS7填充错误解决
我在Unity开发游戏时,需要将游戏数据加密后存储在用户设备上提升安全性,采用的方案是:为每个用户生成专属密钥,先用通用常量密钥加密该专属密钥后存储;解密时先通过常量密钥解密出专属密钥,再用它解密游戏数据。
简化后的实现脚本如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using System; using System.Security.Cryptography; using System.Text; public class PlayerScript : MonoBehaviour { public string FileName = "/datiDIGioco.txt"; public string DataContent; public string chiave = ""; public string KeyFileName = "/chiave.txt"; string ConstantKey = "AnyConstantKeyEqualsForEveryone."; void Start() { if ((File.Exists(Application.persistentDataPath + KeyFileName))) { LoadData(); } } void LoadData() { chiave = File.ReadAllText(Application.persistentDataPath + KeyFileName);//加密后的用户密钥 DecriptKey(); //用常量密钥解密用户密钥 DataContent = File.ReadAllText(Application.persistentDataPath + FileName);//加密后的游戏数据 Decript(); //用解密后的用户密钥解密游戏数据 } public void DecriptKey() { byte[] TestoCriptato = Convert.FromBase64String(chiave); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.BlockSize = 128; aes.KeySize = 256; aes.Key = ASCIIEncoding.ASCII.GetBytes(ConstantKey); byte[] iv = new byte[aes.BlockSize / 8]; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; ICryptoTransform tdc = aes.CreateDecryptor(aes.Key, aes.IV); byte[] testoDecriptato = tdc.TransformFinalBlock(TestoCriptato, 0, TestoCriptato.Length); tdc.Dispose(); chiave = ASCIIEncoding.ASCII.GetString(testoDecriptato); } public void Decript() { byte[] TestoCriptato = Convert.FromBase64String(DataContent); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.BlockSize = 128; aes.KeySize = 256; aes.Key = ASCIIEncoding.ASCII.GetBytes(chiave); byte[] iv = new byte[aes.BlockSize / 8]; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; ICryptoTransform tdc = aes.CreateDecryptor(aes.Key, aes.IV); byte[] testoDecriptato = tdc.TransformFinalBlock(TestoCriptato, 0, TestoCriptato.Length); tdc.Dispose(); DataContent = ASCIIEncoding.ASCII.GetString(testoDecriptato); } }
运行时触发以下错误:
CryptographicException: Bad PKCS7 padding. Invalid length 205. Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (System.Security.Cryptography.PaddingMode padding, System.Int32 length, System.Int32 position) (at <695d1cc93cca45069c528c15c9fdd749>:0) Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) (at <695d1cc93cca45069c528c15c9fdd749>:0) Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) (at <695d1cc93cca45069c528c15c9fdd749>:0) PlayerScript.DecriptKey () (at Assets/script/salva dati/PlayerScript.cs:52) PlayerScript.LoadData () (at Assets/script/salva dati/PlayerScript.cs:33) PlayerScript.Start () (at Assets/script/salva dati/PlayerScript.cs:25)
问题原因分析
- 密钥长度不匹配:AES-256要求密钥必须是32字节,但你的
ConstantKey字符串长度为31,转成ASCII字节数组后只有31字节,不符合AES-256的密钥长度要求,直接导致加密解密逻辑失效。 - IV使用错误:CBC模式下,初始化向量(IV)必须随机生成,且加密和解密必须使用相同的IV。当前代码使用全0的IV,既不安全,也会导致加密解密流程不匹配。
- 编码选择不当:ASCII编码仅支持0-127的字符,存储密钥时可能丢失数据,引发解密失败。
- 加密解密流程不统一:如果加密用户密钥时的参数(IV、密钥处理方式、编码)与解密时不一致,必然触发填充错误。
修复步骤
1. 修正密钥长度
使用SHA256哈希算法将任意长度的常量密钥转成32字节的标准AES-256密钥,替换原有的密钥赋值逻辑:
// 在DecriptKey和Decript方法中替换aes.Key的赋值 using (SHA256 sha256 = SHA256.Create()) { aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(ConstantKey)); }
2. 正确处理IV
加密时生成随机IV,将IV与加密数据拼接存储;解密时先分离IV再解密:
加密用户密钥的修正示例(需同步修改你的加密逻辑):
public string EncryptKey(string userKey) { using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) { aes.BlockSize = 128; aes.KeySize = 256; // 用SHA256处理常量密钥 using (SHA256 sha256 = SHA256.Create()) { aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(ConstantKey)); } aes.GenerateIV(); // 生成随机IV aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); byte[] userKeyBytes = Encoding.UTF8.GetBytes(userKey); byte[] encryptedBytes = encryptor.TransformFinalBlock(userKeyBytes, 0, userKeyBytes.Length); // 拼接IV与加密数据(IV在前,占16字节) byte[] result = new byte[aes.IV.Length + encryptedBytes.Length]; Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length); Buffer.BlockCopy(encryptedBytes, 0, result, aes.IV.Length, encryptedBytes.Length); return Convert.ToBase64String(result); } }
解密用户密钥的修正:
public void DecriptKey() { byte[] fullData = Convert.FromBase64String(chiave); using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) { aes.BlockSize = 128; aes.KeySize = 256; // 分离出IV(前16字节) byte[] iv = new byte[aes.BlockSize / 8]; Buffer.BlockCopy(fullData, 0, iv, 0, iv.Length); aes.IV = iv; // 用SHA256处理常量密钥 using (SHA256 sha256 = SHA256.Create()) { aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(ConstantKey)); } aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); // 解密IV之后的核心数据 byte[] encryptedKeyBytes = new byte[fullData.Length - iv.Length]; Buffer.BlockCopy(fullData, iv.Length, encryptedKeyBytes, 0, encryptedKeyBytes.Length); byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedKeyBytes, 0, encryptedKeyBytes.Length); chiave = Encoding.UTF8.GetString(decryptedBytes); } }
3. 统一编码格式
将所有字符串转字节数组的操作从ASCIIEncoding.ASCII改为Encoding.UTF8,避免字符丢失问题。
4. 同步加密解密流程
确保游戏数据的加密/解密逻辑,与用户密钥的加密/解密逻辑使用完全一致的参数:
- 相同的密钥哈希处理方式
- 相同的IV存储/分离逻辑
- 相同的编码、加密模式、填充方式
内容的提问来源于stack exchange,提问作者Daniele11
相关产品推荐
相关产品推荐

