Google Integrity API For Xamarin.Forms

c3frrgcw  于 12个月前  发布在  Go
关注(0)|答案(1)|浏览(144)

如何使用Google最新的Integerty API保护我们的Xamarin.forms Android应用程序?我发现Google Play Services库“com.google.android.play:integrity”有一个绑定。Xamarin.Google.Android.Play.Integrity。但没有相关文档或任何相关内容。如何使用此Nuget并在Xamarin.Forms Android应用程序上使用Google integrity API?任何帮助都是appriciated。

kyxcudwk

kyxcudwk1#

由于我也很难实现这一点,最后在没有在互联网上找到太多信息/文档/样本的情况下成功了,我在这里分享我的“定制”解决方案:-)

表单项目:

  • 为你的结果创建一个接口(用于表单抽象/依赖注入)和一个类:
public interface IPlayIntegrityService
{
    Task<bool> InitPlayIntegrityAsync();
    Task<List<PlayIntegrityResult>> CheckPlayIntegrityWithDetailsAsync();
}

public class  PlayIntegrityResult
{
    public string VerificationName { get; set; }
    public string VerificationResult { get; set; }
    public bool IsCompliant { get; set; }
    public bool IsRequired { get; set; }
}

字符串

Android项目:

  • 添加Xamarin.Google.Android.Play.Integrity(1.2.0.2)Nuget
  • 添加Google.Apis(1.64.0)Nuget
  • 添加Google.Apis.PlayIntegrity.v1(1.64.0.3253)Nuget
  • 创建IPlayIntegrityService的Android实现(并在启动时注入DI容器)
using Android.Gms.Common.Apis;
using Android.Gms.Extensions;
using Android.Gms.Tasks;
using Android.Runtime;
using Google.Apis.Auth.OAuth2;
using Google.Apis.PlayIntegrity.v1.Data;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.Text.Json;
using Xamarin.Google.Android.Play.Core.Integrity;
using platform = Xamarin.Essentials.Platform;
using Tasks = System.Threading.Tasks;

public class PlayIntegrityService : Java.Lang.Object, IOnFailureListener, IOnSuccessListener, IPlayIntegrityService
{
    IStandardIntegrityManagerStandardIntegrityTokenProvider integrityTokenProvider;
    const string requestHash = "myCustomHashInBase64"; // <- your custom Base64Hash (min. 16 car, base64 encoded)
    const string packagename = "my.Package.Identifier"; //<-your android package id
    const string googleAppName = "MyApplicationName"; // <- your android application name
    const double googleProjectNumber = 012345678901; //<-your google cloud project nb
    const string googleJSONCredentials = "YOURJSONCREDENTIALS"; // your Google cloud API JSON credentials
    
    private string responseToken;
    

