gpt4 book ai didi

android - SafetyNet:包名称总是返回 null

转载 作者:行者123 更新时间:2023-11-29 19:21:55 28 4
gpt4 key购买 nike

我正在根据 Google SafetyNet sample 实现 SafetyNet API和 SafetyNet Helper

这是我的工作代码。第一部分是我在 SafetyNetSampleFragment 中使用的处理代码:

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Base64;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Random;


public class SafetyNetVerifier implements GoogleApiClient.OnConnectionFailedListener {

private final Random mRandom = new SecureRandom();
private String mResult;
private GoogleApiClient mGoogleApiClient;

private FragmentActivity activity;

public SafetyNetVerifier(FragmentActivity activity) {
this.activity = activity;
buildGoogleApiClient();
sendSafetyNetRequest();
}

private byte[] getRequestNonce(String data) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] bytes = new byte[24];
mRandom.nextBytes(bytes);
try {
byteStream.write(bytes);
byteStream.write(data.getBytes());
} catch (IOException e) {
return null;
}

return byteStream.toByteArray();
}

protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addApi(SafetyNet.API)
.enableAutoManage(activity, this)
.build();
}

private void sendSafetyNetRequest() {
Log.e("hqthao", "Sending SafetyNet API request.");

String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
byte[] nonce = getRequestNonce(nonceData);

SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {

@Override
public void onResult(SafetyNetApi.AttestationResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
mResult = result.getJwsResult();
Log.e("hqthao", "Success! SafetyNet result:\n" + mResult + "\n");
SafetyNetResponse response = parseJsonWebSignature(mResult);
Log.e("hqthao", response.toString());
}
}
});
}

@Nullable
private SafetyNetResponse parseJsonWebSignature(String jwsResult) {
if (jwsResult == null) {
return null;
}
//the JWT (JSON WEB TOKEN) is just a 3 base64 encoded parts concatenated by a . character
final String[] jwtParts = jwsResult.split("\\.");

if (jwtParts.length == 3) {
//we're only really interested in the body/payload
String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));

return SafetyNetResponse.parse(decodedPayload);
} else {
return null;
}
}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e("hqthao", "Error connecting to Google Play Services." + connectionResult.getErrorMessage());
}

}

这是我从 SafetyNetResponse 复制的模型 SafetyNetResponse :

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Arrays;

public class SafetyNetResponse {

private static final String TAG = SafetyNetResponse.class.getSimpleName();
private String nonce;
private long timestampMs;
private String apkPackageName;
private String[] apkCertificateDigestSha256;
private String apkDigestSha256;
private boolean ctsProfileMatch;
private boolean basicIntegrity;

//forces the parse()
private SafetyNetResponse() {
}

/**
* @return BASE64 encoded
*/
public String getNonce() {
return nonce;
}

public long getTimestampMs() {
return timestampMs;
}

/**
* @return com.package.name.of.requesting.app
*/
public String getApkPackageName() {
return apkPackageName;
}

/**
* SHA-256 hash of the certificate used to sign requesting app
*
* @return BASE64 encoded
*/
public String[] getApkCertificateDigestSha256() {
return apkCertificateDigestSha256;
}

/**
* SHA-256 hash of the app's APK
*
* @return BASE64 encoded
*/
public String getApkDigestSha256() {
return apkDigestSha256;
}


/**
* If the value of "ctsProfileMatch" is true, then the profile of the device running your app matches the profile of a device that has passed Android compatibility testing.
*
* @return
*/
public boolean isCtsProfileMatch() {
return ctsProfileMatch;
}

/**
* If the value of "basicIntegrity" is true, then the device running your app likely wasn't tampered with, but the device has not necessarily passed Android compatibility testing.
*
* @return
*/
public boolean isBasicIntegrity() {
return basicIntegrity;
}

/**
* Parse the JSON string into populated SafetyNetResponse object
*
* @param decodedJWTPayload JSON String (always a json string according to JWT spec)
* @return populated SafetyNetResponse
*/
@Nullable
public static SafetyNetResponse parse(@NonNull String decodedJWTPayload) {

Log.d(TAG, "decodedJWTPayload json:" + decodedJWTPayload);

SafetyNetResponse response = new SafetyNetResponse();
try {
JSONObject root = new JSONObject(decodedJWTPayload);
if (root.has("nonce")) {
response.nonce = root.getString("nonce");
}

if (root.has("apkCertificateDigestSha256")) {
JSONArray jsonArray = root.getJSONArray("apkCertificateDigestSha256");
if (jsonArray != null) {
String[] certDigests = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
certDigests[i] = jsonArray.getString(i);
}
response.apkCertificateDigestSha256 = certDigests;
}
}

if (root.has("apkDigestSha256")) {
response.apkDigestSha256 = root.getString("apkDigestSha256");
}

if (root.has("apkPackageName")) {
response.apkPackageName = root.getString("apkPackageName");
}

if (root.has("basicIntegrity")) {
response.basicIntegrity = root.getBoolean("basicIntegrity");
}

if (root.has("ctsProfileMatch")) {
response.ctsProfileMatch = root.getBoolean("ctsProfileMatch");
}

if (root.has("timestampMs")) {
response.timestampMs = root.getLong("timestampMs");
}

return response;
} catch (JSONException e) {
Log.e(TAG, "problem parsing decodedJWTPayload:" + e.getMessage(), e);
}
return null;
}


@Override
public String toString() {
return "SafetyNetResponse{" +
"nonce='" + nonce + '\'' +
", timestampMs=" + timestampMs +
", apkPackageName='" + apkPackageName + '\'' +
", apkCertificateDigestSha256=" + Arrays.toString(apkCertificateDigestSha256) +
", apkDigestSha256='" + apkDigestSha256 + '\'' +
", ctsProfileMatch=" + ctsProfileMatch +
", basicIntegrity=" + basicIntegrity +
'}';
}
}

我们可以通过在 Activity 中调用这行代码来轻松调用上面的可行代码:

    new SafetyNetVerifier(this);

结果是:

SafetyNetResponse{
nonce='Xc4dSnAjAqf9KWDZokwK2TdBw9Td+ZILU2FmZXR5IE5ldCBTYW1wbGU6IDE0ODcxODQyMjYwNjc=',
timestampMs=1487184225994,
apkPackageName='null',
apkCertificateDigestSha256=[],
apkDigestSha256='null',
ctsProfileMatch=false,
basicIntegrity=false
}

作为正确解析的时间戳。我想我已成功获得安全网响应。但我不知道为什么 apkPackageName 总是空的,而我显示的其他字段是空的。请帮助我。

最佳答案

在您的 SafetyNetResponse 对象中,您会注意到 basicIntegrity 为 false。这表明已检测到某种系统篡改其他修改(root 就是其中一个例子)。

这提供了有关为什么缺少 APK 信息字段的线索。如 documentation 中所述:

The apkPackageName, apkCertificateDigestSha256, and apkDigestSha256 fields provide information about the APK that you can use to verify the identity of the calling app. These fields are absent if the API cannot reliably determine the APK information.

您的代码似乎工作正常。您可以通过在运行已批准的 Android 版本的未经修改的设备上进行测试来验证这一点 - 然后应该包含缺失的信息。

关于android - SafetyNet:包名称总是返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42258489/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com