鸿蒙网络编程系列7-TLS安全数据传输单向认证示例
1.TLS简介
TLS(Transport Layer Security)协议的前身是SSL(Secure Socket Layer)安全套接层协议,由Netscape公司于1994年提出,是一套网络通信安全协议。IETF(The Internet Engineering Task Force)后期负责SSL协议,并且重新命名为TLS协议。IETF于1999年发布了TLS 1.0版本,该版本基于SSL 3.0;2006年4月,发布了TLS 1.1版本,2008年8月,发布了TLS 1.2版本,2018年3月,TLS 1.3版本发布,是目前最新的TLS版本。
2. TLS的常用方法
鸿蒙封装的TLS操作类位于模块socket中,使用如下的方式导入:
import socket from '@ohos.net.socket';
socket模块包括了众多的TCP操作方法,就本文而言,重点需要掌握的是如下五个:
1)constructTLSSocketInstance(): TLSSocket
创建并返回一个TLSSocket对象,,在使用TLSSocket的方法以前需要创建该对象。
2)bind(address: NetAddress): Promise<void>
绑定IP地址和端口,端口可以指定或由系统随机分配,可以使用0.0.0.0表示本机IP地址;使用Promise方式作为异步方法。
3)connect(options: TLSConnectOptions): Promise<void>
连接到指定的TLS服务端,参数options包含了连接的地址address、TLS安全配置secureOptions以及ALPN协议列表ALPNProtocols,其中address和secureOptions是必选的,ALPN是可选的,使用promise方法作为异步方法。
4)send(data: string): Promise<void>
通过TLSSocket连接向服务端发送消息data,使用Promise方式作为异步方法。
5)on(type: 'message', callback: Callback<{message: ArrayBuffer, remoteInfo: SocketRemoteInfo}>): void
订阅TLSSocket连接的接收消息事件,当套接字接收到消息时触发该事件,其中message表示接收到的消息,remoteInfo是发送方信息;使用callback方式作为异步方法。
3. TLS单向认证通讯示例
为演示TLS安全通讯单向认证的方式(即客户端认证服务端,客户端本身不提供证书),本示例实现了使用TLS协议发送、接收消息的功能,运行后的初始界面如下所示:

