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

ops/2024/11/2 3:52:40/

1. TLS数字证书验签简介

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

用户数字证书的验签是通过签发该数字证书的CA证书完成的,因为用户数字证书是由CA证书的私钥签名的,使用CA的公钥可以验证数字证书的合法性,鸿蒙的X509Cert数字证书类提供了验签方法verify:

verify(key: cryptoFramework.PubKey): Promise<void>

该方法可以通过CA的公钥来验证证书的有效性。

本文将通过一个示例演示数字证书内容的查看方法以及如何对一个数字证书进行验签。

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

本示例运行后的界面如图所示:

单击CA证书后面的“选择”按钮,选择一个CA证书,再单击“查看”按钮可以查看该证书的详细信息,如图所示:

然后再选择一个不是该CA证书签发的用户证书,比如百度的证书,再单击“验签”按钮:

很显然,验签失败了。再选择一个该CA证书签名的用户证书,然后单击“验签”按钮:

这次验签就通过了。

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

下面详细介绍创建该示例的步骤。

步骤1:创建Empty Ability项目。

步骤2:在Index.ets文件里添加如下的代码:

import { BusinessError } from '@kit.BasicServicesKit';
import { cert } from '@kit.DeviceCertificateKit';
import fs from '@ohos.file.fs';
import { picker } from '@kit.CoreFileKit';@Entry
@Component
struct Index {//连接、通讯历史记录@State msgHistory: string = ''//CA证书是否已选择@State caFileSelect: boolean = false//用户证书是否已选择@State certFileSelect: boolean = false//选择的ca文件@State caFileUri: string = ''//选择的用户证书文件@State certFileUri: string = ''scroller: Scroller = new Scroller()build() {Row() {Column() {Text("数字证书查看及验签示例").fontSize(14).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.Center).padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("CA证书").fontSize(14).width(90).flexGrow(1)Button("选择").onClick(async () => {this.caFileUri = await selectSingleDocFile(getContext(this))if (this.caFileUri) {this.caFileSelect = true}}).width(70).fontSize(14)Button("查看").onClick(() => {this.viewCertInfo(this.caFileUri)}).width(70).fontSize(14).enabled(this.caFileSelect)}.width('100%').padding(10)Text(this.caFileUri).width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("用户证书:").fontSize(14).width(90).flexGrow(1)Button("选择").onClick(async () => {this.certFileUri = await selectSingleDocFile(getContext(this))if (this.certFileUri) {this.certFileSelect = true}}).width(70).fontSize(14)Button("查看").onClick(() => {this.viewCertInfo(this.certFileUri)}).width(70).fontSize(14).enabled(this.certFileSelect)Button("验签").onClick(() => {this.verifyCert(this.caFileUri, this.certFileUri)}).width(70).fontSize(14).enabled(this.certFileSelect && this.caFileSelect)}.width('100%').padding(10)Text(this.certFileUri).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%')}//输出指定证书文件的证书信息async viewCertInfo(filePath: string) {let x509Cert = await this.getCertFromFile(filePath)if (x509Cert != undefined) {this.showCertInfo(x509Cert)} else {this.msgHistory += "错误的证书文件格式:" + filePath + "\r\n";}}//从文件获取X509证书async getCertFromFile(filePath: string): Promise<cert.X509Cert | undefined> {let newCert: cert.X509Cert | undefined = undefined//读取文件内容let certData = readArrayBufferContentFromFile(filePath);if (certData) {let encodingBlob: cert.EncodingBlob = {data: new Uint8Array(certData),encodingFormat: cert.EncodingFormat.FORMAT_PEM};//创建X509数字证书await cert.createX509Cert(encodingBlob).then((x509Cert: cert.X509Cert) => {newCert = x509Cert}).catch((err: BusinessError) => {this.msgHistory += `创建X509证书失败: 错误码 ${err.code}, 错误信息 ${JSON.stringify(err)}\r\n`;})}return newCert}//输出证书信息async showCertInfo(x509Cert: cert.X509Cert) {try {let issuerName = x509Cert.getIssuerX500DistinguishedName().getName()this.msgHistory += `颁发者可分辨名称:${issuerName}\r\n`;let subjectName = x509Cert.getSubjectX500DistinguishedName().getName()this.msgHistory += `证书主题可分辨名称:${subjectName}\r\n`;let subjectCNName = x509Cert.getSubjectX500DistinguishedName().getName("CN")this.msgHistory += `证书主题CN名称:${subjectCNName}\r\n`;this.msgHistory += `证书有效期:${x509Cert.getNotBeforeTime()}${x509Cert.getNotAfterTime()}\r\n`;this.msgHistory += `证书签名算法:${x509Cert.getSignatureAlgName()}\r\n`;} catch (e) {this.msgHistory += '输出证书信息异常: ' + e.message + "\r\n";}}//使用CA证书验证用户证书async verifyCert(caFilePath: string, certFilePath: string) {//获取CA证书let caCert = await this.getCertFromFile(caFilePath)if (caCert == undefined) {this.msgHistory += "错误的证书文件格式:" + caFilePath + "\r\n";return}//获取用户证书let userCert = await this.getCertFromFile(certFilePath)if (userCert == undefined) {this.msgHistory += "错误的证书文件格式:" + certFilePath + "\r\n";}//使用CA证书公玥验证用户证书userCert?.verify(caCert.getPublicKey()).then(() => {this.msgHistory += "证书验签通过\r\n";}).catch((err: BusinessError) => {this.msgHistory += `验签失败:错误码 ${err.code}, 错误信息 ${JSON.stringify(err)}\r\n`;})}
}//选择单个文件并返回选中文件地址
async function selectSingleDocFile(context: Context): Promise<string> {let selectedFilePath: string = ""let documentPicker = new picker.DocumentViewPicker(context);await documentPicker.select({ maxSelectNumber: 1 }).then((result) => {if (result.length > 0) {selectedFilePath = result[0]}})return selectedFilePath
}//从文件读取二进制内容
function readArrayBufferContentFromFile(fileUri: string): ArrayBuffer {let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY);let fsStat = fs.statSync(file.fd);let buf = new ArrayBuffer(fsStat.size);fs.readSync(file.fd, buf);fs.fsyncSync(file.fd)fs.closeSync(file);return buf
}

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

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

