鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥协商(ArkTS)】

news2024/10/3 10:48:02

密钥协商(ArkTS)

以协商密钥类型为X25519 256,并密钥仅在HUKS内使用为例,完成密钥协商。

开发步骤

生成密钥

设备A、设备B各自生成一个非对称密钥,具体请参考[密钥生成]或[密钥导入]。

密钥生成时,可指定参数HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG(可选),用于标识基于该密钥协商出的密钥是否由HUKS管理。

  • 当TAG设置为HUKS_STORAGE_ONLY_USED_IN_HUKS时,表示基于该密钥协商出的密钥,由HUKS管理,可保证协商密钥全生命周期不出安全环境。
  • 当TAG设置为HUKS_STORAGE_KEY_EXPORT_ALLOWED时,表示基于该密钥协商出的密钥,返回给调用方管理,由业务自行保证密钥安全。
  • 若业务未设置TAG的具体值,表示基于该密钥协商出的密钥,即可由HUKS管理,也可返回给调用方管理,业务可在后续协商时再选择使用何种方式保护密钥。
  • 开发前请熟悉鸿蒙开发指导文档gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。

导出密钥

设备A、B导出非对称密钥对的公钥材料。

密钥协商

设备A、B分别基于本端私钥和对端设备的公钥,协商出共享密钥。

密钥协商时,可指定参数HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG(可选),用于标识协商得到的密钥是否由HUKS管理。

生成协商规格
HUKS_STORAGE_ONLY_USED_IN_HUKSHUKS_STORAGE_ONLY_USED_IN_HUKS密钥由HUKS管理
HUKS_STORAGE_KEY_EXPORT_ALLOWEDHUKS_STORAGE_KEY_EXPORT_ALLOWED密钥返回给调用方管理
未指定TAG具体值HUKS_STORAGE_ONLY_USED_IN_HUKS密钥由HUKS管理
未指定TAG具体值HUKS_STORAGE_KEY_EXPORT_ALLOWED密钥返回给调用方管理
未指定TAG具体值 HarmonyOS与OpenHarmony鸿蒙文档籽料:mau123789是v直接拿未指定TAG具体值密钥返回给调用方管理

搜狗高速浏览器截图20240326151547.png

注:协商时指定的TAG值,不可与生成时指定的TAG值冲突。表格中仅列举有效的指定方式。

删除密钥

当密钥废弃不用时,设备A、B均需要删除密钥。

下面分别以X25519 与 DH密钥为例,进行协商。

/*
*以下以X25519 256密钥的Promise操作使用为例
*/
import { huks } from '@kit.UniversalKeystoreKit';

/*
* 确定密钥别名和封装密钥属性参数集
*/
let srcKeyAliasFirst = "AgreeX25519KeyFirstAlias";
let srcKeyAliasSecond = "AgreeX25519KeySecondAlias";
let agreeX25519InData = 'AgreeX25519TestIndata';
let finishOutData: Uint8Array;
let handle: number;
let exportKey: Uint8Array;
let exportKeyFirst: Uint8Array;
let exportKeySecond: Uint8Array;
/* 集成生成密钥参数集 */
let properties: Array<huks.HuksParam> = [{
  tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
  value: huks.HuksKeyAlg.HUKS_ALG_X25519,
}, {
  tag: huks.HuksTag.HUKS_TAG_PURPOSE,
  value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE,
}, {
  tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
  value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256,
}, {
  tag: huks.HuksTag.HUKS_TAG_DIGEST,
  value: huks.HuksKeyDigest.HUKS_DIGEST_NONE,
}, {
  tag: huks.HuksTag.HUKS_TAG_PADDING,
  value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
}, {
  tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
  value: huks.HuksCipherMode.HUKS_MODE_CBC,
}, {
  tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG,
  value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS,
}];
let HuksOptions: huks.HuksOptions = {
  properties: properties,
  inData: new Uint8Array(new Array())
}
/* 集成第一个协商参数集 */
const finishProperties: Array<huks.HuksParam> = [{
  tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG,
  value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS,
}, {
  tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS,
  value: true
}, {
  tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
  value: huks.HuksKeyAlg.HUKS_ALG_AES,
}, {
  tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
  value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256,
}, {
  tag: huks.HuksTag.HUKS_TAG_PURPOSE,
  value:
  huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT |
  huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
}, {
  tag: huks.HuksTag.HUKS_TAG_DIGEST,
  value: huks.HuksKeyDigest.HUKS_DIGEST_NONE,
}, {
  tag: huks.HuksTag.HUKS_TAG_PADDING,
  value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
}, {
  tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
  value: huks.HuksCipherMode.HUKS_MODE_ECB,
}];
let finishOptionsFirst: huks.HuksOptions = {
  properties: [
    ...finishProperties, {
    tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS,
    value: StringToUint8Array(srcKeyAliasFirst + 'final'),
  }],
  inData: StringToUint8Array(agreeX25519InData)
}
/* 集成第二个协商参数集 */
let finishOptionsSecond: huks.HuksOptions = {
  properties: [
    ...finishProperties, {
    tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS,
    value: StringToUint8Array(srcKeyAliasSecond + 'final'),
  }],
  inData: StringToUint8Array(agreeX25519InData)
}

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