本示例中,可以配置TLS服务端的地址,可以直接输入服务端证书的CA信息,或者从文件加载,在配置好CA后,就可以连接服务端了,连接握手成功后,就可以发送信息给对方。
下面详细介绍创建该应用的步骤。
步骤1:创建Empty Ability项目。
步骤2:在module.json5配置文件加上对权限的声明:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_WIFI_INFO"
}
]
这里分别添加了访问互联网和访问WIFI信息的权限。
步骤3:在Index.ets文件里添加如下的代码:
import socket from '@ohos.net.socket';
import wifiManager from '@ohos.wifiManager';
import systemDateTime from '@ohos.systemDateTime';
import util from '@ohos.util';
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';//执行TLS通讯的对象
let tlsSocket = socket.constructTLSSocketInstance()//说明:本地的IP地址不是必须知道的,绑定时绑定到IP:0.0.0.0即可,显示本地IP地址的目的是方便对方发送信息过来
//本地IP的数值形式
let ipNum = wifiManager.getIpInfo().ipAddress
//本地IP的字符串形式
let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF);let caFileUri = ''@Entry
@Component
struct Index {//连接、通讯历史记录@State msgHistory: string = ''//要发送的信息@State sendMsg: string = ''//服务端IP地址@State serverIp: string = "0.0.0.0"//服务端端口@State serverPort: number = 9999//是否可以加载@State canLoad: boolean = false//是否可以连接@State canConnect: boolean = false//是否可以发送消息@State canSend: boolean = false@State ca: string = ``scroller: Scroller = new Scroller()build() {Row() {Column() {Text("TLS通讯示例").fontSize(14).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.Center).padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("本地IP地址:").width(100).fontSize(14).flexGrow(0)Text(localIp).width(110).fontSize(12).flexGrow(1)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("服务端地址:").fontSize(14).width(90).flexGrow(1)TextInput({ text: this.serverIp }).onChange((value) => {this.serverIp = value}).width(110).fontSize(12).flexGrow(4)Text(":").width(5).flexGrow(0)TextInput({ text: this.serverPort.toString() }).type(InputType.Number).onChange((value) => {this.serverPort = parseInt(value)}).fontSize(12).flexGrow(2).width(50)}.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).flexGrow(0)Button("加载").onClick(() => {this.loadCA()}).enabled(this.canLoad).width(70).fontSize(14).flexGrow(0)Button("连接").onClick(() => {this.connect2Server()}).enabled(this.canConnect).width(70).fontSize(14).flexGrow(0)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {TextArea({ text: this.ca }).onChange((value) => {this.ca = value}).flexGrow(1).height(200)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {TextInput({ placeholder: "输入要发送的消息" }).onChange((value) => {this.sendMsg = value}).width(200).flexGrow(1)Button("发送").enabled(this.canSend).width(70).fontSize(14).flexGrow(0).onClick(() => {this.sendMsg2Server()})}.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%')}//发送消息到服务端sendMsg2Server() {tlsSocket.send(this.sendMsg + "\r\n").then(async () => {this.msgHistory += "我:" + this.sendMsg + await getCurrentTimeString() + "\r\n"}).catch((e) => {this.msgHistory += '发送失败' + e.message + "\r\n";})}//绑定本地地址async bind2LocalAddress() {//本地地址let localAddress = { address: "0.0.0.0", family: 1 }await tlsSocket.bind(localAddress).then(() => {this.msgHistory = 'bind success' + "\r\n";}).catch((e) => {this.msgHistory = 'bind fail ' + e.message + "\r\n";})//收到消息时的处理tlsSocket.off("message")tlsSocket.on("message", async (value) => {let msg = buf2String(value.message)let time = await getCurrentTimeString()this.msgHistory += "服务端:" + msg + time + "\r\n"this.scroller.scrollEdge(Edge.Bottom)})}//选择CA证书文件selectCA() {let documentPicker = new picker.DocumentViewPicker();documentPicker.select().then((result) => {if (result.length > 0) {caFileUri = result[0]this.msgHistory += "select file: " + caFileUri + "\r\n";this.canLoad = true}}).catch((e) => {this.msgHistory += 'DocumentViewPicker.select failed ' + e.message + "\r\n";});}//加载CA文件内容loadCA() {try {let buf = new ArrayBuffer(1024 * 4);let file = fs.openSync(caFileUri, fs.OpenMode.READ_ONLY);let readLen = fs.readSync(file.fd, buf, { offset: 0 });this.ca = buf2String(buf.slice(0, readLen))this.canConnect = truefs.closeSync(file);}catch (e) {this.msgHistory += 'readText failed ' + e.message + "\r\n";}}//连接服务端connect2Server() {//绑定本地地址this.bind2LocalAddress()//服务端地址let serverAddress = { address: this.serverIp, port: this.serverPort, family: 1 }let opt: socket.TLSSecureOptions = {ca: [this.ca]}tlsSocket.connect({ address: serverAddress, secureOptions: opt }).then(() => {this.msgHistory = 'connect success ' + "\r\n";this.canSend = true}).catch((e) => {this.msgHistory = 'connect fail ' + e.message + "\r\n";})}
}//同步获取当前时间的字符串形式
async function getCurrentTimeString() {let time = ""await systemDateTime.getDate().then((date) => {time = date.getHours().toString() + ":" + date.getMinutes().toString()+ ":" + date.getSeconds().toString()})return "[" + time + "]"
}//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信息,单击“选择”按钮选择CA证书,如图所示:

步骤6:单击“加载”按钮加载CA证书,如图所示:

