2017-02-15 134 views
1

我实现安全网API。第一部分是处理的代码,我在SafetyNetSampleFragment使用:安全网:包名总是返回基于<a href="https://github.com/googlesamples/android-play-safetynet" rel="nofollow noreferrer">Google SafetyNet sample</a>和<a href="https://github.com/scottyab/safetynethelper" rel="nofollow noreferrer">SafetyNet Helper</a></p> <p>这是我工作的代码空

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 + 
       '}'; 
    } 
} 

我们可以很容易地通过调用活动中这行代码调用上面可行的代码:

new SafetyNetVerifier(this); 

结果是:

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

正确解析时间戳。我想我已经成功地获得了安全网的回应。但我不知道为什么apkPackageName始终为空,而我所显示的其他字段为空。请帮帮我。

+1

您可能想检查有关[检查设备与SafetyNet的兼容性 - 阅读兼容性检查响应](https://developer.android.com/training/safetynet/index.html#compat-check-response)的文档: “'apkPackageName'”,“'apkCertificateDigestSha256'”和“'apkDigestSha256'”字段提供有关可用于验证调用应用程序身份的APK的信息。如果API无法可靠地确定APK信息,则这些字段不存在。产生'null'结果表明对服务的调用没有成功完成。 –

回答

2

在您的SafetyNetResponse对象中,您会注意到basicIntegrity是错误的。这表明某种系统篡改其他修改已被检测到(生根就是这方面的一个例子)。

这给出了为什么APK信息字段不存在的线索。正如documentation描述:

apkPackageNameapkCertificateDigestSha256apkDigestSha256领域提供有关APK,你可以用它来验证调用应用程序的身份信息。如果API无法可靠地确定APK信息,则这些字段不存在。

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

+0

谢谢。我已经尝试了以上代码的正常设备,它的工作原理:D它应该在正常设备firsit之前测试此api:D –

相关问题