Java Google SafetyNet API JwsResult是一个哈希字符串,而不是JSON

Java Google SafetyNet API JwsResult是一个哈希字符串,而不是JSON,java,android,safetynet,safetynet-api,Java,Android,Safetynet,Safetynet Api,我已经成功地实现了谷歌安全网API,甚至有一个成功的响应。问题是来自DetectionResponse的JWSResult是一个哈希字符串,而我的期望是在响应中得到一个JSON 请问我需要先在哪里查找问题 下面是调用assert()的代码: fun callSafetyNetAttentationApi(context: Activity, callback: SafetyNetCallback) { if (GoogleApiAvailability.getInstance(

我已经成功地实现了谷歌安全网API,甚至有一个成功的响应。问题是来自
DetectionResponse
JWSResult
是一个哈希字符串,而我的期望是在响应中得到一个JSON

请问我需要先在哪里查找问题

下面是调用
assert()
的代码:

    fun callSafetyNetAttentationApi(context: Activity, callback: SafetyNetCallback) {

    if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) {

        val nonce: ByteArray? = getRequestNonce("Safety Net Sample: " + System.currentTimeMillis())
        val client = SafetyNet.getClient(context)

        nonce?.let {
            val task: Task<AttestationResponse> = client.attest(it, BuildConfig.SAFETY_NET_KEY)

            task.addOnSuccessListener { response -> safetyNetSuccess(response, callback) }
                    .addOnFailureListener { callback.isDeviceTrusted(false) }

        } ?: kotlin.run {
            callback.isDeviceTrusted(false)
        }

    } else {
        MaterialDialog.Builder(context)
                .title("The app cannot be used")
                .content("Please update Google Play Services and try again.")
                .cancelable(false)
                .positiveText("Dismiss")
                .onPositive { dialog, which -> context.finish() }
                .show()
    }
}
fun callsafetynetattentionapi(上下文:活动,回调:SafetyNetCallback){
if(GoogleAppAvailability.getInstance().isGooglePlayServicesAvailable(上下文)==ConnectionResult.SUCCESS){
val nonce:ByteArray?=getRequestNonce(“安全网示例:+System.currentTimeMillis())
val client=SafetyNet.getClient(上下文)
现在?让我来{
val task:task=client.authentice(it、BuildConfig.SAFETY\u NET\u KEY)
task.addOnSuccessListener{response->safetyNetSuccess(响应,回调)}
.addOnFailureListener{callback.isDeviceTrusted(false)}
}?:kotlin.run{
callback.isDeviceTrusted(false)
}
}否则{
MaterialDialog.Builder(上下文)
.title(“应用程序无法使用”)
.content(“请更新Google Play服务并重试。”)
.可取消(错误)
.positiveText(“驳回”)
.onPositive{dialog,其中->context.finish()}
.show()
}
}

这是一个典型的JSON响应,您将在执行
safetyNetClient.attest(nonce,apiKey)
后收到该响应:

这里的
foo.bar.zar
是一个base64编码字符串,类似于
aisnfaksdf.8439hundf.ghbadsjn
,其中每个部分对应于:

<Base64 encoded header>.<Base64 encoded JSON data>.<Base64 encoded signature>
然后使用您喜欢的JSON库构造java对象,例如GSON:

val model = Gson().fromJson(extractJwsData(jws).toString(), SafetyNetApiModel::class.java)
其中
SafetyNetApiModel
为:

class SafetyNetApiModel {
    @SerializedName("nonce")
    var nonce: String? = null

    @SerializedName("timestampMs")
    var timestampMs: Long? = null

    @SerializedName("apkPackageName")
    var apkPackageName: String? = null

    @SerializedName("apkCertificateDigestSha256")
    var apkCertificateDigestSha256: List<String>? = null

    @SerializedName("apkDigestSha256")
    var apkDigestSha256: String? = null

    @SerializedName("ctsProfileMatch")
    var ctsProfileMatch: Boolean? = null

    @SerializedName("basicIntegrity")
    var basicIntegrity: Boolean? = null
}
类安全Netapimodel{
@序列化名称(“nonce”)
变量nonce:字符串?=null
@SerializedName(“时间戳”)
var timestampMs:Long?=null
@SerializedName(“apkPackageName”)
var apkPackageName:字符串?=null
@SerializedName(“apkCertificateDigestSha256”)
var apkCertificateDigestSha256:列表?=空
@SerializedName(“apkDigestSha256”)
var apkDigestSha256:字符串?=null
@SerializedName(“ctsProfileMatch”)
var ctsProfileMatch:布尔?=null
@序列化名称(“基本完整性”)
变量基本完整性:布尔?=null
}

查看以供参考。

JWS响应始终是符号结果。在获得JWS响应后,您必须使用nonce从服务器端代码验证它,然后服务器将验证该请求,如果它是有效的请求,那么它将返回如下JSON响应

查看链接

对于示例应用程序:

private OnSuccessListener<SafetyNetApi.AttestationResponse> mSuccessListener =
        new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
            @Override
            public void onSuccess(SafetyNetApi.AttestationResponse attestationResponse) {
                /*
                 Successfully communicated with SafetyNet API.
                 Use result.getJwsResult() to get the signed result data. See the server
                 component of this sample for details on how to verify and parse this result.
                 */
                mResult = attestationResponse.getJwsResult();
                Log.d(TAG, "Success! SafetyNet result:\n" + mResult + "\n");

                    /*
                     TODO(developer): Forward this result to your server together with
                     the nonce for verification.
                     You can also parse the JwsResult locally to confirm that the API
                     returned a response by checking for an 'error' field first and before
                     retrying the request with an exponential backoff.
                     NOTE: Do NOT rely on a local, client-side only check for security, you
                     must verify the response on a remote server!
                    */
            }
        };
private OnSuccessListener msSuccessListener=
新OnSuccessListener(){
@凌驾
成功时公共无效(SafetyNetApi.DetectionResponse-DetectionResponse-DetectionResponse){
/*
已成功与SafetyNet API通信。
使用result.getJwsResult()获取签名结果数据。请参阅服务器
有关如何验证和分析此结果的详细信息,请参阅此示例的组件。
*/
mResult=DetectionResponse.getJwsResult();
Log.d(标记“Success!SafetyNet result:\n”+mResult+“\n”);
/*
TODO(开发人员):将此结果连同
用于验证的临时值。
您还可以在本地解析JwsResult,以确认API
通过先检查“错误”字段,然后再检查返回响应
使用指数回退重试请求。
注意:不要依赖本地、客户端仅检查安全性,否则会导致
必须在远程服务器上验证响应!
*/
}
};

阅读成功响应中的注释,此代码来自

我没有遵循您的代码,因为您无法将字节数组转换为json。因此,在转换为json之前,我将其转换为字符串,但这就是我得到的”[B@efc8722"这可能不是一个100%的解决方案,但我尝试只在应用程序级别应用它。请参阅我对第一个答案的评论。你不需要转换为Base64,只需将JWS响应哈希字符串发送到服务器进行验证,它总是有效的,我已经检查了客户端和服务器端的代码,它对我有效,但我的后端没有执行修订后的safetynet服务器端。因此,如何测试设备完整性,如果您正在将服务器代码写入客户端应用程序,那么它的用途是什么。您正在客户端应用程序中生成nonce,并验证响应是否也在客户端应用程序中。您将如何检查设备是否已根目录和设备完整性,任何黑客或攻击者都将绕过下载并将你的应用程序运行到根设备中。嘿@klutch,你是否在不从服务器调用API的情况下将JWS响应解析为JSON?如果完成了,请告诉我,这将对我有帮助。你需要Base64。解码jwsResult,以提取JSON。
class SafetyNetApiModel {
    @SerializedName("nonce")
    var nonce: String? = null

    @SerializedName("timestampMs")
    var timestampMs: Long? = null

    @SerializedName("apkPackageName")
    var apkPackageName: String? = null

    @SerializedName("apkCertificateDigestSha256")
    var apkCertificateDigestSha256: List<String>? = null

    @SerializedName("apkDigestSha256")
    var apkDigestSha256: String? = null

    @SerializedName("ctsProfileMatch")
    var ctsProfileMatch: Boolean? = null

    @SerializedName("basicIntegrity")
    var basicIntegrity: Boolean? = null
}
private OnSuccessListener<SafetyNetApi.AttestationResponse> mSuccessListener =
        new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
            @Override
            public void onSuccess(SafetyNetApi.AttestationResponse attestationResponse) {
                /*
                 Successfully communicated with SafetyNet API.
                 Use result.getJwsResult() to get the signed result data. See the server
                 component of this sample for details on how to verify and parse this result.
                 */
                mResult = attestationResponse.getJwsResult();
                Log.d(TAG, "Success! SafetyNet result:\n" + mResult + "\n");

                    /*
                     TODO(developer): Forward this result to your server together with
                     the nonce for verification.
                     You can also parse the JwsResult locally to confirm that the API
                     returned a response by checking for an 'error' field first and before
                     retrying the request with an exponential backoff.
                     NOTE: Do NOT rely on a local, client-side only check for security, you
                     must verify the response on a remote server!
                    */
            }
        };