介绍

本篇Codelab基于网络模块以及Webview实现一次HTTPS请求,并对其过程进行抓包分析。效果如图所示:

相关概念

● Webview:提供Web控制能力,Web组件提供网页显示能力。

● HTTP数据请求:网络管理模块,提供HTTP数据请求能力,支持GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT请求方法。

● HTTPS:应用层协议,支持加密传输以及身份认证,保证数据的安全传输。

● SSL:SSL(Secure Socket Layer)安全套接层是位于传输通信协议(TCP/IP)之上实现的一种安全协议。

● TLS:TLS(Transport Layer Security)是一种安全协议,旨在实现数据加密传输。

完整示例

gitee源码地址

源码下载

HTTPS请求过程(ArkTS).zip

环境搭建

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。

软件要求

● DevEco Studio版本:DevEco Studio 3.1 Release。

● HarmonyOS SDK版本:API version 9。

硬件要求

● 设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。

● HarmonyOS系统:3.1.0 Developer Release。

环境搭建

1.  安装DevEco Studio,详情请参考下载和安装软件

2.  设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

● 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

● 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

3.  开发者可以参考以下链接,完成设备调试的相关配置:

● 使用真机进行调试

● 使用模拟器进行调试

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在源码下载或gitee中提供。

├──entry/src/main/ets                // 代码区
│ ├──common
│ │ ├──constants
│ │ │ ├──StyleConstants.ets // 样式常量类
│ │ │ └──CommonConstants.ets // 常量类
│ │ └──utils
│ │ ├──HttpUtil.ets // 网络请求方法
│ │ └──Logger.ets // 日志打印工具类
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口类
│ └──pages
│ └──WebPage.ets // 页面入口
└──entry/src/main/resources // 资源文件目录

  

创建HTTPS请求

HTTPS协议是位于应用层的一种安全传输协议,与HTTP最大的区别是服务端与客户端之间进行数据传输都会经过TLS/SSL加密。该示例请求HarmonyOS官网,并将请求得到的内容通过Web容器展示出来。效果如图所示:

首先在HttpUtil.ets中调用createHttp方法创建一个请求任务,再通过request方法发起网络请求。该方法支持三个参数:url、options以及callback回调,其中options可以设置请求方法、请求头以及超时时间等。

// HttpUtil.ets
import http from '@ohos.net.http';
export default async function httpGet(url: string) {
if (!url) {
return undefined;
}
let request = http.createHttp();
let options = {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
readTimeout: CommonConstant.READ_TIMEOUT,
connectTimeout: CommonConstant.CONNECT_TIMEOUT
} as http.HttpRequestOptions;
let result = await request.request(url, options);
return result;
}

  

接着在入口页面中调用上述封装的httpGet方法请求指定网址,将请求得到的内容嵌入到Web组件中。

// WebPage.ets
import http from '@ohos.net.http';
...
@Entry
@Component
struct WebPage {
@State webVisibility: Visibility = Visibility.Hidden;
...
build() {
Column() {
...
}
} async onRequest() {
if (this.webVisibility === Visibility.Hidden) {
this.webVisibility = Visibility.Visible;
try {
let result = await httpGet(this.webSrc);
if (result && result.responseCode === http.ResponseCode.OK) {
this.controller.clearHistory();
this.controller.loadUrl(this.webSrc);
}
} catch (error) {
promptAction.showToast({
message: $r('app.string.http_response_error')
})
}
} else {
this.webVisibility = Visibility.Hidden;
}
}
}

  

分析模块源码可知,通过request方法建立请求后,模块底层首先会调用三方库libcurl中的curl_easy_init初始化一个简单会话。初始化完成后,接着调用curl_easy_setopt方法设置传输选项。其中CURLOPT_URL用于设置请求的URL地址,对应request中的url参数;CURLOPT_WRITEFUNCTION可以设置一个回调,保存接收的数据;CURLOPT_HEADERDATA支持设置回调,在回调中保存响应头数据。

