前言
系統完整性檢測,是App需具備的一個實用功能,我們都知道,在系統不完整的手機上,例如被root過,運行App將面臨被惡意攻擊、竊取隱私等威脅,尤其是商城類App,購買環節的環境安全性至關重要,因此在App中增加能快速檢測手機系統風險的功能必不可少,
這一重要功能目前是免費,其基本的技術原理是,App集成華為HMS Core的SDK,呼叫免費提供的安全檢測服務,在TEE可信執行環境中評估,得到的檢測結果經過X.509數字證書簽名,雙重保障下,檢測到的結果真實可信、不會被惡意更改~
功能運行起來的效果:

以下是開發程序,分享給大家,
開發前準備

1.1 Android studio安裝
還沒裝開發工具的小伙伴下載指路:Android studio官網下載
1.2 在AppGallery Connect中配置相關資訊
在開發應用前,需在AppGallery Connect中配置相關資訊,具體操作步驟
1.3 配置華為maven倉地址
打開Android Studio專案級“build.gradle”檔案:

添加HUAWEI agcp插件以及Maven代碼庫:
- 在“buildscript > repositories”中配置HMS Core SDK的Maven倉地址,
- 在“allprojects > repositories”中配置HMS Core SDK的Maven倉地址,
- 如果App中添加了“agconnect-services.json”檔案則需要在“buildscript > dependencies”中增加agcp配置,
buildscript {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven倉地址,
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
...
// 增加agcp配置,
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
}
}
allprojects {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven倉地址,
maven {url 'https://developer.huawei.com/repo/'}
}
}
這里需要說明的是,Maven倉地址只能在IDE中配置,需要添加多個Maven代碼庫的話,將華為公司的Maven倉地址配置在最后哦,
1.4 添加編譯依賴
打開應用級的“build.gradle”檔案:

在檔案頭apply plugin: 'com.android.application'下一行添加如下配置:
apply plugin: 'com.huawei.agconnect'
在“dependencies”中添加如下編譯依賴:
dependencies {
implementation 'com.huawei.hms:safetydetect:5.0.5.302'
}
1.5 配置混淆腳本
如果你自己開發時要用到AndResGuard,那就還需要在應用級的“build.gradle”檔案中加入AndResGuard允許清單,代碼可以參考官網的混淆配置,
代碼開發

2.1 創建SafetyDetectClient 并生成nonce值
這里的nonce值會被包含在后面的檢測結果里,我們要通過校驗nonce值,來確定回傳結果是對應我們的請求的、沒有被重放攻擊,要注意nonce值需滿足:
l 一個nonce值只能被使用一次;
l 長度在16~66位元組間;
l 建議從發送到您的服務器的資料中派生nonce值,
2.2 請求系統完整性檢測介面
1) SysIntegrity API有兩個引數:第1個引數是nonce值,可以從上一步驟獲取;第2個引數是appid,可以從agconnect-services.json 檔案中讀取:
? 登錄AppGallery Connect網站,-點擊“我的專案”,
? 在專案串列中找到您的專案,在專案中點擊需要配置簽名證書指紋的應用,
? 在“專案設定 > 常規” > “應用”,可以查看,

