曂沅仴駦 发表于 2024-11-3 14:59:10

鸿蒙双向认证

鸿蒙双向认证

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

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

[*]DefaultTrustEvaluator,利用默认的验证方式,验证证书的有效性,证书信任链那套
[*]RevocationTrustEvaluator,验证证书是否被吊销
[*]PinnedCertificatesTrustEvaluator,验证证书是否同当地的同等,可以是自签证书
[*]PublicKeysTrustEvaluator,验证证书的公钥,可以是自签证书,不过这个有个好处就是不消关心证书的有效期了
[*]CompositeTrustEvaluator,混合模式
[*]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.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 = nulllet serverCertStr: string | null = nullcert.createX509Cert({data: stringToUint8Array(getRawFileContent(context, 'server.cer')),encodingFormat: cert.EncodingFormat.FORMAT_DER}).then(x509Cert => {serverCert = x509CertserverCertStr = 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.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}=${kv}`)}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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙双向认证