当前位置: 首页 > article >正文

鸿蒙双向认证

鸿蒙双向认证

开发环境 基于API12

参考文档

切换到鸿蒙也要用上双向认证。使用的其中的 rcp 功能,详细文档https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/remote-communication-rcp-V5

双向认证包含两个方向,分为客户端验证服务端的证书和服务端验证客户端的证书。

客户端验证服务端的证书

这个就需要把服务端的证书内置在客户端里边,并且在请求的时候获取到服务端的证书,然后自己进行对比证书是否同内置的一样。

服务器证书的校验等级有以下几种:

  1. DefaultTrustEvaluator,使用默认的验证方式,验证证书的有效性,证书信任链那套
  2. RevocationTrustEvaluator,验证证书是否被吊销
  3. PinnedCertificatesTrustEvaluator,验证证书是否同本地的一致,可以是自签证书
  4. PublicKeysTrustEvaluator,验证证书的公钥,可以是自签证书,不过这个有个好处就是不用关心证书的有效期了
  5. CompositeTrustEvaluator,混合模式
  6. DisabledTrustEvaluator,不验证证书

可以按需自己处理。本文以对比公钥为例。

首先获取本地证书及证书的公钥。

let context = getContext(this)
const getRawFileContent = (ctx: Context, file: string) : string => {
  let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  return String.fromCharCode(...new Uint8Array(buffer))
}