步骤7:配置服务端地址,当前,前提是要运行一个TLS服务端,具体的服务端可以使用别的语言编写,或者使用更高版本的鸿蒙API编写,本例假定使用一个基于TLS的回声服务器,配置好后,单击“连接”按钮即可开始连接服务端。连接成功后,如果在服务端监听,可以看到如下的连接过程信息:

步骤8:输入要发送的信息,单击“发送”按钮即可发送信息到服务端,如下图所示:

如果监听发送接收消息,可以看到Application Data的发送过程:

通过上面的过程可知,在经过成功的握手后,消息发送接收都是基于密文的,达到了保密的目的。
这样就完成了一个简单的TLS单向认证消息发送应用。
(本文作者原创,除非明确授权禁止转载)
本文码云源码地址: https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tls/TlsDemo
本系列码云源码地址:https://gitee.com/zl3624/harmonyos_network_samples
相关文章:
鸿蒙网络编程系列7-TLS安全数据传输单向认证示例
1.TLS简介 TLS(Transport Layer Security)协议的前身是SSL(Secure Socket Layer)安全套接层协议,由Netscape公司于1994年提出,是一套网络通信安全协议。IETF(The Internet Engineering Task Fo…...
LangGraph 源码分析 | BaseTool 模板类
文章目录 BaseTool 源码分析核心属性以 TavilySearchResults(BaseTool) 为例namedescriptionargs_schemaresponse_format查询选项属性 需要子类实现的抽象方法以 TavilySearchResults(BaseTool) 为例 核心方法arun():run()的异步执行版本invoke()和ainvoke() BaseTo…...
vulnhub靶场之JOY
一.环境搭建 1.靶场描述 Does penetration testing spark joy? If it does, this machine is for you. This machine is full of services, full of fun, but how many ways are there to align the stars? Perhaps, just like the child in all of us, we may find joy in …...
intel和AMD突然联姻,这操作给我看傻了
要说现在的显卡一哥,那肯定非 NVIDIA 莫属,不仅仅是在 AI 领域是赚的盆满钵满,更是在游戏显卡领域把红蓝两家打的节节败退。 在 6000 系列尚能与之一战的 AMD 也认清了现实,在最近宣布了下一代 8000 系列显卡放弃高端显卡战争&…...
yolo_face_pose-DataBall 人脸关键点数据集 >> DataBall
数据集下载地址:ultralyticsyolo训练自定义人脸关键点训练和验证数据集资源-CSDN文库 数据集定义: ultralytics yolo 训练自定义人脸关键点训练和验证数据集 数据集格式:yolo 训练集数量:3295 验证集数量:120 类别&a…...
Unity 山水树木
本章节内容 1. Unity对3D游戏物体的简单操作; 2. 构建山水树木的场景 1. Unity 简易操作 1.1 新建3D游戏场景 1. 打开Unity Hub,点击 New Project (新建项目)按键,选择第二项 3D(Built-In Render Pipeline)…...
Redis 性能优化选择:Pika 的配置与使用详解
引言 在我们日常开发中 redis是我们开发业务场景中不可缺少的部分。Redis 凭借其内存存储和快速响应的特点,广泛应用于缓存、消息队列等各种业务场景。然而,随着数据量的不断增长,单节点的 Redis 因为内存限制和并发能力的局限,逐…...
【某农业大学计算机网络实验报告】实验三 IP数据报发送和转发流程
实验目的: (1)掌握基本的网络配置方法。 (2)观察 IP 数据报的发送和转发流程,掌握 IP 转发分组的原理。 实验器材: 一台Windows操作系统的PC机。 实验准备: 1.配置…...
Android13 添加运行时权限
在一些场景下,需要给app 添加运行时权限,这样就不需要在使用的时候再去点击授权。 直接上代码: --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.javab/services/core/java/com/android/server/pm…...
官方操刀占用仅6G,Win 11 LTSC详细安装、优化教程来了
前段时间微软发布 Win 11 年度重磅更新 24H2,顺便也带来了备受期待的 Win 11 2024 官方精简 LTSC(老坛酸菜)版。 Win 11 重磅更新发布,老坛酸菜版成了配角! 简单来说,Win 11 LTSC 是微软针对企业用户推出…...
【论文精读】RELIEF: Reinforcement Learning Empowered Graph Feature Prompt Tuning
RELIEF: Reinforcement Learning Empowered Graph Feature Prompt Tuning 前言AbstractMotivationSolutionRELIEFIncorporating Feature Prompts as MDPAction SpaceState TransitionReward Function Policy Network ArchitectureDiscrete ActorContinuous ActorCritic Overall…...
2023-06 GESP C++三级试卷
2023-06 GESP C三级试卷 (满分:100 分 考试时间:90 分钟) PDF试卷及答案回复:GESPC2023063 一、单选题(每题 2 分,共 30 分) 1 高级语言编写的程序需要经过以下( )操…...
Maven--简略
简介 Apache旗下的一款开源项目,用来进行项目构建,帮助开发者管理项目中的jar及jar包之间的依赖,还拥有项目编译、测试、打包的功能。 管理方式 统一建立一个jar仓库,把jar上传至统一的仓库,使用时,配置…...
leetcode 刷题day44动态规划Part13( 647. 回文子串、516.最长回文子序列)
647. 回文子串 动规五部曲: 1、确定dp数组(dp table)以及下标的含义 按照之前做题的惯性,定义dp数组的时候很自然就会想题目求什么,就如何定义dp数组。但是对于本题来说,这样定义很难得到递推关系&#x…...
华为OD机试真题---关联子串
华为OD机试中的“关联子串”题目是一个考察字符串处理和算法理解的经典问题。以下是对该题目的详细解析: 一、题目描述 给定两个字符串str1 和 str2,如果字符串 str1 中的字符, 经过排列组合后的字符串中只要有一个是 str2 的子串ÿ…...
【OpenAI】第二节(Token)什么是Token?如何计算ChatGPT的Token?
深入解析:GPT如何计算Token数?让你轻松掌握自然语言处理的核心概念!🚀 在当今的人工智能领域,GPT(Generative Pre-trained Transformer)无疑是最受关注的技术之一。无论是在文本生成、对话系统…...
GraphRAG + Ollama + Groq 构建知识库 续篇 利用neo4j显示知识库
GraphRAG Ollama Groq 构建知识库 在上一篇文章中,我们详细介绍了如何创建一个知识库。尽管知识库已经建立,但其内容的可视化展示尚未实现。我们无法直接看到知识库中的数据,也就无法判断这些数据是否符合我们的预期。为了解决这个问题&…...
工业以太网之战:EtherCAT是如何杀出重围的?
前言 EtherCAT 是一种开放的实时工业以太网协议,由德国倍福公司开发并在 2003 年 4 月的汉诺威工业博览会上首次亮相,目前由 EtherCAT 技术协会(ETG)进行维护和推广。经过 21 年的不断发展,EtherCAT 显示出极强的生命…...
轻量级可视化数据分析报表,分组汇总表!
什么是可视化分组汇总表? 可视化分组汇总表,是一种结合了数据分组、聚合计算与视觉呈现功能的数据分析展示功能。它能够按照指定的维度(如时间、地区、产品类型等)对数据进行分组,还能自动计算各组的统计指标…...
初始Python篇(4)—— 元组、字典
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: Python 目录 元组 相关概念 元组的创建与删除 元组的遍历 元组生成式 字典 相关概念 字典的创建与删除 字典的遍历与访问 字典…...
3个真实场景告诉你,Avogadro 2分子建模软件如何改变化学研究方式
3个真实场景告诉你,Avogadro 2分子建模软件如何改变化学研究方式 【免费下载链接】avogadroapp Avogadro is an advanced molecular editor designed for cross-platform use in computational chemistry, molecular modeling, bioinformatics, materials science, …...
告别玄学调试:用示波器‘看透’开关电源的十大常见故障波形
告别玄学调试:用示波器‘看透’开关电源的十大常见故障波形 实验室里,工程师们常把开关电源调试戏称为"玄学"——参数微调、元件更换、反复试错,往往耗费数小时仍找不到问题根源。这种低效的调试方式即将成为历史。本文将彻底改变你…...
RK3588 LGA核心板:高性能嵌入式开发的模块化解决方案
1. 项目概述:当旗舰SoC遇见极致封装最近在嵌入式圈子里,一个“小而强”的组合引起了我的注意:瑞芯微的旗舰级SoC RK3588,被塞进了一个极其紧凑的LGA封装里,做成了名为SOM-3588-LGA的核心板,并且已经现货发售…...
【量化】IPTQ-ViT: Post-Training Quantization of Non-linear Functions for Integer-only Vision Transformer
【PTQ】PTQViT/IPTQ-ViT (arXiv 2022) 问题: ViT 中的非线性函数(GELU、Softmax)在纯整数推理中存在计算障碍。 核心创新: 模块方法作用多项式近似 GELU用低阶多项式逼近 GELU将非线性运算转化为整数可执行的乘加Bit-shifting Softmax用位移操作近似 …...
FreeRTOS+LwIP 2.2.0实战:tcpip_thread消息队列与定时器如何协同工作?
FreeRTOS与LwIP 2.2.0深度协同:消息队列与定时器的精妙舞步 在嵌入式网络开发中,实时操作系统与轻量级TCP/IP协议栈的协同工作一直是开发者关注的焦点。FreeRTOS作为嵌入式领域广泛使用的实时操作系统,与LwIP这一轻量级TCP/IP协议栈的组合&am…...
STM32F103C8T6驱动BMP280气压模块:从I2C地址纠错到数据转换的完整避坑指南
STM32F103C8T6驱动BMP280气压模块:从I2C地址纠错到数据转换的完整避坑指南 在嵌入式开发中,气压传感器BMP280因其高精度和低成本成为许多项目的首选。然而,当这个看似简单的模块遇上STM32F103C8T6这颗经典的MCU时,不少开发者却踩进…...
HC32L110(三) 从零构建:基于GCC与VSCode的轻量级ARM开发工作流
1. 为什么选择GCCVSCode开发HC32L110 第一次接触HC32L110这款MCU时,我像大多数嵌入式开发者一样,本能地打开了Keil和IAR这些传统IDE。但很快发现,这些"重量级选手"在资源受限的HC32L110开发中显得格外笨重——动辄几个GB的安装包、…...
编码效率翻倍实测:OpenClaw 联动 Claude Code 实现 3 类数字员工协同的 4 步配置
1. 效率翻倍不是幻觉:OpenClaw 联动 Claude Code 的真实瓶颈在哪? 我上线第三个用 OpenClaw + Claude Code 搭建的数字员工协同流水线时,把同一套接口自动化脚本重构任务交给两组人:一组纯人工,一组走 OpenClaw 管道。结果不是“快一点”,而是人工组平均耗时 47 分钟,A…...
Arm架构AMU性能监控原理与实践指南
1. Arm架构活动监视器(AMU)核心原理活动监视器(Activity Monitors Unit, AMU)是Armv8/v9架构中用于性能监控的关键硬件模块。作为处理器微架构的一部分,AMU通过专用硬件计数器实时采集CPU执行过程中的各类性能事件数据。与传统的性能监控单元(PMU)相比,A…...
别再只盯着p值和FC了!用DisGeNET给你的Hub Gene打分,提升下游验证成功率
别再只盯着p值和FC了!用DisGeNET给你的Hub Gene打分,提升下游验证成功率 在基因功能研究的海洋中,Hub Gene如同灯塔般指引着研究方向。然而,许多研究者仍被困在传统筛选方法的局限中——过度依赖差异表达基因的p值和fold change阈…...
