You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

问题原因分析

  1. 密钥长度不匹配:AES-256要求密钥必须是32字节,但你的ConstantKey字符串长度为31,转成ASCII字节数组后只有31字节,不符合AES-256的密钥长度要求,直接导致加密解密逻辑失效。
  2. IV使用错误:CBC模式下,初始化向量(IV)必须随机生成,且加密和解密必须使用相同的IV。当前代码使用全0的IV,既不安全,也会导致加密解密流程不匹配。
  3. 编码选择不当:ASCII编码仅支持0-127的字符,存储密钥时可能丢失数据,引发解密失败。
  4. 加密解密流程不统一:如果加密用户密钥时的参数(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:10:38