鸿蒙双向认证

打印 上一主题 下一主题

主题 1010|帖子 1010|积分 3030

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
鸿蒙双向认证

开发环境 基于API12
参考文档
切换到鸿蒙也要用上双向认证。利用的其中的 rcp 功能,详细文档https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/remote-communication-rcp-V5
双向认证包含两个方向,分为客户端验证服务端的证书和服务端验证客户端的证书。
客户端验证服务端的证书

这个就必要把服务端的证书内置在客户端里边,并且在请求的时候获取到服务端的证书,然后自己进行对比证书是否同内置的一样。
服务器证书的校验等级有以下几种:

  • DefaultTrustEvaluator,利用默认的验证方式,验证证书的有效性,证书信任链那套
  • RevocationTrustEvaluator,验证证书是否被吊销
  • PinnedCertificatesTrustEvaluator,验证证书是否同当地的同等,可以是自签证书
  • PublicKeysTrustEvaluator,验证证书的公钥,可以是自签证书,不过这个有个好处就是不消关心证书的有效期了
  • CompositeTrustEvaluator,混合模式
  • DisabledTrustEvaluator,不验证证书
可以按需自己处理。本文以对比公钥为例。
首先获取当地证书及证书的公钥。
  1. let context = getContext(this)
  2. const getRawFileContent = (ctx: Context, file: string) : string => {
  3.   let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  4.   return String.fromCharCode(...new Uint8Array(buffer))
  5. }
  6. function stringToUint8Array(str: string): Uint8Array {
  7.   let arr: Array<number> = [];
  8.   for (let i = 0, j = str.length; i < j; i++) {
  9.     arr.push(str.charCodeAt(i));
  10.   }
  11.   return new Uint8Array(arr);
  12. }
  13. const uInt8ToString = (buffer: Uint8Array): string => {
  14.   return String.fromCharCode(...new Uint8Array(buffer))
  15. }
  16. let serverCert: cert.X509Cert | null = null
  17. let serverCertStr: string | null = null
  18. cert.createX509Cert({
  19.   data: stringToUint8Array(getRawFileContent(context, 'server.cer')),
  20.   encodingFormat: cert.EncodingFormat.FORMAT_DER
  21. }).then(x509Cert => {
  22.   serverCert = x509Cert
  23.   serverCertStr = uInt8ToString(serverCert?.getPublicKey().getEncoded().data)
  24. }).catch((error: BusinessError) => {
  25. })
复制代码
增长自定义验证证书函数。对比公钥是否同等。
  1. {
  2.   security: {
  3.     remoteValidation: (context: rcp.ValidationContext) => {
  4.       let tar = uInt8ToString(context.x509Certs[0].getPublicKey().getEncoded().data)
  5.       if (serverCertStr === tar) {
  6.         return true
  7.       }
  8.       return false
  9.     },
  10.   },
  11. }
复制代码
如许客户端就验证了服务端证书是否符合要求。
服务端验证客户端的证书

由于接口请求的题目,必要把客户端证誊写入到沙盒里边,然后把沙盒地点传进去,就有点贫苦。
首先写入客户端证书到沙盒。
本文必要利用到crt及key两个文件。接口crt文件必要文本形式,但是key又必要沙盒地点。
  1. const getRawFileContent = (ctx: Context, file: string) : string => {
  2.   let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  3.   return String.fromCharCode(...new Uint8Array(buffer))
  4. }
  5. let context = getContext(this)
  6. let filesDir = context.filesDir
  7. function saveFile(fn: string) {
  8.   let file = fs.openSync(filesDir + '/' + fn, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  9.   let clientContent = context.resourceManager.getRawFileContentSync(fn)
  10.   fs.writeSync(file.fd, clientContent.buffer)
  11.   fs.fsyncSync(file.fd)
  12.   fs.closeSync(file)
  13. }
  14. saveFile('client.key')
复制代码
然后提供客户端证书给服务端进行校验。
  1. {
  2.   security: {
  3.     certificate: {
  4.       content: getRawFileContent(context, 'client.crt'),
  5.       key: filesDir + '/client.key',
  6.       type: 'PEM',
  7.       keyPassword: 'xxx'
  8.     },
  9.   },
  10. }
复制代码
如许就能把证书提供给服务端进行校验了。
完整实例

  1. 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 => {
  2.   let buffer = ctx.resourceManager.getRawFileContentSync(file).buffer
  3.   return String.fromCharCode(...new Uint8Array(buffer))
  4. }
  5. let context = getContext(this)
  6. let filesDir = context.filesDir
  7. function saveFile(fn: string) {
  8.   let file = fs.openSync(filesDir + '/' + fn, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
  9.   let clientContent = context.resourceManager.getRawFileContentSync(fn)
  10.   fs.writeSync(file.fd, clientContent.buffer)
  11.   fs.fsyncSync(file.fd)
  12.   fs.closeSync(file)
  13. }
  14. saveFile('client.key')
  15. 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 = nulllet serverCertStr: string | null = nullcert.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*1000const 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()    })  })}
复制代码
如此这般就能实现双向认证。感觉安全级别又上了一个等级。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表