鴻蒙網絡編程系列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
英文地址,請注明出處:http://en.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;修改副本不會影響原始變量。 雖然我們…