鸿蒙网络编程系列58-仓颉版TLS数字证书查看及验签示例

1. TLS数字证书验签简介

数字证书的签名验证是网络编程中一个重要的功能,它保证了数字证书是由可信任的签发方签署的,在此基础上,我们才可以信任该证书,进而信任基于该证书建立的安全通道,所以说,数字证书的真实性是通讯安全的基石,了解数字证书验签的原理和方法,有助于我们建立安全的通讯。

一般来说,用户数字证书的来源是这样的:

  • 首先,由受信任的根证书颁发机构生成根证书,这是数字证书信任链的起源;
  • 其次,根证书颁发机构使用根证书签发中间证书,因为根证书的安全级别非常高,使用程序非常繁琐,轻易不使用,所以一般使用中间证书做为签发证书;
  • 最后,使用中间证书签发用户数字证书。

本文将通过一个示例演示数字证书内容的查看方法以及如何对一个数字证书进行验签,本示例将使用仓颉语言在API17的环境下编写,下面是详细的演示过程。

2. TLS数字证书查看及验签演示

要进行数字证书的验签,需要提前准备根证书、中间证书和用户证书,为方便起见,这里使用百度的数字证书及其签发证书,获取证书的步骤如下:

  • 首先,打开百度网站,单击地址栏前的图标,会弹出下拉菜单,如图所示:

  • 然后,单击“连接安全”菜单项,弹出安全菜单,如图所示:

  • 接着,单击“证书有效”菜单项,弹出证书信息,进入详细信息页面,如图所示:

  • 在证书层次结构那里选择 baidu.com,然后单击下面的“导出”按钮,即可导出百度的用户证书。然后在证书层次结构那里选择“GlobalSign RSA OV SSL CA 2018”,单击下面的“导出”按钮,即可导出中间证书,如图所示。依次也可以导出根证书。这些证书需要预先上传到手机上。

本应用打开的初始界面如图所示:


单击根证书后的“选择”按钮,弹出文件选择窗口,如图所示:

从中选择对应的根证书,返回界面后单击“查看”按钮,效果如图所示:

然后可以选择中间证书和用户证书,如图所示

此时单击“验签”按钮,可以查看验签结果,如图所示:

如果把用户证书更换成其他的证书,然后再进行验签,会发现验签不通过,如图所示:

3. TLS数字证书查看及验签示例编写

下面详细介绍创建该示例的步骤(确保DevEco Studio已安装仓颉插件)。

步骤1:创建[Cangjie]Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

"requestPermissions": [{"name": "ohos.permission.INTERNET"}]

这里添加了访问互联网的权限。

步骤3:在build-profile.json5配置文件加上仓颉编译架构:

"cangjieOptions": {"path": "./src/main/cangjie/cjpm.toml","abiFilters": ["arm64-v8a", "x86_64"]}

步骤4:在main_ability.cj文件里添加如下的代码:

package ohos_app_cangjie_entryinternal import ohos.base.AppLog
internal import ohos.ability.AbilityStage
internal import ohos.ability.LaunchReason
internal import cj_res_entry.app
import ohos.ability.*//Ability全局上下文
var globalAbilityContext: Option<AbilityContext> = Option<AbilityContext>.None
class MainAbility <: Ability {public init() {super()registerSelf()}public override func onCreate(want: Want, launchParam: LaunchParam): Unit {AppLog.info("MainAbility OnCreated.${want.abilityName}")globalAbilityContext = Option<AbilityContext>.Some(this.context)match (launchParam.launchReason) {case LaunchReason.START_ABILITY => AppLog.info("START_ABILITY")case _ => ()}}public override func onWindowStageCreate(windowStage: WindowStage): Unit {AppLog.info("MainAbility onWindowStageCreate.")windowStage.loadContent("EntryView")}
}

步骤5:在index.cj文件里添加如下的代码:

