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

Xamarin.Forms调用SafetyNet Api Attestation无法获取返回响应如何解决

问题排查及修复方案

现有代码核心问题

  • 主线程死锁:你在OnCreate(Android主线程执行)中调用Task.Run(() => RunSafetyNetCheck(client, nonce)).Result,Result会阻塞主线程等待异步任务返回,而await client.AttestAsync执行完成后需要回传上下文到主线程,导致互相等待的死锁,自然拿不到返回结果。
  • 业务逻辑倒置:RunSafetyNetCheck返回true代表验证通过,你现有代码中if (!valid) LoadApplication(new App())逻辑完全写反,会导致验证通过的设备反而没法进入应用。
  • 校验字段缺失:你只校验了ctsProfileMatch,缺少basicIntegrity字段的判断,这个字段才是直接标识设备是否root/运行在模拟器的核心指标,ctsProfileMatch是额外的系统兼容性认证校验。
  • 缺少权限和配置校验:没有检查网络权限、Google Play服务权限,也没有提前校验API密钥是否正确配置。

修复步骤

1. 提前配置校验

  • 首先确认你已在Google Cloud控制台启用SafetyNet Attestation API,并且在API密钥的限制中添加了你应用包名对应的SHA-256签名指纹,否则请求会被Google直接拒绝。
  • 在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

2. 修复死锁和逻辑问题

不要在主线程用.Result阻塞,把SafetyNet校验改成异步执行,完成后再加载应用,修正后的代码如下:

protected override void OnCreate(Bundle bundle)
{
    Instance = this;
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;
    BackgroundAggregator.Init(this);
    base.OnCreate(bundle);
    DependencyService.Register<ToastNotification>();
    ToastNotification.Init(this);
    Rg.Plugins.Popup.Popup.Init(this);
    global::Xamarin.Forms.Forms.Init(this, bundle);
    micService = DependencyService.Resolve<IMicrophoneService>();
    if (GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this, 13000000) == ConnectionResult.Success)
    {
        // 异步执行校验,不阻塞主线程
        _ = CheckSafetyNetAsync();
    }
    else
    {
        Toast.MakeText(this, "请更新Google Play服务", ToastLength.Short).Show();
        // 可选:如果要求必须支持SafetyNet,这里可以直接退出应用
        LoadApplication(new App());
    }
}
private async Task CheckSafetyNetAsync()
{
    SafetyNetClient client = Android.Gms.SafetyNet.SafetyNetClass.GetClient(this);
    byte[] nonce = Android.Gms.SafetyNet.Nonce.Generate();
    bool isValid = await RunSafetyNetCheck(client, nonce);
    if (isValid)
    {
        // 校验通过,加载应用
        LoadApplication(new App());
    }
    else
    {
        Toast.MakeText(this, "设备环境不安全,无法运行应用", ToastLength.Long).Show();
        // 可选:退出应用
        Finish();
    }
}
private async Task<bool> RunSafetyNetCheck(SafetyNetClient client, byte[] nonce)
{
    try
    {
        SafetyNetApiAttestationResponse r = await client.AttestAsync(nonce, apiKey);
        if (r == null || string.IsNullOrEmpty(r.JwsResult))
        {
            Toast.MakeText(this, "兼容性检查失败", ToastLength.Long).Show();
            return false;
        }
        attestationResponse = r;
        var decodedResult = r.DecodeJwsResult(nonce);
        string error = null;
        if (VerifyAttestResponse(decodedResult, nonce, out error))
        {
            return await attestationResponse.ValidateWithGoogle(apiKey);
        }
        else
        {
            Toast.MakeText(this, "兼容性不通过: " + error, ToastLength.Long).Show();
            return false;
        }
    }
    catch (Exception ex)
    {
        Toast.MakeText(this, "校验出错: " + ex.Message, ToastLength.Long).Show();
        return false;
    }
}
private bool VerifyAttestResponse(string data, byte[] sentNonce, out string errorMessage)
{
    errorMessage = null;
    var json = JsonObject.Parse(data);
    var error = GetValue(json, "error");
    if (!string.IsNullOrEmpty(error))
    {
        errorMessage = "响应返回错误: " + error;
        return false;
    }
    var nonce = GetValue(json, "nonce");
    var sentNonceStr = Convert.ToBase64String(sentNonce);
    if (!nonce.Equals(sentNonceStr))
    {
        errorMessage = "随机数校验失败";
        return false;
    }
    if (PackageName != GetValue(json, "apkPackageName"))
    {
        errorMessage = "包名校验失败";
        return false;
    }
    // 新增基础完整性校验,判断root/模拟器
    bool basicIntegrity = GetValue(json, "basicIntegrity") == "true";
    if (!basicIntegrity)
    {
        errorMessage = "设备已root或运行在模拟器环境";
        return false;
    }
    // 可选:如果需要严格校验系统合规性,保留ctsProfileMatch判断
    bool ctsProfileMatch = GetValue(json, "ctsProfileMatch") == "true";
    if (!ctsProfileMatch)
    {
        errorMessage = "设备未通过CTS认证";
        return false;
    }
    return true;
}
private string GetValue(JsonValue json, string field)
{
    return json.ContainsKey(field) ? json[field].ToString().Trim('"') : string.Empty;
}

3. 额外说明

目前Google已正式停用SafetyNet Attestation API,建议你后续迁移到Play Integrity API,新的API对设备环境的判断准确率更高,也不会有废弃风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 11:30:01