    public async Tasks.Task<bool> InitPlayIntegrityAsync()
    {
        if (integrityTokenProvider == null)
        {
            try
            {
                // Create an instance of a manager.
                IStandardIntegrityManager standardIntegrityManager = IntegrityManagerFactory.CreateStandard(platform.AppContext);

                //Create "PREPARE" Request
                var request = StandardIntegrityManagerPrepareIntegrityTokenRequest
                    .InvokeBuilder()
                    .SetCloudProjectNumber(googleProjectNumber)
                    .Build();

                // execute "PREPARE" request
                await standardIntegrityManager.PrepareIntegrityToken(request)
                    .AddOnSuccessListener(this)
                    .AddOnFailureListener(this);

                //Wait for tokenProvider (max 15s)
                int nbtries = 0;
                while (integrityTokenProvider == null && nbtries < 15)
                {
                    await Tasks.Task.Delay(1000);
                    nbtries++;
                }

                //wrong init if no token provider has been received during 15 seconds...
                if (integrityTokenProvider == null)
                {
                    return false;
                }

                //All is prepared correctly !
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        else
        {
            //Provider already initialized, all is OK
            return true;
        }

    }
    
    public void OnFailure(Java.Lang.Exception e)
    {
        integrityTokenProvider = null;
        Console.Write($"OnFailure : {e}");
    }

    public void OnSuccess(Java.Lang.Object result)
    {
        //If we received a "Prepare" response
        try
        {
            var castedresult = result?.JavaCast<IStandardIntegrityManagerStandardIntegrityTokenProvider>();
            if (castedresult != null)
            {
                integrityTokenProvider = castedresult;
            }
            Console.Write("OnSuccess : Received IntegrityTokenProvider !");
        }
        catch {} //Handle the error for not throwing

        //If we received a "Token" response
        try
        {
            var castedresult = (StandardIntegrityManagerStandardIntegrityToken)result;
            if (castedresult != null)
            {
                responseToken = castedresult.Token();
            }
            Console.Write("OnSuccess : Received IntegrityTokenResponse !");
        }
        catch {} //Handle the error for not throwing
    }

    // THIS IS OUR CUSTOM RULES FOR VERIFICATIONS WE WANTED/NEEDED... ADAPT THESE VERIFICATIONS TO YOUR OWN RULES !!!
    public async Tasks.Task<List<PlayIntegrityResult>>CheckPlayIntegrityWithDetailsAsync()
    {
        //Result Initialization
        List<PlayIntegrityResult> results = new List<PlayIntegrityResult>()
        {
            new PlayIntegrityResult() { VerificationName = "PackageName & Hash", IsRequired = true, VerificationResult = "Not evaluated", IsCompliant = false},
            new PlayIntegrityResult() { VerificationName = "Device integrity", IsRequired = true, VerificationResult = "Not evaluated", IsCompliant = false },
            new PlayIntegrityResult() { VerificationName = "Application integrity", IsRequired = false, VerificationResult = "Not evaluated", IsCompliant = false },
            new PlayIntegrityResult() { VerificationName = "GooglePlay licence", IsRequired = false, VerificationResult = "Not evaluated", IsCompliant = false},
            new PlayIntegrityResult() { VerificationName = "PlayProtect state", IsRequired = false, VerificationResult = "Not evaluated", IsCompliant = false }
        };
        try
        {
            if (integrityTokenProvider != null)
            {
                // Request integrity token
                await integrityTokenProvider
                        .Request(StandardIntegrityManagerStandardIntegrityTokenRequest
                                       .InvokeBuilder()
                                       .SetRequestHash(requestHash)
                                       .Build())
                        .AddOnSuccessListener(this)
                        .AddOnFailureListener(this);

                //Wait for Token response (max 15s)
                int nbtries = 0;
                while (String.IsNullOrWhiteSpace(responseToken) && nbtries < 15)
                {
                    await Tasks.Task.Delay(1000);
                    nbtries++;
                }

                //wrong check if no responseToken has been received during 15 seconds...
                if (String.IsNullOrWhiteSpace(responseToken))
                {
                    return null;
                }

                //Need to connect GoogleCloudAPI/PlayIntegrityAPI (it's not the same as PlayIntegritySDK !!! (normaly server-side... but here I consider my android/mobileApp can do everything on its own, withour server side app...)
                GoogleCredential credential = GoogleCredential.FromJson(googleJSONCredentials);
                var service = new Google.Apis.PlayIntegrity.v1.PlayIntegrityService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = googleAppName });
                var requestAPI = service.V1.DecodeIntegrityToken(new DecodeIntegrityTokenRequest { IntegrityToken = responseToken }, packagename);
                var result = await requestAPI.ExecuteAsync();
                if (result != null)
                {
                    //We check our custom verifications one by one
                    //PackageName & Hash
                    var requestDetail = result.TokenPayloadExternal.RequestDetails;
                    if (requestDetail.RequestPackageName == packagename && requestDetail.RequestHash == requestHash)
                    {
                        results[0].VerificationResult = "OK";
                        results[0].IsCompliant = true;
                    }
                    else
                    {
                        results[0].VerificationResult = "NOK";
                        results[0].IsCompliant = false;
                    }
                    //DeviceIntegrity
                    var deviceVerdict = result.TokenPayloadExternal.DeviceIntegrity.DeviceRecognitionVerdict;
                    results[1].VerificationResult = JsonSerializer.Serialize(deviceVerdict, options: new JsonSerializerOptions() { WriteIndented = false });
                    if (deviceVerdict.Contains("MEETS_DEVICE_INTEGRITY") || deviceVerdict.Contains("MEETS_STRONG_INTEGRITY"))
                    {
                        results[1].IsCompliant = true;
                    }
                    else
                    {
                        results[1].IsCompliant = false;
                    }

                    //Application Integrity
                    var AppIntegrity = result.TokenPayloadExternal.AppIntegrity;
                    results[2].VerificationResult = AppIntegrity.AppRecognitionVerdict;
                    if (AppIntegrity.AppRecognitionVerdict == "PLAY_RECOGNIZED")
                    {
                        results[2].IsCompliant = true;
                    }

                    //GooglePlay Licence
                    var AccountDetail = result.TokenPayloadExternal.AccountDetails;
                    results[3].VerificationResult = AccountDetail.AppLicensingVerdict;
                    if (AccountDetail.AppLicensingVerdict == "LICENSED")
                    {
                        results[3].IsCompliant = true;
                    }

                    //PlayProtect
                    var playProtectVerdict = result.TokenPayloadExternal.EnvironmentDetails.PlayProtectVerdict;
                    results[4].VerificationResult = playProtectVerdict;
                    if (playProtectVerdict == "NO_ISSUES")
                    {
                        results[4].IsCompliant = true;
                    }
                    if (playProtectVerdict == "MEDIUM_RISK" || playProtectVerdict == "HIGH_RISK")
                    {
                        results[4].IsRequired = true;
                        results[4].IsCompliant = false;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.Write($"Exception during CheckPlayIntegrityWithDetailsAsync ({ex})");
        }
        return results;
    }
}


要在Forms App中调用它,只需解析您的示例(取决于您的DI容器......这里我使用Shiny......)并调用您需要的方法:

var PlayService = ShinyHost.Resolve<IPlayIntegrityService>();
if (await PlayService?.InitPlayIntegrityAsync())
{
    var result = await PlayService?.CheckPlayIntegrityWithDetailsAsync();
    if (result != null && result.Count > 0)
    {
        var notCompliants = result.Where(x => x.IsCompliant == false && x.IsRequired == true);
        if (notCompliants != null && notCompliants.Count() > 0)
        {
            var notCompliantDescription = "";
            foreach (var notCompliant in notCompliants)
            {
                notCompliantDescription += string.Join(notCompliant.VerificationName, " : ", notCompliant.VerificationResult, Environment.NewLine);
            }
            await Dialogs?.Alert(notCompliantDescription, "Play Integrity Failed !");
            Logger.LogInformation("GooglePlayIntegrity not compliant : {0}", notCompliantDescription.Replace(Environment.NewLine, " - "));
        }
        else
        {
            isPlayIntegrityOk = true;
        }
    }
}


这是一个简化的/不是最好的干净的示例,你必须适应你的验证需求.但所有这一切都是目前在生产应用程序中正确运行在我身边(2023/12/07 - X.Forms 5.0.0.2622 - AndroidTargetFrameworkVersion=v13.0)
希望这能有所帮助;-)
热罗姆

相关问题