pva*_*lle 30 java android google-api integrity
我需要一些帮助!我是一个自学成才的加密新手,在阅读、测试和错误了两个多星期后如何解决这个问题,发现大众知识很少,几乎没有来自谷歌的文档。
我正在尝试阅读诚信判决,我已经设法做到IntegrityTokenRequest了
String nonce = Base64.encodeToString("this_is_my_nonce".getBytes(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
IntegrityManager myIntegrityManager = IntegrityManagerFactory
.create(getApplicationContext());
// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> myIntegrityTokenResponse = myIntegrityManager
.requestIntegrityToken(IntegrityTokenRequest
.builder()
.setNonce(nonce)
.build());
myIntegrityTokenResponse.addOnSuccessListener(new OnSuccessListener<IntegrityTokenResponse>() {
@Override
public void onSuccess(IntegrityTokenResponse myIntegrityTokenResponse) {
String token = myIntegrityTokenResponse.token();
// so here I have my Integrity token.
// now how do I read it??
}
}
Run Code Online (Sandbox Code Playgroud)
根据文档,这一切都是在 Play Console 中设置的,并相应地创建了 Google Cloud 项目。现在文档中出现了一个大漏洞:
a) JWT 有 4 个点,将 JWT 分为 5 个部分,而不是此处描述的 3 个部分https://jwt.io/
b) Developer.Android.com 建议在 Google 服务器上解密和验证

我不知道如何或将要执行这个命令...:-(
c) 如果我选择解密并验证返回的令牌,情况会更复杂,因为我没有自己的安全服务器环境,只有我的应用程序和 Google Play 控制台。
d) 我在我下载的 Google Cloud Platform OAuth 2.0 客户端 ID“Android client for com.company.project”JSON 文件中找到了它,但(再次)不知道如何在我的应用程序中使用它来从诚信令牌。
{"installed":
{"client_id":"123456789012-abcdefghijklmnopqrstuvwxyza0g2ahk.apps.googleusercontent.com",
"project_id":"myproject-360d3",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":https://www.googleapis.com/oauth2/v1/certs
}
}
Run Code Online (Sandbox Code Playgroud)
我确信我错过了很多,请帮忙
pva*_*lle 13
非常感谢@John_S的回答,我将其标记为最终答案,无论如何,我在这里为未来的开发人员发布了所有缺失的部分,这样他们就可以缩短我在这个问题上近一个月的时间,因为没有完整的文档,也没有Google PlayIntegrity API 的 java 示例(在撰写本文时)。
首先,您需要按照@John_S的说明在 Google Cloud 和 Google Play 中设置我们的项目,但缺少的部分是您需要将 Credential 设置为“ Service Account ”,然后按照java.io的描述设置“ Add Key ” .IOException:从流中读取凭据时出错,未指定“类型”字段,并且此https://developers.google.com/workspace/guides/create-credentials#android;然后,您可以使用您的凭据下载 .json 文件。我的问题中描述的 .json 文件无效,因为它必须具有如下结构:
{ "type": "service_account",
"project_id": "your-project",
"private_key_id": "your-key-id",
"private_key": "your-private-key",
"client_email": "your-email@appspot.gserviceaccount.com",
"client_id": "your-client-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-email%40appspot.gserviceaccount.com"
}
Run Code Online (Sandbox Code Playgroud)
其次,下载有效的 .json 文件后,将其存储在“ src/main/resources/credentials.json ”中(如果需要,请创建新文件夹,而不是“res”文件夹),如此处所述client_secrets 必须在哪里。 json 文件位于 Android Studio 项目文件夹树中吗?
第三,要完成 build.gradle 中所有缺失的部分,您必须包括:
dependencies {
implementation 'com.google.android.play:integrity:1.0.1'
implementation 'com.google.apis:google-api-services-playintegrity:v1-rev20220211-1.32.1'
implementation 'com.google.api-client:google-api-client-jackson2:1.20.0'
implementation 'com.google.auth:google-auth-library-credentials:1.7.0'
implementation 'com.google.auth:google-auth-library-oauth2-http:1.7.0'
}
Run Code Online (Sandbox Code Playgroud)
并将它们导入到您的项目中
import com.google.android.gms.tasks.Task;
import com.google.android.play.core.integrity.IntegrityManager;
import com.google.android.play.core.integrity.IntegrityManagerFactory;
import com.google.android.play.core.integrity.IntegrityTokenRequest;
import com.google.android.play.core.integrity.IntegrityTokenResponse;
import com.google.api.services.playintegrity.v1.PlayIntegrity;
import com.google.api.services.playintegrity.v1.PlayIntegrityRequestInitializer;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenRequest;
import com.google.api.services.playintegrity.v1.model.DecodeIntegrityTokenResponse;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
Run Code Online (Sandbox Code Playgroud)
然后,请求“完整性令牌”并对其进行解码的完整代码为:
// create the NONCE Base64-encoded, URL-safe, and non-wrapped String
String mynonce = Base64.encodeToString("this_is_my_nonce".getBytes(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
// Create an instance of a manager.
IntegrityManager myIntegrityManager = IntegrityManagerFactory.create(getApplicationContext());
// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> myIntegrityTokenResponse = myIntegrityManager
.requestIntegrityToken(IntegrityTokenRequest
.builder()
.setNonce(mynonce)
// .setCloudProjectNumber(cloudProjNumber) // necessary only if sold outside Google Play
.build());
// get the time to check against the decoded integrity token time
timeRequest = Calendar.getInstance().getTimeInMillis();
myIntegrityTokenResponse.addOnSuccessListener(new OnSuccessListener<IntegrityTokenResponse>() {
@Override
public void onSuccess(IntegrityTokenResponse myIntegrityTokenResponse) {
try {
String token = myIntegrityTokenResponse.token();
DecodeIntegrityTokenRequest requestObj = new DecodeIntegrityTokenRequest();
requestObj.setIntegrityToken(token);
//Configure your credentials from the downloaded Json file from the resource
GoogleCredentials credentials = GoogleCredentials.fromStream(Objects.requireNonNull(getClass().getClassLoader()).getResourceAsStream("credentials.json"));
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleClientRequestInitializer initializer = new PlayIntegrityRequestInitializer();
PlayIntegrity.Builder playIntegrity = new PlayIntegrity.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer).setApplicationName("your-project")
.setGoogleClientRequestInitializer(initializer);
PlayIntegrity play = playIntegrity.build();
// the DecodeIntegrityToken must be run on a parallel thread
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
DecodeIntegrityTokenResponse response = play.v1().decodeIntegrityToken("com.project.name", requestObj).execute();
String licensingVerdict = response.getTokenPayloadExternal().getAccountDetails().getAppLicensingVerdict();
if (licensingVerdict.equalsIgnoreCase("LICENSED")) {
// Looks good! LICENSED app
} else {
// LICENSE NOT OK
}
} catch (Exception e) {
// LICENSE error
}
}
});
// execute the parallel thread
thread.start();
} catch (Error | IOException e) {
// LICENSE error
} catch (Exception e) {
// LICENSE error
}
}
});
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助。
Joh*_*n_S 12
使用云服务器来解码和验证令牌效果更好。例如,如果您使用 Java 服务,那么下面的代码会将完整性令牌发送到 google 服务器,因此您可以验证响应。在 Google Cloud Platform 中针对应用启用 PlayIntegrity API,下载 JSON 文件并在代码中进行配置。同样,您应该针对应用程序在 Google PlayConsole 中启用 PlayIntegrity API 将 Google Play Integrity Client Library 添加到您的项目
Maven依赖
<project>
<dependencies>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-playintegrity</artifactId>
<version>v1-rev20220211-1.32.1</version>
</dependency>
</dependencies>
Run Code Online (Sandbox Code Playgroud)
摇篮
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.apis:google-api-services-playintegrity:v1-rev20220211-1.32.1'
}
Run Code Online (Sandbox Code Playgroud)
令牌解码
DecodeIntegrityTokenRequest requestObj = new DecodeIntegrityTokenRequest();
requestObj.setIntegrityToken(request.getJws());
//Configure downloaded Json file
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("<Path of JSON file>\\file.json"));
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleClientRequestInitializer initialiser = new PlayIntegrityRequestInitializer();
Builder playIntegrity = new PlayIntegrity.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer).setApplicationName("testapp")
.setGoogleClientRequestInitializer(initialiser);
PlayIntegrity play = playIntegrity.build();
DecodeIntegrityTokenResponse response = play.v1().decodeIntegrityToken("com.test.android.integritysample", requestObj).execute();
Run Code Online (Sandbox Code Playgroud)
那么响应将如下
{
"tokenPayloadExternal": {
"accountDetails": {
"appLicensingVerdict": "LICENSED"
},
"appIntegrity": {
"appRecognitionVerdict": "PLAY_RECOGNIZED",
"certificateSha256Digest": ["pnpa8e8eCArtvmaf49bJE1f5iG5-XLSU6w1U9ZvI96g"],
"packageName": "com.test.android.integritysample",
"versionCode": "4"
},
"deviceIntegrity": {
"deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"]
},
"requestDetails": {
"nonce": "SafetyNetSample1654058651834",
"requestPackageName": "com.test.android.integritysample",
"timestampMillis": "1654058657132"
}
}
}
Run Code Online (Sandbox Code Playgroud)
检查许可证
String licensingVerdict = response.getTokenPayloadExternal().getAccountDetails().getAppLicensingVerdict();
if(!licensingVerdict.equalsIgnoreCase("LICENSED")) {
throw new Exception("Licence is not valid.");
}
Run Code Online (Sandbox Code Playgroud)
验证应用程序完整性
public void checkAppIntegrity(DecodeIntegrityTokenResponse response, String appId) throws Exception {
AppIntegrity appIntegrity = response.getTokenPayloadExternal().getAppIntegrity();
if(!appIntegrity.getAppRecognitionVerdict().equalsIgnoreCase("PLAY_RECOGNIZED")) {
throw new Exception("The certificate or package name does not match Google Play records.");
}
if(!appIntegrity.getPackageName().equalsIgnoreCase(appId)) {
throw new Exception("App package name mismatch.");
}
if(appIntegrity.getCertificateSha256Digest()!= null) {
//If the app is deployed in Google PlayStore then Download the App signing key certificate from Google Play Console (If you are using managed signing key).
//otherwise download Upload key certificate and then find checksum of the certificate.
Certificate cert = getCertificate("<Path to Signing certificate>\deployment_cert.der");
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] der = cert.getEncoded();
md.update(der);
byte[] sha256 = md.digest();
//String checksum = Base64.getEncoder().encodeToString(sha256);
String checksum = Base64.getUrlEncoder().encodeToString(sha256);
/** Sometimes checksum value ends with '=' character, you can avoid this character before perform the match **/
checksum = checksum.replaceAll("=","");
if(!appIntegrity.getCertificateSha256Digest().get(0).contains(checksum)) {
throw new Exception("App certificate mismatch.");
}
}
}
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;
}
Run Code Online (Sandbox Code Playgroud)
验证设备完整性
//Check Device Integrity
public void deviceIntegrity(DecodeIntegrityTokenResponse response) {
DeviceIntegrity deviceIntegrity = response.getTokenPayloadExternal().getDeviceIntegrity();
if(!deviceIntegrity.getDeviceRecognitionVerdict().contains("MEETS_DEVICE_INTEGRITY")) {
throw new Exception("Does not meet Device Integrity.");
}
}
Run Code Online (Sandbox Code Playgroud)
类似地,您可以使用服务器中先前存储的数据来验证 Nonce 和应用程序包名称
| 归档时间: |
|
| 查看次数: |
10838 次 |
| 最近记录: |