4. 代码分析

本示例关键点有两个,一个是从证书文件中获取证书信息创建X509Cert对象,这是通过方法getCertFromFile实现的;另一个是使用CA证书验签用户证书,在获取CA公钥的时候使用的是X509Cert的getPublicKey方法,需要注意的是,该方法获取的公钥只能用于验签,不能用来获取公钥的内容,否则会出现异常。

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

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

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


http://www.ppmy.cn/ops/130315.html

相关文章

QT报错,QObject::setParent: Cannot set parent, new parent is in a different Thread

文章目录 一、背景分析二、错误原因跨线程设置父对象&#xff1a;对象迁移&#xff1a;线程间共享对象&#xff1a; 三、解决方案确保父子对象在同一线程&#xff1a;正确管理对象迁移&#xff1a;避免线程间共享对象&#xff1a; 四、代码示例五、总结 QT报错&#xff0c;QObj…

网安加·百家讲坛 | 赵锐:数据安全二十载

作者简介&#xff1a;赵锐&#xff0c;诸子云上海会长、某跨国企业中国区副总裁、网安加社区特聘专家、专利发明人&#xff0c;历任多家金融机构、世界500强企业的安全负责人、安全专家。有20年科技风险、信息安全、数据安全、业务安全等领域的丰富经验&#xff0c;是多家机构的…

notepad++ compare插件的离线下载和安装

一、离线安装 去改地址找到最新的插件&#xff1a;https://github.com/notepad-plus-plus/nppPluginList/blob/master/doc/plugin_list_x64.md下载之后复制到插件文件夹&#xff0c;插件文件夹的打开方式如下 注意目录&#xff1a; 二、问题汇总 &#xff08;1&#xff09…

[ 问题解决篇 ] 解决windows虚拟机安装vmtools报错-winserver2012安装vmtools及安装KB2919355补丁 (附离线工具)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

企业数字化转型的理论指南:从企业架构能力的建立到战略实施

在全球数字化竞争加剧的背景下&#xff0c;企业必须通过技术和战略转型来保持竞争力。企业架构&#xff08;Enterprise Architecture, EA&#xff09;不仅是一项技术工具&#xff0c;更是推动企业在转型过程中保持高效、灵活和创新的关键要素。《世界级的企业架构&#xff1a;领…

长短期记忆网络LSTM

背景&#xff1a; RNN中存在梯度爆炸和梯度消失问题&#xff0c;梯度消失问题严重 设计思路&#xff1a; RNN希望将所有的信息记住&#xff0c;不论是有效信息还是无效信息 LSTM设计一个记忆细胞&#xff0c;具备选择性记忆的功能&#xff0c;可以选择记忆重要信息&#xf…

C语言中的main函数:命令行参数的工作原理

在C语言中&#xff0c;main函数是程序的入口点。它不仅可以接受返回值&#xff0c;还能处理命令行参数&#xff0c;允许用户在运行程序时传递数据。命令行参数是用户在启动程序时通过命令行界面提供的输入。C语言允许通过main函数的参数来访问这些输入。   int main(int argc…

如何理解PostgreSQL全页写?

读了很多PostgreSQL的FullPageWrite过程&#xff0c;结果思考时把自己绕进去了。每次感觉读懂了&#xff0c;但是都没能理解&#xff0c;其实是一个很简单的逻辑。   全页写发生在PG的Checkpoint时候&#xff0c;不要过于考虑全页写在checkpoint中的过程&#xff0c;而是单纯…