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

密钥协商(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.niftyadmin.cn/n/5546935.html

相关文章

mysql实战入门-基础篇

目录 1、MySQL概述 1.1、数据库相关概念 1.2、MySQL数据库 1.2.1、版本 1.2.2、下载 1.2.3、安装 输入MySQL中root用户的密码,一定记得记住该密码 1.2.4、启动停止 1.2.5、客户端连接 1.2.6、数据模型 2、SQL 2.1、SQL通用语法 2.2、SQL分类 2.3、DDL 2.3.1、数据…

谷粒商城学习笔记-踩坑合集

1&#xff0c;Idea新增Module报错&#xff1a;sdk ‘1.8‘ type ‘JavaSDK‘ is not registered in ProjectJdkTable 2&#xff0c;IDEA创建Spring项目无法使用Java8的解决方案 3&#xff0c;谷粒商城-记录创建工程和模块时遇到的两个问题 4&#xff0c;谷粒商城学习笔记-使用r…

python拆分Excel数据,自动发邮箱

import pandas as pd import poplib import email from email.header import decode_header from email.parser import Parser df = pd.read_excel("年假明细表.xlsx") depts = df["部门"].unique() for dept in depts: department_df = df[df[&q…

linux之管道重定向

管道与重定向 一、重定向 将原输出结果存储到其他位置的过程 标准输入、标准正确输出、标准错误输出 ​ 进程在运行的过程中根据需要会打开多个文件&#xff0c;每打开一个文件会有一个数字标识。这个标识叫文件描述符。 进程使用文件描述符来管理打开的文件&#xff08;FD--…

探索HTML5的设计原则:引领Web开发的未来方向

随着互联网的飞速发展&#xff0c;HTML5作为Web技术的核心标准之一&#xff0c;不仅极大地丰富了网页的表现力和交互性&#xff0c;还推动了Web应用向更加动态、高效、安全的方向迈进。HTML5的设计原则&#xff0c;体现了对用户体验、内容可访问性、跨平台兼容性以及未来可扩展…

去除Win32 Tab Control控件每个选项卡上的深色对话框背景

一般情况下&#xff0c;我们是用不带边框的对话框来充当Tab Control的每个选项卡的内容的。 例如&#xff0c;主对话框IDD_TABBOX上有一个Tab Control&#xff0c;上面有两个选项卡&#xff0c;第一个选项卡用的是IDD_DIALOG1充当内容&#xff0c;第二个用的则是IDD_DIALOG2。I…

接口测试(3)

接口自动化 # 获取图片验证码import requestsresponse requests.get(url"http://kdtx-test.itheima.net/api/captchaImage")print(response.status_code) print(response.text) import requestsurl "http://kdtx-test.itheima.net/api/login" header_da…

Jmeter进阶-接口自动化

JMeter 是一款广泛使用的开源软件,它不仅可以用于性能测试,还可以用于接口自动化测试。以下是 JMeter 进行接口自动化测试的一些优缺点: 优点: 易用性:JMeter 上手简单,大部分操作都有对应的元件帮助完成,开源且社区接受度高 。灵活性:提供 BeanShell 脚本能力,允许自…