鸿蒙网络编程系列27-HTTPS服务端证书的四种校验方式示例
1. 服务端数字证书验证的问题
在鸿蒙客户端对服务端发起HTTPS请求时,如果使用HttpRequest的request发起请求,那么就存在服务端数字证书的验证问题,你只有两个选择,一个是使用系统的CA,一个是使用自己选定的CA,在上文鸿蒙网络编程系列26-HTTPS证书自选CA校验示例中,对此进行了介绍。但是,还有一些更常见的问题难以解决:
-
可不可以跳过对服务端数字证书的验证
-
可不可以自定义验证规则,比如,只验证数字证书的公玥,忽略有效期,就是说失效了也可以继续用
如果你还是使用HttpRequest的话,答案是否定的。但是,鸿蒙开发者很贴心的推出了远场通信服务,可以使用rcp模块的方法发起请求,并且在请求时指定服务端证书的验证方式,关键点就在SecurityConfiguration接口上,该接口的remoteValidation属性支持远程服务器证书的四种验证模式:
-
'system':使用系统CA,默认值
-
'skip':跳过验证
-
CertificateAuthority:选定CA
-
ValidationCallback:自定义证书校验
Talk is cheap, show you the code!
2. 实现HTTPS服务端证书四种校验方式示例
本示例运行后的界面如下所示:
选择证书验证模式,在请求地址输入要访问的https网址,然后单击“请求”按钮,就可以在下面的日志区域显示请求结果。
下面详细介绍创建该应用的步骤。
步骤1:创建Empty Ability项目。
步骤2:在module.json5配置文件加上对权限的声明:
"requestPermissions": [{"name": "ohos.permission.INTERNET"}]这里添加了获取互联网信息的权限。
步骤3:在Index.ets文件里添加如下的代码:
import util from '@ohos.util';
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import { BusinessError } from '@kit.BasicServicesKit';
import { rcp } from '@kit.RemoteCommunicationKit';
@Entry
@Component
struct Index {//连接、通讯历史记录@State msgHistory: string = ''//请求的HTTPS地址@State httpsUrl: string = "https://47.**.**.***:8081/hello"//服务端证书验证模式,默认系统CA@State certVerifyType: number = 0//是否显示选择CA的组件@State selectCaShow: Visibility = Visibility.None//选择的ca文件@State caFileUri: string = ''scroller: Scroller = new Scroller()
build() {Row() {Column() {Text("远场通讯HTTPS证书校验示例").fontSize(14).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.Center).padding(10)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("选择服务器HTTPS证书的验证模式:").fontSize(14).width(90).flexGrow(1)}.width('100%').padding(10)
Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {Column() {Text('系统CA').fontSize(14)Radio({ value: '0', group: 'rgVerify' }).checked(true).height(50).width(50).onChange((isChecked: boolean) => {if (isChecked) {this.certVerifyType = 0}})}
Column() {Text('指定CA').fontSize(14)Radio({ value: '1', group: 'rgVerify' }).checked(false).height(50).width(50).onChange((isChecked: boolean) => {if (isChecked) {this.certVerifyType = 1}})}
Column() {Text('跳过验证').fontSize(14)Radio({ value: '2', group: 'rgVerify' }).checked(false).height(50).width(50).onChange((isChecked: boolean) => {if (isChecked) {this.certVerifyType = 2}})}
Column() {Text('自定义验证').fontSize(14)Radio({ value: '3', group: 'rgVerify' }).checked(false).height(50).width(50).onChange((isChecked: boolean) => {if (isChecked) {this.certVerifyType = 3}})}}.width('100%').padding(10)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("服务端证书CA").fontSize(14).width(90).flexGrow(1)
Button("选择").onClick(() => {this.selectCA()}).width(70).fontSize(14)}.width('100%').padding(10).visibility(this.certVerifyType == 1 ? Visibility.Visible : Visibility.None)
Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("请求地址:").fontSize(14).width(80)TextInput({ text: this.httpsUrl }).onChange((value) => {this.httpsUrl = value}).width(110).fontSize(12).flexGrow(1)Button("请求").onClick(() => {this.doHttpRequest()}).width(60).fontSize(14)}.width('100%').padding(10)
Scroll(this.scroller) {Text(this.msgHistory).textAlign(TextAlign.Start).padding(10).width('100%').backgroundColor(0xeeeeee)}.align(Alignment.Top).backgroundColor(0xeeeeee).height(300).flexGrow(1).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20)}.width('100%').justifyContent(FlexAlign.Start).height('100%')}.height('100%')}
//自定义证书验证方式selfDefServerCertValidation: rcp.ValidationCallback = (context: rcp.ValidationContext) => {//此处编写证书有效性判断逻辑return true;}
//生成rcp配置信息buildRcpCfg() {let caCert: rcp.CertificateAuthority = {content: this.getCAContent()}//服务器端证书验证模式let certVerify: 'system' | 'skip' | rcp.CertificateAuthority | rcp.ValidationCallback = "system"
if (this.certVerifyType == 0) { //系统验证certVerify = 'system'} else if (this.certVerifyType == 1) { //选择CA证书验证certVerify =caCert} else if (this.certVerifyType == 2) { //跳过验证certVerify = 'skip'} else if (this.certVerifyType == 3) { //自定义证书验证certVerify = this.selfDefServerCertValidation}let secCfg: rcp.SecurityConfiguration = { remoteValidation: certVerify }let reqCfg: rcp.Configuration = { security: secCfg }let sessionCfg: rcp.SessionConfiguration = { requestConfiguration: reqCfg }return sessionCfg}
//发起http请求doHttpRequest() {let rcpCfg = this.buildRcpCfg()let rcpSession: rcp.Session = rcp.createSession(rcpCfg)rcpSession.get(this.httpsUrl).then((response) => {if (response.body != undefined) {let result = buf2String(response.body)this.msgHistory += '请求响应信息: ' + result + "\r\n";}}).catch((err: BusinessError) => {this.msgHistory += `err: err code is ${err.code}, err message is ${JSON.stringify(err)}\r\n`;})}
//选择CA证书文件selectCA() {let documentPicker = new picker.DocumentViewPicker();documentPicker.select().then((result) => {if (result.length > 0) {this.caFileUri = result[0]this.msgHistory += "select file: " + this.caFileUri + "\r\n";}}).catch((e: BusinessError) => {this.msgHistory += 'DocumentViewPicker.select failed ' + e.message + "\r\n";});}
//加载CA文件内容getCAContent(): string {let caContent = ""try {let buf = new ArrayBuffer(1024 * 4);let file = fs.openSync(this.caFileUri, fs.OpenMode.READ_ONLY);let readLen = fs.readSync(file.fd, buf, { offset: 0 });caContent = buf2String(buf.slice(0, readLen))fs.closeSync(file);} catch (e) {this.msgHistory += 'readText failed ' + e.message + "\r\n";}return caContent}
}
//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {let msgArray = new Uint8Array(buf);let textDecoder = util.TextDecoder.create("utf-8");return textDecoder.decodeWithStream(msgArray)
}
步骤4:编译运行,可以使用模拟器或者真机。
步骤5:选择默认“系统CA”,输入请求网址(假设web服务端使用的是自签名证书),然后单击“请求”按钮,这时候会出现关于数字证书的错误信息,如图所示:
步骤6:选择“指定CA”类型,然后单击出现的“选择”按钮,可以在本机选择CA证书文件,然后单击“请求”按钮:
可以看到,得到了正确的请求结果。
步骤7:选择“跳过验证”类型,然后然后单击“请求”按钮:
也得到了正确的请求结果。
步骤8:选择“自定义验证”类型,然后然后单击“请求”按钮:
也得到了正确的请求结果。
3. 关键功能分析
关键点主要有两块,第一块是设置验证模式:
//服务器端证书验证模式let certVerify: 'system' | 'skip' | rcp.CertificateAuthority | rcp.ValidationCallback = "system" if (this.certVerifyType == 0) { //系统验证certVerify = 'system'} else if (this.certVerifyType == 1) { //选择CA证书验证certVerify =caCert} else if (this.certVerifyType == 2) { //跳过验证certVerify = 'skip'} else if (this.certVerifyType == 3) { //自定义证书验证certVerify = this.selfDefServerCertValidation}let secCfg: rcp.SecurityConfiguration = { remoteValidation: certVerify }let reqCfg: rcp.Configuration = { security: secCfg }let sessionCfg: rcp.SessionConfiguration = { requestConfiguration: reqCfg }return sessionCfg}
这个比较好理解,第二块是自定义证书验证的方法:
//自定义证书验证方式selfDefServerCertValidation: rcp.ValidationCallback = (context: rcp.ValidationContext) => {//此处编写证书有效性判断逻辑return true;}
这里为简单起见,自定义规则是所有的验证都通过,读者可以根据自己的需要来修改,比如不验证证书的有效期。
(本文作者原创,除非明确授权禁止转载)
本文源码地址:
https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/rcp/RCPCertVerify
本系列源码地址:
https://gitee.com/zl3624/harmonyos_network_samples
相关文章:

