Google depreciated SafetyAPI and Introduced PlayIntegrity API for attestation. PlayIntegrity Service provides the response as follows.
{
"tokenPayloadExternal": {
"accountDetails": {
"appLicensingVerdict": "LICENSED"
},
"appIntegrity": {
"appRecognitionVerdict": "PLAY_RECOGNIZED",
"certificateSha256Digest": ["pnpa8e8eCArtvmaf49bJE1f5iG5-XLSU6w1U9ZvI96g"],
"packageName": "com.test.android.safetynetsample",
"versionCode": "4"
},
"deviceIntegrity": {
"deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"]
},
"requestDetails": {
"nonce": "SafetyNetSample1654058651834",
"requestPackageName": "com.test.android.safetynetsample",
"timestampMillis": "1654058657132"
}
}}
Response contains only certificateSha256Digest of the app (The sha256 digest of app certificates) instead of having apkDigestSha256 and apkCertificateDigestSha256.
How do we validate the received certificateSha256Digest at server?
Below steps:-
public static Certificate getCertificate(String certificatePath)throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
FileInputStream in = new FileInputStream(certificatePath);
Certificate certificate = certificateFactory.generateCertificate(in);
in.close();
return certificate;
}
Generate checksum of the certificate
Certificate x509Cert = getCertificate("<Path of file>/deployment_cert.der");
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] x509Der = x509Cert.getEncoded();
md.update(x509Der);
byte[] sha256 = md.digest();
String checksum = Base64.getEncoder().encodeToString(sha256);
Then compare checksum with received certificateSha256Digest
String digest = jwsResponse.tokenPayloadExternal.appIntegrity.certificateSha256Digest;
if(checksum.contains(digest)){
//
}