前言

系统完整性检测,是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代码库:

  1. 在“buildscript > repositories”中配置HMS Core SDK的Maven仓地址。
  2. 在“allprojects > repositories”中配置HMS Core SDK的Maven仓地址。
  3. 如果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>() {@Overridepublic 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 RS256sysintegrityrequest.setAlg("RS256"); Task task = mClient.sysIntegrity(sysintegrityrequest);task.addOnSuccessListener(new OnSuccessListener<SysIntegrityResp>() {@Overridepublic 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解码, 并构造JWSObjectJWSObject 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.comtry {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;}@Overridepublic 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,下载后,根据提示说明进行操作就可运行。

>>访问华为开发者联盟官网,了解更多相关内容

>>获取开发指导文档

>>华为移动服务开源仓库地址:GitHub、Gitee

关注我们,第一时间了解华为移动服务最新技术资讯~

用免费开放的华为安全检测功能,评估App运行设备安全相关推荐

  1. GitHub免费开放所有核心功能;告别纸币-央行牵头数字货币

    行 业 要 闻 Industry   News ▲▲▲ 0 1 一次登录,终身免费:GitHub宣布向所有人免费开放所有核心功能 每个私有库合作人数不设限,所有核心功能向所有人免费开放,这是 GitH ...

  2. 测一测!中科视拓免费开放口罩人脸检测与识别技术

    全民抗疫形势下,口罩已成为复工复产的标配.对于人脸识别技术厂商而言,两个应用需求应运而生: 1.检测人脸是否佩戴口罩: 2.在戴口罩的情况下依旧能够实现高精度人脸识别. 疫情初期,中科视拓紧急研发口罩 ...

  3. 百度免费开放长语音识别功能

    近日,百度AI开放平台向开发者免费开放长语音识别功能,通过SDK调用服务,可将长语音转换为文字.据了解,新版本SDK解除了对时间的限制,开发者无需再将长语音切割成60秒以内的分段,进行调用,提升了转写 ...

  4. API 接口监控产品全新改版,免费开放全部功能

    作为 EOLINKER 研发管理体系的重要一环,EOLINKER 接口监控即 AMT 产品将在 3月4日 迎来全新变化,AMT 产品将正式命名为 EOLINKER-API Beacon --API-烽 ...

  5. 【数据堂开放数据】活体检测数据免费开放

    人证核验.人脸通行.刷脸支付.远程身份验证.手机刷脸解锁等任务研究者的福音来啦! 数据堂自有版权数据产品 60 人活体检测数据限时免费开放. 链接:https://www.datatang.com/w ...

  6. 【口罩人脸检测/识别】中科视拓免费开放口罩人脸检测与识别技术解读

    原文:中科视拓免费开放口罩人脸检测与识别技术 思路 通过掺入了20%以上戴口罩的人脸识别数据集训练专门戴口罩识别模型,然后调整整个识别的Pipeline为先检测是否戴口罩,如果判断为带口罩则使用戴口罩 ...

  7. 如何保障手机钱包账户安全?iCard App集成系统完整性检测功能,从登录环节防范风险

    iCard是保加利亚一款热门的手机钱包App,为30多个欧洲国家和地区提供银行卡管理.收付款.购物支付.国际转账.外币兑换等服务,已拥有超过12年行业经验和庞大的用户群体.因此,如何保障用户账户和资金 ...

  8. 阿里将投入1000亿元助力共同富裕;字节大战腾讯元宇宙;微软Outlook网页版将提供语气检测功能​ | EA周报...

    EA周报 2021年9月3日 每个星期7分钟,元宝带你喝一杯IT人的浓缩咖啡,了解天下事.掌握IT核心技术. 周报看点 1.阿里将投入1000亿元助力共同富裕 2.雷军:小米汽车正式注册,注册资金10 ...

  9. 银河水滴打响步态识别商用第一枪!3大产品1.35亿订单,工业视觉平台免费开放...

    李根 发自 凹非寺  量子位 报道 | 公众号 QbitAI 你知道么?每个人的走路的步态都是独一无二的. 之前,你可能在<碟中谍>中看过步态识别技术,被用于高级别机密的身份验证. 但从今 ...

最新文章

  1. 第三课 弹性盒模型知识点
  2. 可视化篇(四)——— python绘制双y轴、箱线图、概率分布三种图形及案例
  3. 工厂模式(简单工厂模式)快速理解
  4. [转]Delphi过程函数传递参数的几种方式
  5. ArcGIS JS API 4 —— GET https://static.arcgis.com/fonts/simsun-regular/37888-38143.pbf 404
  6. linux完全卸载mysql_mysql完全卸载教程(图文详细)
  7. vue主动刷新页面及列表数据删除后的刷新方法
  8. InDesign入门教程,如何链接图形?
  9. Linux内核调用SPI平台级驱动_实现OLED的显示功能
  10. 过滤器 拦截器 controller 页面 的执行顺序
  11. MySQL数据库选型
  12. oracle sql 正则表达式
  13. html毕业答辩ppt,毕业答辩ppt
  14. 白盒测试的测试用例设计方法
  15. 阿里云活动价格点击购买时价格上涨的解决办法
  16. 【机器学习】数据驱动方法在电网稳定分析应用浅谈
  17. 计算机系统软件与工具软件
  18. Iphone IPA软件制作教程
  19. UltraEdit专业文本十六进制编辑器IDM UltraEdit v24.10.0.24 中文注册版 免费下载
  20. 虚拟服务器至强,解析新至强:选对平台是虚拟化的关键

热门文章

  1. bootstrap之popover插件使用
  2. 排序算法(二)—— 选择法排序算法
  3. 使用 BasePage 来解决 GridView 执行 RenderControl 产生的错误
  4. 美国之行4-Nasa航天城
  5. 【Linux】RPM 数据库损坏之后的修复办法
  6. python 边缘检测
  7. 由select/epoll返回的非阻塞connect还会是EINPROGRESS状态吗?
  8. SouthernBiotech 艾美捷正常山羊血清说明书
  9. 留学生该如何准备快消行业秋招笔试?(宝洁、联合利华、达能、强生等
  10. 007 计算机系统知识——计算机可靠性计算