鸿蒙网络编程系列27-HTTPS服务端证书的四种校验方式示例
1. 服务端数字证书验证的问题 在鸿蒙客户端对服务端发起HTTPS请求时,如果使用HttpRequest的request发起请求,那么就存在服务端数字证书的验证问题,你只有两个选择,一个是使用系统的CA,一个是使用自己选定的CA…...
scala继承
Scala中继承的定义为在原有类的基础上定义一个新类,原有类称为父类,新类称为子类。 当子类从父类中继承的方法不能满足需要时,子类需要有自己的行为,怎么办? 此时使用override可以重写父类方法。 class Aniaml(){va…...

【Hive】2-Apache Hive概述、架构、组件、数据模型
Apache Hive概述 什么是Hive Apache Hive是一款建立在Hladoop之上的开源数据仓库系统,可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表,基于表提供了一种类似SQL的查询模型,称为Hive查询语言(HQL),用于访…...
关于目前面试八股文的一些心得体会
现在是2024年10月,自22年开始,明显感觉到整个计算机行业,越来越卷了。一方面,随着信息的传播,越来越多的新人涌入了这个赛道,另一方面,众所周知的原因,不管大厂还是小厂在经历寒冬之…...

大数据-178 Elasticsearch Query - Java API 索引操作 文档操作
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...

PHP(一)从入门到放弃
参考文献:https://www.php.net/manual/zh/introduction.php PHP 是什么? PHP(“PHP: Hypertext Preprocessor”,超文本预处理器的字母缩写)是一种被广泛应用的开放源代码的多用途脚本语言,它可嵌入到 HTML…...
基于深度学习的生物启发的学习系统
基于深度学习的生物启发学习系统(Biologically Inspired Learning Systems)旨在借鉴生物大脑的结构和学习机制,设计出更高效、更灵活的人工智能系统。这类系统融合了生物神经科学的研究成果,通过模仿大脑中的学习模式、记忆过程和…...

10_实现readonly
在某些时候,我们希望定义一些数据是只读的,不允许被修改,从而实现对数据的保护,即为 readonly 只读本质上也是对数据对象的代理,我们同样可以基于之前实现的 createReactiveObject 函数来实现,可以为此函数…...

简单介绍$listeners
$listeners 它可以获取父组件传递过来的所有自定义函数,如下: // 父组件 <template><div class"a"><Child abab"handleAbab" acac"handleAcac"/></div> </template><script> impor…...
架构设计笔记-20-补充知识
知识产权 我国没有专门针对知识产权制定统一的法律(知识产权法),而是在民法通则规定的原则下,根据知识产权的不同类型制定了不同的单项法律及法规,如著作权法、商标法、专利法、计算机软件保护条例等,这些法律、法规共同构成了我…...

scrapy 爬虫学习之【中医药材】爬虫
本项目纯学习使用。 1 scrapy 代码 爬取逻辑非常简单,根据url来处理翻页,然后获取到详情页面的链接,再去爬取详情页面的内容即可,最终数据落地到excel中。 经测试,总计获取 11299条中医药材数据。 import pandas as…...
PDH稳频技术粗谈
PDH(Plesiochronous Digital Hierarchy)是一种传输技术,主要用于数字通信中的传输系统。PDH稳频技术是指在PDH传输系统中,通过稳定频率来实现传输系统的稳定性和可靠性。 PDH传输系统中,时钟同步是非常重要的。传输系…...

[LeetCode] 130. 被围绕的区域
题目描述: 给你一个 m x n 的矩阵 board ,由若干字符 X 和 O 组成,捕获 所有 被围绕的区域: 连接:一个单元格与水平或垂直方向上相邻的单元格连接。区域:连接所有 O 的单元格来形成一个区域。围绕&#x…...
C语言位运算
目录 1.C语言位运算符表 2.C语言移位运算符详解(配实例作业) 3.C语言&按位与运算符详解 4.C语言|按位或运算符详解 5.C语言^按位异或运算符详解 6.C语言~取反运算符详解 C语言位运算这一章主要介绍C语言位运算符表、C语言移位运算符、C语言&按…...
Go 语言中格式化动词
当然,我很乐意为你提供 Go 语言中所有的格式化动词的完整列表。Go 语言的格式化动词非常丰富,可以满足各种打印和格式化需求。以下是完整的列表: 通用: %v - 以默认格式打印值 %v - 类似 %v,但对结构体会添加字段名 %#…...

CSS3 动画相关属性实例大全(四)(font、height、left、letter-spacing、line-height 属性)
CSS3 动画相关属性实例大全(四) (font、height、left、letter-spacing、line-height 属性) 本文目录: 一、font 属性(所有字体属性) 1.1、font-size属性(指定字体的大小) 1.2、f…...

大模型涌现判定
什么是大模型? 大模型:是“规模足够大,训练足够充分,出现了涌现”的深度学习系统; 大模型技术的革命性:延申了人的器官的功能,带来了生产效率量级提升,展现了AGI的可行路径&#x…...
LeetCode 1456.定长子串中元音的最大数目
题目: 给你字符串 s 和整数 k 。 请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。 英文中的 元音字母 为(a, e, i, o, u)。 思路:定长滑动窗口 入 更新 出 代码: class Solution {pub…...
freeswitch-esl 三方设备实现监听功能
使用场景: A和B在通话中,C想监听A和B通话内容 方法一: 修改拨号计划<extension name="global" continue="true"><condition><action application="info"/>...

【LeetCode】123.买卖股票的最佳时间
清晰明了的思路是解决问题的至上法宝。如何把一个复杂的问题拆成简单的问题,就是我们需要考虑的。 1. 题目 2. 思想 这道题虽然是难题,但是思想比较简单。 题目要求说至多买卖两次,也就是说,也可以买卖一次,这种情况…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...