// http_exec.cpp
bool HttpExec::RequestWithoutCache(RequestContext *context)
{
if (!staticVariable_.initialized) {
NETSTACK_LOGE("curl not init");
return false;
}
auto handle = curl_easy_init();
...
if (!SetOption(handle, context, context->GetCurlHeaderList())) {
NETSTACK_LOGE("set option failed");
return false;
}
...
return true;
}
...
bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
{
const std::string &method = context->options.GetMethod();
if (!MethodForGet(method) && !MethodForPost(method)) {
NETSTACK_LOGE("method %{public}s not supported", method.c_str());
return false;
}
if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
}
// 设置请求URL
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
...
// 设置CURLOPT_WRITEFUNCTION传输选项,OnWritingMemoryBody为回调函数
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
// 在OnWritingMemoryHeader写入响应头数据
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
...
return true;
}
...
#define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext) \
do {
CURLcode result = curl_easy_setopt(handle, opt, data); \
if (result != CURLE_OK) { \
const char *err = curl_easy_strerror(result); \
NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
(asyncContext)->SetErrorCode(result); \
return false; \
}

  

传输选项设置成功后,调用curl_multi_perform执行传输请求,并通过curl_multi_info_read查询处理句柄是否有消息返回,最后进入HandleCurlData方法处理返回数据。

// http_exec.cpp
void HttpExec::SendRequest()
{
...
do {
...
auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
...
} while (runningHandle > 0);
}
...
void HttpExec::ReadResponse()
{
CURLMsg *msg = nullptr; /* NOLINT */
do {
...
msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
if (msg) {
if (msg->msg == CURLMSG_DONE) {
HandleCurlData(msg);
}
}
} while (msg);
}

  

在HandleCurlData函数中调用ParseHeaders函数将上面回调写入的响应头解析出来,其中响应头中会携带客户端和服务端支持的最高网络协议,如果是HTTP/2表示支持HTTPS加密传输。

// http_exec.cpp
bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
{
...
context->response.ParseHeaders();
return true;
}
// http_response.cpp
void HttpResponse::ParseHeaders()
{
std::vector<std::string> vec = CommonUtils::Split(rawHeader_, HttpConstant::HTTP_LINE_SEPARATOR);
for (const auto &header : vec) {
if (CommonUtils::Strip(header).empty()) {
continue;
}
auto index = header.find(HttpConstant::HTTP_HEADER_SEPARATOR);
if (index == std::string::npos) {
header_[CommonUtils::Strip(header)] = "";
NETSTACK_LOGI("HEAD: %{public}s", CommonUtils::Strip(header).c_str());
continue;
}
header_[CommonUtils::ToLower(CommonUtils::Strip(header.substr(0, index)))] =
CommonUtils::Strip(header.substr(index + 1));
}
}

  

将本篇Codelab中的网址协议头更改为http时,在DevEco Studio的日志中看到服务端会返回301状态码永久重定向到https,因此最终通信依旧会经历TLS加密传输。

模块源码可以在Gitee开源仓库communication_netstack中获取,本篇Codelab引用源码部分位于http_exec文件中。

TLS/SSL握手过程

本章节主要通过抓包数据分析TLS协议的握手过程,其中包括交换参数、证书验证、密钥计算以及验证密钥等,抓包内容如图所示:握手过程如图所示:

5.1 第一次握手

根据上图中可以看到,客户端首先会进行第一次握手连接,发送“Client Hello”消息给服务端开启一个新的会话连接。分析数据包得到,客户端在第一次握手时会向服务端传递协议版本号(TLS1.2)、随机数(Client Random,用于后续生成“会话密钥”)、Session ID以及Cipher Suites(客户端支持的密码套件)。数据内容如图所示:

5.2 第二次握手

服务端接收到客户端数据后,将响应数据通过“Sever Hello”传递给客户端,包括随机数(Sever Random,用于后续生成“会话密钥”)、协议版本号(TLS1.2)以及Cipher Suite(任意选择一个客户端支持的密码套件),数据内容如图所示:

服务端传递“Sever Hello”后,紧跟着会将Certificate(证书)、“Sever Key Exchange”消息以及“Server Hello Done”消息传递给客户端。此处着重分析“Sever Key Exchange”,数据内容如图所示:

5.3 第三次握手

客户端收到“Server Hello Done”消息后,会将Client Params数据传递给服务端,其中包含自身生成的椭圆曲线公钥(Pubkey),数据内容如图所示:经过上述过程,客户端持有Client Random、Server Random以及Server Params,将Server Params使用服务端公钥解密后得到“Server Key Exchange”消息中的临时公钥,客户端使用x25519算法计算出预主密钥(Premaster Secret),然后再结合客户端随机数、服务端随机数以及预主密钥生成主密钥,最终构建“会话密钥”。“Change Cipher Spec”消息表示客户端已经生成密钥,并切换到加密模式。最后将之前所有的握手数据做一个摘要,再利用双方协商好的对称密钥进行加密, 通过“Encrypted Handshake Message”消息将加密数据传递给服务端做校验。数据内容如图所示:

5.4 第四次握手

服务端利用Client Random、Server Random以及Client Params计算得出“会话密钥”,向客户端传递“Change Cipher Spec”和“Encrypted Handshake Message”消息供客户端校验。当双方校验通过后,真正的数据才开始传输。

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

1.  使用@ohos.net.http建立一次https请求。

2.  通过分析TLS/SSL握手过程中的传输数据包来理解数据安全传输。

基于HarmonyOS的HTTPS请求过程开发示例(ArkTS)的更多相关文章

  1. https请求过程

    我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取.所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议. HTTPS简介 HTTPS其实是有两部分组成:HTTP + SSL ...

  2. 解决https 请求过程中SSL问题

    最近一个项目中用到了https的请求,在实际调用过程中发现之前的http方法不支持https,调用一直报错. 查询了一下,添加几行代码解决问题. public string HttpPost(stri ...

  3. 从本质上学会基于HarmonyOS开发Hi3861(主要讲授方法)

    引言:花半秒钟就看透事物本质的人,和花一辈子都看不透事物本质的人,注定是截然不同的命运 做开发也一样,如果您能看透开发的整个过程,就不会出现"学会了某个RTOS的开发,同样的RTOS开发换一 ...

  4. https ssl 请求过程详解

    http  协议:http 协议是一种无状态,短链接的 通信协议,http 协议建立在 tcp 协议之上. http 协议 分成 三个 部分 请求行,请求头,请求体 请求行: 就是访问的地址 ( 包含 ...

  5. Python爬虫开发【第1篇】【HTTP与HTTPS请求与响应】

    一.HTTP.HTTPS介绍 HTTP协议(超文本传输协议):一种发布.接收HTML页面的方法 HTTPS协议:简单讲是HTTP安全版,在HTTP下加入SSL层 SSL(安全套接层),用于WEB的安全 ...

  6. iOS开发 支持https请求以及ssl证书配置(转)

    原文地址:http://blog.5ibc.net/p/100221.html 众所周知,苹果有言,从2017年开始,将屏蔽http的资源,强推https 楼主正好近日将http转为https,给还没 ...

  7. TCP面试题之HTTP和HTTPS的请求过程

    HTTP的请求过程: 1.TCP建立连接后,客户端会发送报文给服务端: 2.服务端接收报文并作出响应: 3.客户端收到响应后解析给用户: HTTPS的请求过程: 1.客户端发送请求到服务端: 2.服务 ...

  8. 测试开发HTTP请求过程(一)

    测试开发HTTP请求过程 HTTP请求过程: 首先要熟悉http请求过程: 1,服务端建立socket监听 2,客户端发送http请求 3,客户端与服务端建立socket连接 4,客户端------t ...

  9. Dojo初探之5:dojo的request(请求)操作、请求过程事件绑定和隐藏数据data()操作(基于dojo1.11.2版本)

    前言: 上一章详细阐述了dojo的事件绑定操作,本章将讲解dojo的请求操作 注:dojo的请求操作与js和jquery完全不同! 1.dojo的请求 dojo通过request.get()/.put ...

  10. (四)进行HTTPS请求并进行(或不进行)证书校验(示例)

    原文:https://blog.csdn.net/justry_deng/article/details/81042379 相关方法详情(非完美封装): /** * 根据是否是https请求,获取Ht ...

随机推荐

  1. 冲击900亿美元估值!邀约路演、秘密交表的Shein上市有望

    双十一的狂欢刚刚结束,Shein即将赴美上市的消息又在电商圈里投下一枚重磅炸弹. 继被媒体曝光其寻求900亿美金估值后,最新的消息称其已邀请投资人参与路演,且已秘密完成交表.这个神秘的中国独角兽,离敲 ...

  2. BigDecimal类处理高精度计算

    BigDecimal类处理高精度计算 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数,但 ...

  3. 【开源库推荐】#1 SpiderMan 可快速查看Android闪退崩溃日志

    原文:https://stars-one.site/2020/12/22/android-log-spiderman 开发Android的时候想必大家都遭受过这种经历: 用户手机上App闪退了,但是我 ...

  4. stream使用汇总

    整理了下java使用stream处理list的几个便捷的方法 准备数据 List<KnowledgeInfoTable> knowledgeInfoTables = knowledgeIn ...

  5. maven问题之Could not calculate build plan:

    问题描述: Could not calculate build plan: Failure to transfer org.apache.maven.plugins:maven-surefire-pl ...

  6. Linux 运维工程师面试真题-4-Linux 服务配置及管理

    Linux 运维工程师面试真题-4-Linux 服务配置及管理** 1.请写出 apache2.X 版本的两种工作模式,以及各自工作原理.如何查看 apache 当前所 支持的模块,并且查看是工作在哪 ...

  7. View之invalidate,requestLayout,postInvalidate

    目录介绍 01.invalidate,requestLayout,postInvalidate区别 02.invalidate深入分析 03.postInvalidate深入分析 04.request ...

  8. 记录--分享并解析一个倒计时组件(Vue)

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.前言 入职的第一个需求是跟着一位前端大佬一起完成的一个活动项目. 由于是一起开发,当然不会放过阅读大佬的代码的机会. 因为我的页面中需 ...

  9. Oracle 常用建库模板

    记录一下 create tablespace lxw_tablespace datafile '/oradata/orcl/lxw_data_01.ora' size 30G; --或者 create ...

  10. 基于proteus的4019的移位设计

    基于proteus的4019的移位设计 1.实验原理 4019是一个基于CMOS的数字集成芯片,具有数据选择和逻辑门或两种工作状态.这里利用数据选择的切换,实现数据的左移和右移操作.简而言之就是左移使 ...