package ohos_app_cangjie_entryimport ohos.base.*
import ohos.component.*
import ohos.state_manage.*
import ohos.state_macro_manage.*
import ohos.file_picker.*
import ohos.ability.*
import ohos.file_fs.*
import crypto.x509.*
import ohos.crypto.*
import encoding.base64.toBase64String@Observed
//证书选择状态
class CertFileSelectStatus {@Publishpublic var certFileSelected: Bool = false@Publishpublic var certFileUri: String = ""
}@Entry
@Component
class EntryView {@Statevar title: String = '数字证书验签示例';//连接、通讯历史记录@Statevar msgHistory: String = ''//根证书选择状态@Statevar rootCertStatus: CertFileSelectStatus = CertFileSelectStatus()//中间证书选择状态@Statevar middleCertStatus: CertFileSelectStatus = CertFileSelectStatus()//用户证书选择状态@Statevar userCertStatus: CertFileSelectStatus = CertFileSelectStatus()let scroller: Scroller = Scroller()func build() {Row {Column {Text(title).fontSize(14).fontWeight(FontWeight.Bold).width(100.percent).textAlign(TextAlign.Center).padding(10)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("根证书:").fontSize(14).width(90).flexGrow(1)Button("选择").onClick {evt => selectCertFile(this.rootCertStatus)}.width(60).fontSize(14)Button("查看").onClick {evt =>let cert = getCert(rootCertStatus.certFileUri)showCertInfo(cert)}.width(60).fontSize(14).enabled(rootCertStatus.certFileSelected)}.width(100.percent).padding(5)Text(rootCertStatus.certFileUri).fontSize(14).width(100.percent).padding(10)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("中间证书:").fontSize(14).width(90).flexGrow(1)Button("选择").onClick {evt => selectCertFile(this.middleCertStatus)}.width(60).fontSize(14)Button("查看").onClick {evt =>let cert = getCert(middleCertStatus.certFileUri)showCertInfo(cert)}.width(60).fontSize(14).enabled(middleCertStatus.certFileSelected)}.width(100.percent).padding(5)Text(middleCertStatus.certFileUri).fontSize(14).width(100.percent).padding(10)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("用户证书:").fontSize(14).width(90).flexGrow(1)Button("选择").onClick {evt => selectCertFile(this.userCertStatus)}.width(60).fontSize(14)Button("查看").onClick {let cert = getCert(userCertStatus.certFileUri)showCertInfo(cert)}.width(60).fontSize(14).enabled(userCertStatus.certFileSelected)Button("验签").onClick {evt => verifyCert()}.width(60).fontSize(14).enabled(rootCertStatus.certFileSelected && userCertStatus.certFileSelected && middleCertStatus.certFileSelected)}.width(100.percent).padding(5)Text(userCertStatus.certFileUri).fontSize(14).width(100.percent).padding(10)Scroll(scroller) {Text(msgHistory).textAlign(TextAlign.Start).padding(10).width(100.percent).backgroundColor(0xeeeeee)}.align(Alignment.Top).backgroundColor(0xeeeeee).height(300).flexGrow(1).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20)}.width(100.percent).height(100.percent)}.height(100.percent)}//选择证书文件func selectCertFile(certFileStatus: CertFileSelectStatus) {let picker = DocumentViewPicker(getContext())let documentSelectCallback = {errorCode: Option<AsyncError>, data: Option<Array<String>> => match (errorCode) {case Some(e) => msgHistory += "选择失败,错误码:${e.code}\r\n"case _ => match (data) {case Some(value) =>certFileStatus.certFileUri = value[0]certFileStatus.certFileSelected = truecase _ => ()}}}picker.select(documentSelectCallback, option: DocumentSelectOptions(selectMode: DocumentSelectMode.MIXED))}//使用签发证书验证用户证书func verifyCert() {try {let caCert: X509Certificate = getCert(rootCertStatus.certFileUri)let middleCert: X509Certificate = getCert(middleCertStatus.certFileUri)let userCert: X509Certificate = getCert(userCertStatus.certFileUri)var verifyOpt: VerifyOption = VerifyOption()verifyOpt.roots = [caCert]verifyOpt.intermediates = [middleCert]let result = userCert.verify(verifyOpt)if (result) {msgHistory += "证书验签通过\r\n"} else {msgHistory += "证书验签未通过\r\n"}} catch (err: Exception) {msgHistory += "验签异常:${err.message}\r\n"}}//获取数字证书func getCert(certPath: String) {let fileName = getFileNameFromPath(certPath)let file = FileFs.open(certPath)//构造证书在沙箱cache文件夹的路径let realUrl = getContext().filesDir.replace("files", "cache") + "/" + fileName//复制证书到沙箱给定路径FileFs.copyFile(file.fd, realUrl)//关闭文件FileFs.close(file)//从沙箱读取证书文件信息let certContent = FileFs.readText(realUrl)return X509Certificate.decodeFromPem(certContent)[0]}//输出证书信息func showCertInfo(cert: X509Certificate) {try {this.msgHistory += "颁发者可分辨名称:${ cert.issuer}\r\n"this.msgHistory += "证书主题可分辨名称:${ cert.subject}\r\n"this.msgHistory += "证书主题CN名称:${ cert.subject.commonName.getOrThrow()}\r\n"this.msgHistory += "证书有效期:${ cert.notBefore} 至${ cert.notAfter}\r\n"this.msgHistory += "证书签名算法:${ cert.signatureAlgorithm}\r\n"let keyHash = getPubKeyHash(cert)this.msgHistory += "公钥摘要:${ keyHash}\r\n"} catch (err: Exception) {msgHistory += "出现异常:${err.message}\r\n"}}//获取证书的公钥摘要func getPubKeyHash(cert: X509Certificate) {let mdSHA256 = createMd("SHA256")mdSHA256.update(DataBlob(cert.publicKey.encodeToDer().body));//公钥摘要计算结果return toBase64String(mdSHA256.digest().data)}//从文件路径获取文件名称public func getFileNameFromPath(filePath: String) {let segments = filePath.split('/')//文件名称return segments[segments.size - 1]}//获取Ability上下文func getContext(): AbilityContext {match (globalAbilityContext) {case Some(context) => contextcase _ => throw Exception("获取全局Ability上下文异常")}}
}