class throwObject {
  isThrow: boolean = false
}

/* 生成密钥 */
function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<void>((resolve, reject) => {
    try {
      huks.generateKeyItem(keyAlias, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/* 调用generateKeyItem生成密钥 */
async function publicGenKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
  console.info(`enter promise generateKeyItem`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await generateKeyItem(keyAlias, huksOptions, throwObject)
      .then((data) => {
        console.info(`promise: generateKeyItem success, data = ${JSON.stringify(data)}`);
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: generateKeyItem failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: generateKeyItem input arg invalid, ${JSON.stringify(error)}`);
  }
}

/*初始化密钥会话接口,并获取一个句柄(必选)和挑战值(可选)*/
function initSession(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<huks.HuksSessionHandle>((resolve, reject) => {
    try {
      huks.initSession(keyAlias, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/*调用initSession获取handle*/
async function publicInitFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
  console.info(`enter promise doInit`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await initSession(keyAlias, huksOptions, throwObject)
      .then((data) => {
        console.info(`promise: doInit success, data = ${JSON.stringify(data)}`);
        handle = data.handle;
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: doInit failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: doInit input arg invalid, ${JSON.stringify(error)}`);
  }
}

/* 分段添加密钥操作的数据并进行相应的密钥操作,输出处理数据 */
function updateSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<huks.HuksReturnResult>((resolve, reject) => {
    try {
      huks.updateSession(handle, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/* 调用updateSession进行协商操作 */
async function publicUpdateFunc(handle: number, huksOptions: huks.HuksOptions) {
  console.info(`enter promise doUpdate`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await updateSession(handle, huksOptions, throwObject)
      .then((data) => {
        console.info(`promise: doUpdate success, data = ${JSON.stringify(data)}`);
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: doUpdate failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: doUpdate input arg invalid, ${JSON.stringify(error)}`);
  }
}

/* 结束密钥会话并进行相应的密钥操作,输出处理数据 */
function finishSession(handle: number, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<huks.HuksReturnResult>((resolve, reject) => {
    try {
      huks.finishSession(handle, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/* 调用finishSession结束操作 */
async function publicFinishFunc(handle: number, huksOptions: huks.HuksOptions) {
  console.info(`enter promise doFinish`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await finishSession(handle, huksOptions, throwObject)
      .then((data) => {
        finishOutData = data.outData as Uint8Array;
        console.info(`promise: doFinish success, data = ${JSON.stringify(data)}`);
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: doFinish failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: doFinish input arg invalid, ${JSON.stringify(error)}`);
  }
}

/* 导出密钥 */
function exportKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<huks.HuksReturnResult>((resolve, reject) => {
    try {
      huks.exportKeyItem(keyAlias, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/* 调用exportKeyItem导出公钥操作 */
async function publicExportKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
  console.info(`enter promise export`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await exportKeyItem(keyAlias, huksOptions, throwObject)
      .then((data) => {
        console.info(`promise: exportKeyItem success, data = ${JSON.stringify(data)}`);
        exportKey = data.outData as Uint8Array;
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: exportKeyItem failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: exportKeyItem input arg invalid, ${JSON.stringify(error)}`);
  }
}

/* 删除密钥操作 */
function deleteKeyItem(keyAlias: string, huksOptions: huks.HuksOptions, throwObject: throwObject) {
  return new Promise<void>((resolve, reject) => {
    try {
      huks.deleteKeyItem(keyAlias, huksOptions, (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data);
        }
      });
    } catch (error) {
      throwObject.isThrow = true;
      throw (error as Error);
    }
  });
}

/* 调用deleteKeyItem删除密钥操作 */
async function publicDeleteKeyFunc(keyAlias: string, huksOptions: huks.HuksOptions) {
  console.info(`enter promise deleteKeyItem`);
  let throwObject: throwObject = { isThrow: false };
  try {
    await deleteKeyItem(keyAlias, huksOptions, throwObject)
      .then((data) => {
        console.info(`promise: deleteKeyItem key success, data = ${JSON.stringify(data)}`);
      })
      .catch((error: Error) => {
        if (throwObject.isThrow) {
          throw (error as Error);
        } else {
          console.error(`promise: deleteKeyItem failed, ${JSON.stringify(error)}`);
        }
      });
  } catch (error) {
    console.error(`promise: deleteKeyItem input arg invalid, ${JSON.stringify(error)}`);
  }
}

async function testAgree() {
  /* 1.确定密钥别名并集成并集成要参数集 A设备:srcKeyAliasFirst  B设备:srcKeyAliasSecond*/
  /* 2.设备A生成密钥 */
  await publicGenKeyFunc(srcKeyAliasFirst, HuksOptions);
  /* 3.设备B生成密钥 */
  await publicGenKeyFunc(srcKeyAliasSecond, HuksOptions);
  /* 4.设备A、B导出非对称密钥的公钥 */
  await publicExportKeyFunc(srcKeyAliasFirst, HuksOptions);
  exportKeyFirst = exportKey;
  await publicExportKeyFunc(srcKeyAliasSecond, HuksOptions);
  exportKeySecond = exportKey;
  /* 5.对第一个密钥进行协商(三段式)*/
  await publicInitFunc(srcKeyAliasFirst, HuksOptions);
  HuksOptions.inData = exportKeySecond;
  await publicUpdateFunc(handle, HuksOptions);
  await publicFinishFunc(handle, finishOptionsFirst);
  /* 5.对第二个密钥进行协商(三段式) */
  await publicInitFunc(srcKeyAliasSecond, HuksOptions);
  HuksOptions.inData = exportKeyFirst;
  await publicUpdateFunc(handle, HuksOptions);
  await publicFinishFunc(handle, finishOptionsSecond);
  /* 6.设备A、B删除密钥 */
  await publicDeleteKeyFunc(srcKeyAliasFirst, HuksOptions);
  await publicDeleteKeyFunc(srcKeyAliasSecond, HuksOptions);
}

下面以DH密钥协商为例

/*
 *以下以 DH密钥的Promise操作使用为例
 */
import { huks } from '@kit.UniversalKeystoreKit'

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

function Uint8ArrayToBigInt(arr: Uint8Array): bigint {
  let i = 0
  const byteMax: bigint = BigInt('0x100')
  let result: bigint = BigInt('0')
  while (i < arr.length) {
    result = result * byteMax
    result = result + BigInt(arr[i])
    i += 1
  }
  return result
}

const dhAgree: Array<huks.HuksParam> = [{
  tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
  value: huks.HuksKeyAlg.HUKS_ALG_DH,
}, {
  tag: huks.HuksTag.HUKS_TAG_PURPOSE,
  value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE,
}]
const dh2048Agree: Array<huks.HuksParam> = [
  ...dhAgree, {
  tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
  value: huks.HuksKeySize.HUKS_DH_KEY_SIZE_2048,
}]
const dhGenOptions: huks.HuksOptions = {
  properties: dh2048Agree,
  inData: new Uint8Array([])
}
const emptyOptions: huks.HuksOptions = {
  properties: [],
  inData: new Uint8Array([])
}

async function HuksDhAgreeExportKey(keyAlias: string,
  peerPubKey: huks.HuksReturnResult): Promise<huks.HuksReturnResult> {
  const initHandle = await huks.initSession(keyAlias, dhGenOptions)
  const dhAgreeUpdateBobPubKey: huks.HuksOptions = {
    properties: [
      ...dh2048Agree, {
      tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG,
      value: huks.HuksKeyStorageType.HUKS_STORAGE_KEY_EXPORT_ALLOWED,
    }],
    inData: peerPubKey.outData
  }
  await huks.updateSession(initHandle.handle, dhAgreeUpdateBobPubKey)
  return await huks.finishSession(initHandle.handle, emptyOptions)
}

async function HuksDhAgreeExportTest(
  aliasA: string, aliasB: string,
  pubKeyA: huks.HuksReturnResult, pubKeyB: huks.HuksReturnResult) {

  const agreedKeyFromAlice = await HuksDhAgreeExportKey(aliasA, pubKeyB)
  console.info(`ok! agreedKeyFromAlice export is 0x${Uint8ArrayToBigInt(agreedKeyFromAlice.outData).toString(16)}`)

  const agreedKeyFromBob = await HuksDhAgreeExportKey(aliasB, pubKeyA)
  console.info(`ok! agreedKeyFromBob export is 0x${Uint8ArrayToBigInt(agreedKeyFromBob.outData).toString(16)}`)
}

async function HuksDhAgreeInHuks(keyAlias: string, peerPubKey: huks.HuksReturnResult,
  aliasAgreedKey: string): Promise<huks.HuksReturnResult> {
  const onlyUsedInHuks: Array<huks.HuksParam> = [{
    tag: huks.HuksTag.HUKS_TAG_KEY_STORAGE_FLAG,
    value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS,
  }, {
    tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG,
    value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS,
  }]
  const dhAgreeInit: huks.HuksOptions = {
    properties: [
      ...dhAgree,
      { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, },
      ...onlyUsedInHuks],
    inData: new Uint8Array([])
  }
  const dhAgreeFinishParams: Array<huks.HuksParam> = [
    ...onlyUsedInHuks,
    { tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, value: true },
    { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES },
    { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 },
    {
      tag: huks.HuksTag.HUKS_TAG_PURPOSE,
      value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
    }]

  const handle = await huks.initSession(keyAlias, dhAgreeInit)
  const dhAgreeUpdatePubKey: huks.HuksOptions = {
    properties: [...dhAgree, ...onlyUsedInHuks],
    inData: peerPubKey.outData
  }
  await huks.updateSession(handle.handle, dhAgreeUpdatePubKey)
  const dhAgreeAliceFinnish: huks.HuksOptions = {
    properties: [...dhAgreeFinishParams, {
      tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, value: StringToUint8Array(aliasAgreedKey)
    }], inData: new Uint8Array([])
  }
  return await huks.finishSession(handle.handle, dhAgreeAliceFinnish)
}

async function HuksDhAgreeInHuksTest(
  aliasA: string, aliasB: string,
  pubKeyA: huks.HuksReturnResult, pubKeyB: huks.HuksReturnResult,
  aliasAgreedKeyFromA: string, aliasAgreedKeyFromB: string) {

  const finishAliceResult = await HuksDhAgreeInHuks(aliasA, pubKeyB, aliasAgreedKeyFromA)
  console.info(`ok! finishAliceResult in huks is 0x${Uint8ArrayToBigInt(finishAliceResult.outData).toString(16)}`)
  const aliceAgreedExist = await huks.isKeyItemExist(aliasAgreedKeyFromA, emptyOptions)
  console.info(`ok! aliceAgreedExist in huks is ${aliceAgreedExist}`)

  const finishBobResult = await HuksDhAgreeInHuks(aliasB, pubKeyA, aliasAgreedKeyFromB)
  console.info(`ok! finishBobResult in huks is 0x${Uint8ArrayToBigInt(finishBobResult.outData).toString(16)}`)
  const bobAgreedExist = await huks.isKeyItemExist(aliasAgreedKeyFromB, emptyOptions)
  console.info(`ok! bobAgreedExist in huks is ${bobAgreedExist}`)

  await huks.deleteKeyItem(aliasAgreedKeyFromA, emptyOptions)
  await huks.deleteKeyItem(aliasAgreedKeyFromB, emptyOptions)
}

export default async function HuksDhAgreeTest() {
  const aliasAlice = 'alice'
  const aliasBob = 'bob'

  /* 调用generateKeyItem生成别名为alice与bob的两个密钥 */
  await huks.generateKeyItem(aliasAlice, dhGenOptions)
  await huks.generateKeyItem(aliasBob, dhGenOptions)

  /* 导出非对称密钥alice与bob的的公钥 */
  const pubKeyAlice = await huks.exportKeyItem(aliasAlice, emptyOptions)
  const pubKeyBob = await huks.exportKeyItem(aliasBob, emptyOptions)

  /* 开始协商,协商生成的密钥返回给业务管理 */
  await HuksDhAgreeExportTest(aliasAlice, aliasBob, pubKeyAlice, pubKeyBob)

  /* 开始协商,协商生成的密钥由HUKS管理 */
  await HuksDhAgreeInHuksTest(aliasAlice, aliasBob, pubKeyAlice, pubKeyBob, 'agreedKeyFromAlice', 'agreedKeyFromBob')

  await huks.deleteKeyItem(aliasAlice, emptyOptions)
  await huks.deleteKeyItem(aliasBob, emptyOptions)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1912303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STL--栈(stack)

stack 栈是一种只在一端(栈顶)进行数据插入(入栈)和删除(出栈)的数据结构,它满足后进先出(LIFO)的特性。 使用push(入栈)将数据放入stack,使用pop(出栈)将元素从容器中移除。 使用stack,必须包含头文件: #include<stack>在头文件中,class stack定义如下: namespace std…

前端面试题32(浅谈前端热部署)

前端热部署&#xff08;Hot Deployment&#xff09;是指在开发过程中&#xff0c;当开发者修改了前端代码后&#xff0c;这些修改能够立即反映到正在运行的前端应用上&#xff0c;而无需重新启动整个应用或服务器。这种能力极大地提高了开发效率&#xff0c;因为开发者可以即时…

算法学习笔记(8.2)-动态规划入门进阶

目录 问题判断: 问题求解步骤&#xff1a; 图例&#xff1a; 解析&#xff1a; 方法一&#xff1a;暴力搜索 实现代码如下所示&#xff1a; 解析&#xff1a; 方法二&#xff1a;记忆化搜索 代码示例&#xff1a; 解析&#xff1a; 方法三&#xff1a;动态规划 空间…

如何在JetBrains中写Codeforce?

目录 前言 正文 leetcode 个人喜好 参考资料 具体操作步骤 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ningbo China&#x1f4…

硬件:CPU和GPU

一、CPU与GPU 二、提升CPU利用率&#xff1a;计组学过的 1、超线程一般是给不一样的任务的计算使用&#xff0c;而非在计算密集型工作中 2、Cpu一次可以计算一个线程&#xff0c;而gpu有多少个绿点一次就能计算多少个线程&#xff0c;Gpu比cpu快是因为gpu它的核多&#xff0c;…

如何在 PostgreSQL 中确保数据的异地备份安全性?

文章目录 一、备份策略1. 全量备份与增量备份相结合2. 定义合理的备份周期3. 选择合适的备份时间 二、加密备份数据1. 使用 PostgreSQL 的内置加密功能2. 使用第三方加密工具 三、安全的传输方式1. SSH 隧道2. SFTP3. VPN 连接 四、异地存储的安全性1. 云存储服务2. 内部存储设…

RK3568------Openharmony 4.0-Release WIFI/BT模组适配

RK3568------Openharmony 4.0-Release WIFI/BT模组(ap6236)适配 文章目录 RK3568------Openharmony 4.0-Release WIFI/BT模组(ap6236)适配前言一、驱动移植二、设备树配置三 、内核配置四、遇到的问题五、效果展示总结 前言 随着RK3568适配工作的推进&#xff0c;整体适配工作…

泛微E9开发 控制日期浏览按钮的可选日期范围

控制日期浏览按钮的可选日期范围 1、需求说明2、实现方法3、扩展知识点控制日期浏览按钮的可选日期范围格式参数说明演示 1、需求说明 控制日期浏览按钮的可选日期范围为2024/07/01~2024/07/31&#xff0c;如下图所示 2. 控制日期浏览按钮的可选日期范围在当前时间的前一周~当…

【基于R语言群体遗传学】-13-群体差异量化-Fst

在前几篇博客中&#xff0c;我们深度学习讨论了适应性进化的问题&#xff0c;从本篇博客开始&#xff0c;我们关注群体差异的问题&#xff0c;建议大家可以先看之前的博客&#xff1a;群体遗传学_tRNA做科研的博客-CSDN博客 一些新名词 Meta-population:An interconnected gro…

4:表单和通用视图

表单和通用视图 1、编写一个简单的表单&#xff08;1&#xff09;更新polls/detail.html文件 使其包含一个html < form > 元素&#xff08;2&#xff09;创建一个Django视图来处理提交的数据&#xff08;3&#xff09;当有人对 Question 进行投票后&#xff0c;vote()视图…

Proteus + Keil单片机仿真教程(五)多位LED数码管的静态显示

Proteus + Keil单片机仿真教程(五)多位LED数码管 上一章节讲解了单个数码管的静态和动态显示,这一章节将对多个数码管的静态显示进行学习,本章节主要难点: 1.锁存器的理解和使用; 2.多个数码管的接线封装方式; 3.Proteus 快速接头的使用。 第一个多位数码管示例 元件…

谷歌云 | Gemini 大模型赋能 BigQuery 情感分析:解码客户评论,洞悉市场风向

情感分析是企业洞察客户需求和改进产品服务的重要工具。近年来&#xff0c;随着自然语言处理 (NLP) 技术的飞速发展&#xff0c;情感分析变得更加精准高效。Google 推出的 Gemini 模型&#xff0c;作为大型语言模型 (LLM) 的代表&#xff0c;拥有强大的文本处理能力&#xff0c…

共生与变革:AI在开发者世界的角色深度剖析

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已不再是遥不可及的概念&#xff0c;而是逐步渗透到我们工作与生活的每一个角落。对于开发者这一群体而言&#xff0c;AI的崛起既带来了前所未有的机遇&#xff0c;也引发了关于其角色定位的深刻讨论——AI…

electron在VSCode和IDEA及webStrom等编辑器控制台打印日志乱码

window10环境下设置 1.打开Windows设置 2.打开时间和语言&#xff0c;选择语言菜单、如何点击管理语言设置 3.打开之后选择管理&#xff0c;选择更改系统区域设置&#xff0c;把Beta版&#xff1a;使用Unicode UTF-8提供全球语言支持 勾上&#xff0c;点击确定&#xff0c;…

氛围感视频素材高级感的去哪里找啊?带氛围感的素材网站库分享

亲爱的创作者们&#xff0c;大家好&#xff01;今天我们来聊聊视频创作中至关重要的一点——氛围感。一个好的视频&#xff0c;不仅要有视觉冲击力&#xff0c;还要能够触动观众的情感。那我们应该去哪里寻找这些充满氛围感且高级的视频素材呢&#xff1f;别急&#xff0c;我这…

isaac sim 与 WLS2 ros2实现通信

Omniverse以及isaac还是windows下使用顺手一点&#xff0c;但是做跟ros相关的开发时候&#xff0c;基本就得迁移到ubuntu下了&#xff0c;windows下ros安装还是过于复杂&#xff0c;那不想用双系统或者ubuntu或者虚拟机&#xff0c;有啥别的好方法呢&#xff1f;这里想到了wind…

希喂、鲜朗和牧野奇迹主食冻干怎么样?第一次喂冻干哪款更好

我是个宠物医生&#xff0c;每天很长时间都在跟猫猫狗狗打交道&#xff0c;送到店里来的猫猫状态几乎是一眼就能看出来&#xff0c;肥胖、肝损伤真是现在大部分家养猫正面临的&#xff0c;靠送医治疗只能减缓无法根治&#xff0c;根本在于铲屎官的喂养方式。 从业这几年&#…

打开ps提示dll文件丢失如何解决?教你几种靠谱的方法

在日常使用电脑过程中&#xff0c;由于不当操作&#xff0c;dll文件丢失是一种常见现象。当dll文件丢失时&#xff0c;程序将无法正常运行&#xff0c;比如ps&#xff0c;pr等待软件。此时&#xff0c;我们需要对其进行修复以恢复其功能&#xff0c;下面我们一起来了解一下出现…

EtherCAT总线冗余让制造更安全更可靠更智能

冗余定义 什么是总线冗余功能&#xff1f;我们都知道&#xff0c;EtherCAT现场总线具有灵活的拓扑结构&#xff0c;设备间支持线型、星型、树型的连接方式&#xff0c;其中线型结构简单、传输效率高&#xff0c;大多数的现场应用中也是使用这种连接方式&#xff0c;如下图所示…

MiniGPT-Med 通用医学视觉大模型:生成医学报告 + 视觉问答 + 医学疾病识别

MiniGPT-Med 通用医学视觉大模型&#xff1a;生成医学报告 视觉问答 医学疾病识别 提出背景解法拆解 论文&#xff1a;https://arxiv.org/pdf/2407.04106 代码&#xff1a;https://github.com/Vision-CAIR/MiniGPT-Med 提出背景 近年来&#xff0c;人工智能&#xff08;AI…