function stringToUint8Array(str: string): Uint8Array {
  let arr: Array<number> = [];
  for (let i = 0, j = str.length; i < j; i++) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

const uInt8ToString = (buffer: Uint8Array): string => {
  return String.fromCharCode(...new Uint8Array(buffer))
}

let serverCert: cert.X509Cert | null = null
let serverCertStr: string | null = null

cert.createX509Cert({
  data: stringToUint8Array(getRawFileContent(context, 'server.cer')),
  encodingFormat: cert.EncodingFormat.FORMAT_DER
}).then(x509Cert => {
  serverCert = x509Cert

  serverCertStr = uInt8ToString(serverCert?.getPublicKey().getEncoded().data)
}).catch((error: BusinessError) => {
})

增加自定义验证证书函数。对比公钥是否一致。

{
  security: {
    remoteValidation: (context: rcp.ValidationContext) => {
      let tar = uInt8ToString(context.x509Certs[0].getPublicKey().getEncoded().data)
      if (serverCertStr === tar) {
        return true
      }
      return false
    },
  },
}

这样客户端就验证了服务端证书是否符合要求。

服务端验证客户端的证书

由于接口请求的问题,需要把客户端证书写入到沙盒里边,然后把沙盒地址传进去,就有点麻烦。

首先写入客户端证书到沙盒。

本文需要使用到crt及key两个文件。接口crt文件需要文本形式,但是key又需要沙盒地址。

const getRawFileContent = (ctx: Context, file: string) : string => {
  let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  return String.fromCharCode(...new Uint8Array(buffer))
}

let context = getContext(this)
let filesDir = context.filesDir

function saveFile(fn: string) {
  let file = fs.openSync(filesDir + '/' + fn, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  let clientContent = context.resourceManager.getRawFileContentSync(fn)
  fs.writeSync(file.fd, clientContent.buffer)
  fs.fsyncSync(file.fd)
  fs.closeSync(file)
}

saveFile('client.key')

然后提供客户端证书给服务端进行校验。

{
  security: {
    certificate: {
      content: getRawFileContent(context, 'client.crt'),
      key: filesDir + '/client.key',
      type: 'PEM',
      keyPassword: 'xxx'
    },
  },
}

这样就能把证书提供给服务端进行校验了。

完整实例

import { BusinessError } from '@ohos.base';
import { rcp } from "@kit.RemoteCommunicationKit";
import fs from '@ohos.file.fs';
import { cert } from '@kit.DeviceCertificateKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';

const getRawFileContent = (ctx: Context, file: string) : string => {
  let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  return String.fromCharCode(...new Uint8Array(buffer))
}

let context = getContext(this)
let filesDir = context.filesDir

function saveFile(fn: string) {
  let file = fs.openSync(filesDir + '/' + fn, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  let clientContent = context.resourceManager.getRawFileContentSync(fn)
  fs.writeSync(file.fd, clientContent.buffer)
  fs.fsyncSync(file.fd)
  fs.closeSync(file)
}

saveFile('client.key')

function stringToUint8Array(str: string): Uint8Array {
  let arr: Array<number> = [];
  for (let i = 0, j = str.length; i < j; i++) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

const uInt8ToString = (buffer: Uint8Array): string => {
  return String.fromCharCode(...new Uint8Array(buffer))
}

let serverCert: cert.X509Cert | null = null
let serverCertStr: string | null = null

cert.createX509Cert({
  data: stringToUint8Array(getRawFileContent(context, 'server.cer')),
  encodingFormat: cert.EncodingFormat.FORMAT_DER
}).then(x509Cert => {
  serverCert = x509Cert

  serverCertStr = uInt8ToString(serverCert?.getPublicKey().getEncoded().data)
}).catch((error: BusinessError) => {
  console.error('createX509Cert failed, errCode: ' + error.code + ', errMsg: ' + error.message);
})

const defaultTimeout: number = 60*1000

const createRequestConfiguration = (timeout: number): rcp.Configuration => {
  return {
    security: {
      remoteValidation: (context: rcp.ValidationContext) => {
        let tar = uInt8ToString(context.x509Certs[0].getPublicKey().getEncoded().data)
        if (serverCertStr === tar) {
          return true
        }
        return false
      },
      certificate: {
        content: getRawFileContent(context, 'client.crt'),
        key: filesDir + '/client.key',
        type: 'PEM',
        keyPassword: 'xxx'
      },
    },
    transfer: {
      autoRedirect: true,
      timeout: {
        connectMs: 5000,
        transferMs: timeout,
      }
    }
  }
}

const sessionConfig: rcp.SessionConfiguration = {
  requestConfiguration: createRequestConfiguration(defaultTimeout)
}

const session: rcp.Session = rcp.createSession(sessionConfig)

export const rcpget = (url:string, timeout: number = defaultTimeout):Promise<object> => {
  return new Promise((resolve, reject) => {
    let request = new rcp.Request(url, 'GET', getHeaders())
    if (timeout != defaultTimeout) {
      request.configuration = createRequestConfiguration(timeout)
    }
    session.fetch(request).then((response:rcp.Response) => {
      if (response.statusCode == 200) {
        resolve(data)
      } else {
        reject()
      }
    }).catch((err: BusinessError) => {
      reject()
    })
  })
}

export const rcppost = (url: string, params: Record<string, string | number>, timeout: number = defaultTimeout):Promise<object> => {
  let p: string[] = []
  for(let kv of Object.entries(params)) {
    p.push(`${kv[0]}=${kv[1]}`)
  }
  let pstr: string = p.join('&')
  return new Promise((resolve, reject) => {
    let request: rcp.Request
    if (pstr == '') {
      request = new rcp.Request(url, 'POST', getHeaders())
    } else {
      request = new rcp.Request(url, 'POST', getHeaders(), pstr)
    }
    if (timeout != defaultTimeout) {
      request.configuration = createRequestConfiguration(timeout)
    }
    session.fetch(request).then((response:rcp.Response) => {
      if (response.statusCode == 200) {
        resolve(data)
      } else {
        reject()
      }
    }).catch((err: BusinessError) => {
      reject()
    })
  })
}

如此这般就能实现双向认证。感觉安全级别又上了一个等级。


http://www.kler.cn/a/294378.html

相关文章:

  • 408模拟卷较难题(无分类)
  • Window下PHP安装最新sg11(php5.3-php8.3)
  • 密码学的基本原理
  • 《C++在金融领域的技术革命:高效、安全与创新的融合》
  • Springboot 日志处理(非常详细)
  • flink sql + kafka + mysql 如何构建实时数仓
  • 贷款利率高低跟什么有关?仅凭身份证就能贷到款?额度是多少?
  • SCSS darken函数
  • Socket编程---TCP篇
  • Kotlin高阶函数与Lambda表达式及内联函数的介绍
  • 深度学习速通系列:推荐五个提高机器学习模型鲁棒性和稳定性的开源工具或框架
  • 打靶记录16——Momentum
  • 周末总结(2024/09/07)
  • springboot+vue+mybatis计算机毕业设计智慧篮球馆预约+PPT+论文+讲解+售后
  • html 单页面路由模式hash和history
  • Shell脚本基本语法(Linux篇)
  • MapSet之二叉搜索树
  • 1-7 掩膜的运用 opencv树莓派4B 入门系列笔记
  • 力扣239题详解:滑动窗口最大值的多种解法与模拟面试问答
  • GNN会议期刊汇总(人工智能、机器学习、深度学习、数据挖掘)
  • kubernetes--配置与存储(ConfigMap、加密数据配置Secret、SubPath、热更新、Volumes、NFS挂载、PV与PVC)
  • C#基础(5)交错数组*
  • 【Rust光年纪】Rust 机器人学库全景:功能、安装与API概览
  • 多线程篇(阻塞队列- BlockingQueue)(持续更新迭代)
  • 不同vlan之间的通信方法
  • 微信小程序仿微信聊天界面