using System; using System.Security.Cryptography; using System.Text; using UnityEngine; namespace XGame.MiniGame { // 发布产物签名验证(设计 §7):用内置 RSA 公钥(modulus+exponent, base64)验 files.txt 的 digest 签名。 // 公钥未配置(空)时跳过验签(仅本地开发);正式包必须填入与发布方私钥配对的公钥。 // 用 RSAParameters+ImportParameters(netstandard2.0 起即有,Unity mono 普遍支持),避免 ImportFromPem(net5+)。 public static class ClientManifestVerifier { // TODO(发布前必填):从发布方 RSA 密钥对导出的公钥分量 base64。 // 例:RSAParameters p = rsa.ExportParameters(false); Modulus=Convert.ToBase64String(p.Modulus); Exponent=Convert.ToBase64String(p.Exponent)("AQAB") private const string PublicKeyModulusB64 = ""; private const string PublicKeyExponentB64 = ""; public static bool IsConfigured => PublicKeyModulusB64.Length > 0 && PublicKeyExponentB64.Length > 0; // 验 sig(对 digest 的 UTF8 字节做 RSA-SHA256-PKCS1 签名,与发布工具 ManifestSigner.Sign 一致) public static bool Verify(string digest, byte[] signature) { if (!IsConfigured) { Debug.LogWarning("[ClientManifestVerifier] 未配置公钥,跳过验签(仅限开发)"); return true; } if (signature == null || signature.Length == 0) return false; try { var p = new RSAParameters { Modulus = Convert.FromBase64String(PublicKeyModulusB64), Exponent = Convert.FromBase64String(PublicKeyExponentB64), }; using (var rsa = RSA.Create()) { rsa.ImportParameters(p); return rsa.VerifyData(Encoding.UTF8.GetBytes(digest), signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } } catch (Exception e) { Debug.LogError("[ClientManifestVerifier] 验签异常: " + e.Message); return false; } } } }