2) 這里設定的是,用戶在購買會員時,呼叫系統完整性檢測介面,以檢測支付環境是否存在風險,實際編碼中,在MemberCenterAct.java類中的串列點擊事件處理方法,呼叫SafetyDetectUtil 類的detectSysIntegrity的介面,具體代碼:
private void onAdapterItemClick(int position) {
// 呼叫系統完整性檢測介面以檢測支付環境風險
SafetyDetectUtil.detectSysIntegrity(this, new ICallBack<Boolean>() {
@Override
public void onSuccess(Boolean baseIntegrity) {
if (baseIntegrity) {
// 系統完整性未遭到破壞,可以繼續購買
buy(productInfo);
} else {
// 系統完整性遭到破壞,彈出提示框,來提醒用戶,并讓用戶選擇是否繼續
showRootTipDialog(productInfo);
}
}
…
});
}
3) 在商場App中,把系統完整性檢測介面的呼叫放到SafetyDetectUtil.java這個工具類中來實作,封裝在detectSysIntegrity的方法中,具體示例代碼如下:
public static void detectSysIntegrity(final Activity activity, final ICallBack<? super Boolean> callBack) {
// 生成 nonce值
byte[] nonce = ("Sample" + System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8);
// 從app目錄下的agconnect-services.json檔案中讀取app_id欄位
String appId = AGConnectServicesConfig.fromContext(activity).getString("client/app_id");
// 獲取 Safety Detect 服務客戶端,呼叫sysIntegrity API,并添加成功事件監聽
SysIntegrityRequest sysintegrityrequest = new SysIntegrityRequest();
sysintegrityrequest.setAppid(appId);
sysintegrityrequest.setNonce(nonce);
//PS256 or RS256
sysintegrityrequest.setAlg("RS256");
Task task = mClient.sysIntegrity(sysintegrityrequest);
task.addOnSuccessListener(new OnSuccessListener<SysIntegrityResp>() {
@Override
public void onSuccess(SysIntegrityResp response) {
//Safety Detect 服務介面成功回應,可以通過 SysIntegrityResp 類的 getResult 方法來獲取檢測結果
String jwsStr = response.getResult();
VerifyResultHandler verifyResultHandler = new VerifyResultHandler(jwsStr, callBack);
//將檢測結果發送至開發者的服務器進行驗證
verifyJws(activity, jwsStr, verifyResultHandler);
}
});
}
4) 這里在verifyJws 方法中請求App Server的相關介面,來對檢測結果進行驗證,這個方法的第3個引數是一個 VerifyResultHandler 類物件, 它實作了一個回呼介面,以便在服務器驗證結束后,對回傳的結果進行后續的處理,接下來介紹如何在App Server中驗證檢測結果,
2.3 在App Server中驗證檢測結果
App在獲得TSMS Server回傳的檢測結果后,將其發送到App Server,由App Server使用HUAWEI CBG根證書來對結果中的簽名和證書鏈進行校驗,從而確認本次系統完整性檢測結果是否有效,
App Server側讀取證書并驗證 JWS 字串的示例代碼如下:
1) 決議 JWS字串,獲取其中的 header、payload和signature
public JwsVerifyResp verifyJws(JwsVerifyReq jwsVerifyReq) {
// 獲取端側發送到服務器側的jws資訊
String jwsStr = jwsVerifyReq.getJws();
// 決議JWS, 分段, 該JWS固定為三段,使用"."號分隔
String[] jwsSplit = jwsStr.split("\\.");
try {
// 決議JWS, Base64解碼, 并構造JWSObject
JWSObject jwsObject = new JWSObject(new Base64URL(jwsSplit[0]), new Base64URL(jwsSplit[1]), new Base64URL(jwsSplit[2]));
// 驗證JWS并設定驗證結果
boolean result = VerifySignatureUtil.verifySignature(jwsObject);
// 服務器側檢測結果驗證回應訊息體
JwsVerifyResp jwsVerifyResp = new JwsVerifyResp();
jwsVerifyResp.setResult(result);
} catch (ParseException | NoSuchAlgorithmException e) {
RUN_LOG.catching(e);
}
return jwsVerifyResp;
}
2) 這里使用VerifySignatureUtil工具類中的verifySignature方法完成相關資訊的驗證,包括JWS簽名演算法、證書鏈、簽名證書主機名、JWS簽名等,示例代碼:
public static boolean verifySignature(JWSObject jws) throws NoSuchAlgorithmException {
JWSAlgorithm jwsAlgorithm = jws.getHeader().getAlgorithm();
// 1. 驗證JWS簽名演算法
if ("RS256".equals(jwsAlgorithm.getName())) {
// 進行證書鏈校驗,并根據簽名演算法獲取 Signature 類實體,用來驗證簽名
return verify(Signature.getInstance("SHA256withRSA"), jws);
}
return false;
}
private static boolean verify(Signature signature, JWSObject jws) {
// 提取JWS頭部證書鏈資訊, 并轉換為合適的型別, 以便進行后續操作
X509Certificate[] certs = extractX509CertChain(jws);
// 2. 校驗證書鏈
try {
verifyCertChain(certs);
} catch (Exception e) {
return false;
}
// 3. 校驗簽名證書(葉子證書)域名資訊, 該域名固定為sysintegrity.platform.hicloud.com
try {
new DefaultHostnameVerifier().verify("sysintegrity.platform.hicloud.com", certs[0]);
} catch (SSLException e) {
return false;
}
// 4. 驗證JWS簽名資訊,使用簽名證書里的公鑰來驗證
PublicKey pubKey = certs[0].getPublicKey();
try {
// 使用簽名證書里的公鑰初始化 Signature 實體
signature.initVerify(pubKey);
// 從 JWS 提取簽名輸入,并輸入到 Signature 實體
signature.update(jws.getSigningInput());
// 使用Signature 實體來驗證簽名資訊
return signature.verify(jws.getSignature().decode());
} catch (InvalidKeyException | SignatureException e) {
return false;
}
}
3) 這里的extractX509CertChain方法,實作了從JWS Header中提取證書鏈的程序,詳細代碼如下:
private static X509Certificate[] extractX509CertChain(JWSObject jws) {
List<X509Certificate> certs = new ArrayList<>();
List<com.nimbusds.jose.util.Base64> x509CertChain = jws.getHeader().getX509CertChain();
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
certs.addAll(x509CertChain.stream().map(cert -> {
try {
return (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(cert.decode()) );
} catch (CertificateException e) {
RUN_LOG.error("X5c extract failed!");
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList()));
} catch (CertificateException e) {
RUN_LOG.error("X5c extract failed!");
}
return (X509Certificate[]) certs.toArray();
}
4) 這里的verifyCertChain方法,實作了證書鏈校驗的程序,具體實作如下:
private static void verifyCertChain(X509Certificate[] certs) throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
// 逐一驗證證書有效期及證書的簽發關系
for (int i = 0; i < certs.length - 1; ++i) {
certs[i].checkValidity();
PublicKey pubKey = certs[i + 1].getPublicKey();
certs[i].verify(pubKey);
}
// 使用預置的 HUAWEI CBG 根證書, 來驗證證書鏈中的最后一張證書
PublicKey caPubKey = huaweiCbgRootCaCert.getPublicKey();
certs[certs.length - 1].verify(caPubKey);
}
5) 華為根證書的加載是在VerifySignatureUtil工具類的靜態代碼段中實作的,示例代碼如下:
static {
// 加載預置的 HUAWEI CBG 根證書
File filepath = "~/certs/Huawei_cbg_root.cer";
try (FileInputStream in = new FileInputStream(filepath)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
huaweiCbgRootCaCert = (X509Certificate) cf.generateCertificate(in);
} catch (IOException | CertificateException e) {
RUN_LOG.error("HUAWEI CBG root cert load failed!");
}
}
至此,我們已經在App Server側完成了對檢測結果的驗證,驗證通過的結果將回傳給端側進行后續業務處理,
2.4 獲取系統完整性檢測結果
1) 在上一步驟完成后,App就可以從payload中獲取可信的系統完整性檢測結果,我們在前述的VerifyResultHandler類的回呼介面中,決議系統完整性檢測結果,示例代碼如下:
private static final class VerifyResultHandler implements ICallBack<Boolean> {
private final String jwsStr;
private final ICallBack<? super Boolean> callBack;
private VerifyResultHandler(String jwsStr, ICallBack<? super Boolean> callBack) {
this.jwsStr = jwsStr;
this.callBack = callBack;
}
@Override
public void onSuccess(Boolean verified) {
if (verified) {
// 服務器側驗證通過,提取系統完整性檢測結果
String payloadDetail = new String(Base64.decode(jwsStr.split("\\.")[1].getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE), StandardCharsets.UTF_8);
try {
final boolean basicIntegrity = new JSONObject(payloadDetail).getBoolean("basicIntegrity");
// 通過回呼回傳系統完整性檢測結果
callBack.onSuccess(basicIntegrity);
} catch (JSONException e) {
…
}
}
…
}
}
2) 具體的檢測報文的樣例如下:
{
"apkCertificateDigestSha256": [
"osaUtTsdAvezjQBaW3IhN3/fsc6NQ5KwKuAQXcfrxb4="
],
"apkDigestSha256": "vFcmE0uw5s+4tFjXF9rVycxk2xR1rXiZFHuuBFzTVy8=",
"apkPackageName": "com.example.mockthirdapp",
"basicIntegrity": false,
"detail": [
"root",
"unlocked"
],
"nonce": "UjJScmEyNGZWbTV4YTJNZw==",
"timestampMs": 1604048377137,
"advice": "RESTORE_TO_FACTORY_ROM"
}
3) 當檢測結果中basicIntegrity欄位為false時,表示存在風險,App就可以對用戶作風險提示,
結后語
官網開發指南,各位小伙伴們可以自行查閱參考,除了系統完整性檢測(SysIntegrity),還有其他4個功能的代碼,都是支持Java/Kotlin兩種開發語言:華為官網的示例代碼Java/Kotlin,下載后,根據提示說明進行操作就可運行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/258047.html
標籤:其他