步骤6:编译运行,可以使用模拟器或者真机。

步骤7:按照本文第2部分“TLS数字证书查看及验签演示”操作即可。

4. 代码分析

本示例中,读取数字证书内容的时候也存在权限的问题,所以也要把选择的数字证书复制到沙箱中,然后从沙箱中读取文件内容,该部分代码在getCert函数中。另外,获取Ability上下文的方式也要注意,首先在main_ability.cj中定义了全局上下文对象globalAbilityContext,然后在onCreate事件中对其赋值,这样在index.cj中就可以通过函数getContext获取该对象了。

(本文作者原创,除非明确授权禁止转载)

本文源码地址:
https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tls/CertVerify4Cj

本系列源码地址:
https://gitee.com/zl3624/harmonyos_network_samples

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915407.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915407.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【React Native】安装配置 Expo Router

过去开发React Native&#xff0c;所使用的路由都是React Navigation。但是这个东西使用起来非常困难&#xff0c;配置无比繁琐。Expo&#xff0c;为了简化操作&#xff0c;就基于React Navigation开发了Expo Router。 Expo Router用起来就要简单的多了&#xff0c;配置也相对…

美国VPS服务器Linux内核参数调优的实践与验证

美国vps服务器Linux内核参数调优的实践与验证在云计算和虚拟化技术日益普及的今天&#xff0c;美国VPS服务器因其稳定的网络环境和优越的性价比&#xff0c;成为众多企业和开发者的首选。Linux内核参数的默认配置往往无法充分发挥VPS的性能潜力。本文将深入探讨美国VPS服务器上…

在Vscode中使用Kimi K2模型:实践指南,三分钟生成个小游戏

Kimi K2是一款基于多专家&#xff08;MoE&#xff09;架构的强大代码与代理能力基础模型。本文将通过在VS Code及其扩展Cline和RooCode中的实际应用&#xff0c;详细说明如何使用Kimi K2-0711-preview模型。不得不说kimi这次的K2模型就是强大&#xff0c;在vscode中配置使用体验…

基于SpringBoot+Uniapp球场预约小程序(腾讯地图API、Echarts图形化分析、二维码识别)

“ &#x1f388;系统亮点&#xff1a;腾讯地图API、Echarts图形化分析、二维码识别”01系统开发工具与环境搭建前后端分离架构 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17前端&#xff1a; 技术&#xff1a;框架Vue.js&#xff1b;UI库&#xff1a;…

windows + phpstorm 2024 + phpstudy 8 + php7.3 + thinkphp6 配置xdebug调试

windows phpstorm 2024 phpstudy 8 php7.3 thinkphp6 配置xdebug调试 下载配置phpstudyPhp.ini配置phpstorm配置xdebug运行一会就停了配置虚拟机 0localhost_90.conf 配置php.ini配置下载 在下面地址下载合适的xdebug 放到对应的php https://xdebug.org/wizard 配置phpst…

python的pywebview库结合Flask和waitress开发桌面应用程序简介

pywebview的用途与特点 用途 pywebview是一个轻量级Python库&#xff0c;用于创建桌面应用程序&#xff08;GUI&#xff09;。它通过嵌入Web浏览器组件&#xff08;如Windows的Edge/IE、macOS的WebKit、Linux的GTK WebKit&#xff09;&#xff0c;允许开发者使用HTML/CSS/Java…

C#通过HslCommunication连接西门子PLC1200,并防止数据跳动的通用方法

textEdit30.Text ReadValue<int>(() > plc.ReadInt32("DB57.DBD16"), ref _last_num).ToString();// 通用读取方法&#xff08;支持所有值类型&#xff09;private T ReadValue<T>(Func<OperateResult<T>> readFunc, ref T lastValue) w…

Linux切换到Jenkins用户解决Jenkins Host key verification failed

