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

Unity C#端验证本地Android IAP市场响应签名的技术咨询

嘿,你的理解大体是对的,但有个关键细节得澄清下——服务端并不是直接用私钥“加密”哈希值,而是用私钥对数据的哈希值做签名操作(这是非对称加密的特定应用,和普通的加密解密逻辑方向不同)。我们客户端要做的也不是“解密签名”,而是用公钥验证这个签名是否真的来自持有对应私钥的服务端,同时确保返回的数据没有被篡改。

下面我会把完整的验证逻辑、Unity C#端的实现步骤和关键注意事项整理清楚,帮你搞定这个问题:

签名验证的核心逻辑梳理

先把整个流程拆解明白,避免混淆:

  1. 服务端生成要返回给你的响应数据(比如订单详情、支付结果)
  2. 服务端用指定的哈希算法(比如SHA-256)计算这份数据的哈希值
  3. 服务端用自己的私钥对这个哈希值执行签名操作,生成签名串(通常会用Base64编码后返回给你)
  4. 你在Unity端拿到三个关键内容:原始响应数据、Base64编码的签名串、服务端提供的公钥
  5. 你用和服务端完全相同的哈希算法,计算拿到的原始响应数据的哈希值
  6. 用公钥验证:签名串是否能匹配你计算出的哈希值——如果匹配,说明数据没被篡改,且确实是服务端发出的
Unity C#端实现步骤(附代码示例)

假设你从Android端拿到的内容是:

  • 原始响应字符串 rawResponse(比如JSON格式的订单数据)
  • Base64编码的签名串 signatureBase64
  • 服务端提供的PEM格式公钥(开头带-----BEGIN PUBLIC KEY-----的字符串)

步骤1:把PEM格式公钥转成DER格式

Unity的RSA加密类需要DER格式的公钥,所以先清理并转换PEM格式:

private static byte[] ConvertPemToDer(string pemPublicKey)
{
    // 移除PEM的头尾标记和多余换行符
    var cleanedPublicKey = pemPublicKey
        .Replace("-----BEGIN PUBLIC KEY-----", "")
        .Replace("-----END PUBLIC KEY-----", "")
        .Replace("\n", "")
        .Replace("\r", "");
    // Base64解码得到DER格式的字节数组
    return Convert.FromBase64String(cleanedPublicKey);
}

步骤2:初始化RSA对象并导入公钥

private static RSACryptoServiceProvider GetRsaProvider(byte[] derPublicKey)
{
    var rsa = new RSACryptoServiceProvider();
    // 导入DER格式的公钥
    rsa.ImportSubjectPublicKeyInfo(derPublicKey, out _);
    return rsa;
}

步骤3:计算原始数据的哈希值

注意要和服务端用的哈希算法完全一致,这里以SHA-256为例:

private static byte[] ComputeDataHash(string rawData)
{
    using (var sha256 = SHA256.Create())
    {
        // 编码格式要和服务端一致,通常用UTF-8
        var dataBytes = Encoding.UTF8.GetBytes(rawData);
        return sha256.ComputeHash(dataBytes);
    }
}

步骤4:执行签名验证

public static bool VerifySignature(string rawResponse, string signatureBase64, string pemPublicKey)
{
    try
    {
        var derPublicKey = ConvertPemToDer(pemPublicKey);
        using (var rsa = GetRsaProvider(derPublicKey))
        {
            var signatureBytes = Convert.FromBase64String(signatureBase64);
            var dataHash = ComputeDataHash(rawResponse);
            
            // 这里的签名算法要和服务端完全匹配,比如SHA256withRSA
            return rsa.VerifyHash(dataHash, CryptoConfig.MapNameToOID("SHA256"), signatureBytes);
        }
    }
    catch (Exception ex)
    {
        Debug.LogError($"签名验证失败:{ex.Message}");
        return false;
    }
}
关键注意事项(踩坑必看)
  • 算法完全一致:哈希算法(SHA-1/SHA-256等)、签名算法(SHA256withRSA等)必须和服务端用的一模一样,否则验证100%失败
  • 编码一致:计算哈希时的字符串编码(UTF-8/GBK等)要和服务端保持一致
  • 公钥正确性:务必确认你用的是对应环境(测试/生产)的公钥,不能混用
  • 数据完整性:你计算哈希的原始数据必须和服务端签名时用的原始数据完全一致——比如Android端返回时有没有偷偷修改过响应字符串的格式(加空格、换行)?这点一定要和Android端的同事确认

如果还是验证失败,可以从这几个方向排查:

  1. 打印你计算的哈希值,和服务端计算的哈希值对比,看是否一致
  2. 检查签名串的Base64解码是否正确
  3. 用在线工具把PEM转成DER,对比你代码转换后的字节数组是否一致

内容的提问来源于stack exchange,提问作者High

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:55:17