以root或sudo user身份, 切换到jenkins用户 su -s /bin/bash jenkins前往jenkins的home目录 cd /var/lib/jenkins/查看.ssh下是否已经有known_hosts, 有的话, 是什么内容, 正常情况下, 这时候是没有对应IP记录的 cd .ssh/ more known_hosts访问一下对应IP, 记录公钥 ssh 192.16…

7.17 Java基础 | 集合框架(下)

接上文&#xff1a; 7.16 Java基础 | 集合框架&#xff08;上&#xff09;-CSDN博客 【1】Map集合 Map 集合是一种能存储键值对的数据结构。它的主要功能是依据键&#xff08;Key&#xff09;来快速查找对应的值&#xff08;Value&#xff09; 1、声明 Map<Integer,Integer…

【LeetCode刷题指南】--反转链表,链表的中间结点,合并两个有序链表

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

ubuntu上面的wps2019格式很乱在复制粘贴的时候

问题&#xff1a;在复制内容到 Ubuntu 上的 WPS 2019 出现如下问题&#xff1a;列表符号、换行和缩进错乱&#xff0c;表现为每行前的点符号&#xff08;•&#xff09;变成不规则对齐或空格间距不统一。原因分析✅ 主要原因是&#xff1a;WPS 2019 在 Ubuntu 上的兼容性较差&a…

bws-rs:Rust 编写的 S3 协议网关框架,支持灵活后端接入

bws-rs&#xff1a;Rust 编写的 S3 协议网关框架&#xff0c;支持灵活后端接入 bws-rs介绍 bws-rs 是一个用 Rust 编写的轻量级 S3 协议服务端网关框架&#xff0c;旨在帮助开发者快速构建兼容 AWS S3 协议 的对象存储服务。该框架支持 S3 V4 签名校验&#xff0c;集成 Axum 作…

黑马点评系列问题之p70postman报错“服务器异常”

问题描述&#xff1a;在做这个位置的时候报错报错如下控制台报错如下解决根据控制台的报错来看&#xff0c;是​Redis模板未注入导致的空指针异常经过排查&#xff0c;原因是这里少了个Resource

Docker搭建Elasticsearch和Kibana

1.安装docker&#xff0c;确保正常启动 2.按步骤操作&#xff0c;这里的es是单节点的&#xff0c;如需多节点&#xff0c;需安装docker-compose进行yml文件的编写对容器进行编排 #docker拉镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.11.2 docker pul…

【深度学习笔记 Ⅰ】3 step by step (jupyter)

1. 导包 import numpy as np import h5py import matplotlib.pyplot as plt from testCases_v2 import * from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward% matplotlib inline plt.rcParams[figure.figsize] (5.0, 4.0) # set default size of plo…

前端流式渲染流式SSR详解

以下是关于前端流式渲染及流式SSR&#xff08;Server-Side Rendering&#xff09;的详细解析&#xff0c;结合核心原理、技术实现、优化策略及实际应用场景展开说明&#xff1a;⚙️ 一、流式渲染基础原理 核心概念 ◦ 流式渲染&#xff1a;数据通过分块传输&#xff08;Chunke…

Redis通用常见命令(含面试题)

核心命令get 根据key取valueset 把key和vlaue存入进去key和value本事上都是字符串&#xff0c;但在操作的时候可以不用加上引号""Redis作为键值对的结构&#xff0c;key固定就是字符串&#xff0c;value实际上会有多种类型&#xff08;字符串哈希表&#xff0c;列表&…

react/vue vite ts项目中,自动引入路由文件、 import.meta.glob动态引入路由 无需手动引入

utils/autoRouteHelper.ts // src/utils/autoRouteHelper.ts import { lazy } from "react"; import withLoading from "/components/router/withLoading";/** 自动生成某个文件夹下的子路由 */ interface RouteItem {path: string;element?: any;childre…

Linux简单了解历史

一、引言Linux是计算机经久不衰的一个计算机操作系统&#xff0c;在那个unix、苹果macOS、微软Window神仙打架的年代拼出自己的一席之地。最初的Linux完全就是一个unix的一个翻版&#xff0c;并且最开始的版本(0.01)就是一个差不多一万行简单到不能再简单的版本。那现在Linux是…

lua(xlua)基础知识点记录二

1. 关于lua函数传参参数在lua中给function传递参数的时候一般分为两种情况&#xff1a;值传递和引用传递值传递&#xff1a;值传递&#xff1a;数字、字符串、布尔值、nil等基本类型通过值传递。函数内部接收的是外部变量的副本&#xff0c;修改副本不会影响原始变量。 虽然我们…