1# 密钥协商(ArkTS) 2 3<!--Kit: Universal Keystore Kit--> 4<!--Subsystem: Security--> 5<!--Owner: @wutiantian-gitee--> 6<!--Designer: @HighLowWorld--> 7<!--Tester: @wxy1234564846--> 8<!--Adviser: @zengyawen--> 9 10以X25519和DH两个协商密钥类型为例,在密钥由HUKS管理的情况下,完成密钥协商。具体的场景介绍及支持的算法规格,请参考[密钥生成支持的算法](huks-key-generation-overview.md#支持的算法)。 11 12## 开发步骤 13 14**生成密钥** 15 16设备A、设备B各自生成一个非对称密钥,具体请参考[密钥生成](huks-key-generation-overview.md)或[密钥导入](huks-key-import-overview.md)。 17 18密钥生成时,可指定参数[HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG](../../reference/apis-universal-keystore-kit/capi-native-huks-type-h.md#oh_huks_keystoragetype)(可选),用于标识基于该密钥协商出的密钥是否由HUKS管理。 19 20- 当TAG设置为HUKS_STORAGE_ONLY_USED_IN_HUKS时,表示基于该密钥协商出的密钥,由HUKS管理,可保证协商密钥全生命周期不出安全环境。 21 22- 当TAG设置为HUKS_STORAGE_KEY_EXPORT_ALLOWED时,表示基于该密钥协商出的密钥,返回给调用方管理,由业务自行保证密钥安全。 23 24- 若业务未设置TAG的具体值,表示基于该密钥协商出的密钥,可由HUKS管理,也可返回给调用方管理,业务可在后续协商时再选择使用何种方式保护密钥。 25 26**导出密钥** 27 28设备A、B导出非对称密钥对的公钥材料,具体请参考[密钥导出](huks-export-key-arkts.md)。 29 30**密钥协商** 31 32设备A、B分别基于本端私钥和对端设备的公钥,协商出共享密钥。 33 34密钥协商时,可指定参数HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG(可选),用于标识协商得到的密钥是否由HUKS管理。 35 36| 生成 | 协商 | 规格 | 37| -------- | -------- | -------- | 38| HUKS_STORAGE_ONLY_USED_IN_HUKS | HUKS_STORAGE_ONLY_USED_IN_HUKS | 密钥由HUKS管理 | 39| HUKS_STORAGE_KEY_EXPORT_ALLOWED | HUKS_STORAGE_KEY_EXPORT_ALLOWED | 密钥返回给调用方管理 | 40| 未指定TAG具体值 | HUKS_STORAGE_ONLY_USED_IN_HUKS | 密钥由HUKS管理 | 41| 未指定TAG具体值 | HUKS_STORAGE_KEY_EXPORT_ALLOWED | 密钥返回给调用方管理 | 42| 未指定TAG具体值 | 未指定TAG具体值 | 密钥返回给调用方管理 | 43 44注:协商时指定的TAG值,不可与生成时指定的TAG值冲突。表格中仅列举有效的指定方式。 45 46**删除密钥** 47 48当密钥废弃不用时,设备A、B均需要删除密钥,具体请参考[密钥删除](huks-delete-key-arkts.md)。 49 50 51下面分别以X25519 与 DH密钥为例,进行协商。 52### X25519非对称密钥协商用例 53```ts 54/* 55 * 以下以X25519密钥的Promise操作使用为例 56 */ 57import { huks } from '@kit.UniversalKeystoreKit'; 58import { BusinessError } from "@kit.BasicServicesKit"; 59 60function StringToUint8Array(str: string) { 61 let arr: number[] = new Array(); 62 for (let i = 0, j = str.length; i < j; ++i) { 63 arr.push(str.charCodeAt(i)); 64 } 65 return new Uint8Array(arr); 66} 67 68function Uint8ArrayToString(fileData: Uint8Array) { 69 let dataString = ''; 70 for (let i = 0; i < fileData.length; i++) { 71 dataString += String.fromCharCode(fileData[i]); 72 } 73 return dataString; 74} 75 76/* 77 * 确定密钥别名和封装密钥属性参数集 78 */ 79let srcKeyAliasFirst = "AgreeX25519KeyFirstAlias"; 80let srcKeyAliasSecond = "AgreeX25519KeySecondAlias"; 81let agreeX25519InData = 'AgreeX25519TestIndata'; 82let finishOutData: Uint8Array; 83let handle: number; 84let exportKey: Uint8Array; 85let exportKeyFirst: Uint8Array; 86let exportKeySecond: Uint8Array; 87/* 集成生成密钥参数集 */ 88let properties: Array<huks.HuksParam> = [{ 89 tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 90 value: huks.HuksKeyAlg.HUKS_ALG_X25519, 91 }, { 92 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 93 value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE, 94 }, { 95 tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, 96 value: huks.HuksKeySize.HUKS_CURVE25519_KEY_SIZE_256, 97 }, { 98 tag: huks.HuksTag.HUKS_TAG_DIGEST, 99 value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, 100 }, { 101 tag: huks.HuksTag.HUKS_TAG_PADDING, 102 value: huks.HuksKeyPadding.HUKS_PADDING_NONE, 103 }, { 104 tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, 105 value: huks.HuksCipherMode.HUKS_MODE_CBC, 106 }, { 107 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 108 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 109 } 110]; 111let HuksOptions: huks.HuksOptions = { 112 properties: properties, 113 inData: new Uint8Array(new Array()) 114} 115/* 集成第一个协商参数集 */ 116const finishProperties: Array<huks.HuksParam> = [{ 117 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 118 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 119 }, { 120 tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, 121 value: true 122 }, { 123 tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 124 value: huks.HuksKeyAlg.HUKS_ALG_AES, 125 }, { 126 tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, 127 value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, 128 }, { 129 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 130 value: 131 huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | 132 huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT, 133 }, { 134 tag: huks.HuksTag.HUKS_TAG_DIGEST, 135 value: huks.HuksKeyDigest.HUKS_DIGEST_NONE, 136 }, { 137 tag: huks.HuksTag.HUKS_TAG_PADDING, 138 value: huks.HuksKeyPadding.HUKS_PADDING_NONE, 139 }, { 140 tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, 141 value: huks.HuksCipherMode.HUKS_MODE_ECB, 142 } 143]; 144let finishOptionsFirst: huks.HuksOptions = { 145 properties: [ 146 ...finishProperties, { 147 tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, 148 value: StringToUint8Array(srcKeyAliasFirst + 'final'), 149 }], 150 inData: StringToUint8Array(agreeX25519InData) 151} 152/* 集成第二个协商参数集 */ 153let finishOptionsSecond: huks.HuksOptions = { 154 properties: [ 155 ...finishProperties, { 156 tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, 157 value: StringToUint8Array(srcKeyAliasSecond + 'final'), 158 }], 159 inData: StringToUint8Array(agreeX25519InData) 160} 161 162/* 生成密钥 */ 163async function generateKeyItem(keyAlias: string, huksOptions: huks.HuksOptions) { 164 console.info("promise: enter generateKeyItem"); 165 try { 166 await huks.generateKeyItem(keyAlias, huksOptions) 167 .then(() => { 168 console.info(`promise: generateKeyItem success`); 169 }).catch((error: BusinessError) => { 170 console.error(`promise: generateKeyItem failed, errCode : ${error.code}, errMsg : ${error.message}`); 171 }) 172 } catch (error) { 173 console.error(`promise: generateKeyItem input arg invalid`); 174 } 175} 176 177/* 初始化密钥会话接口,并获取一个句柄(必选)和挑战值(可选) */ 178async function initSession(keyAlias: string, huksOptions: huks.HuksOptions) { 179 console.info("promise: enter initSession"); 180 try { 181 await huks.initSession(keyAlias, huksOptions) 182 .then((data) => { 183 handle = data.handle; 184 console.info(`promise: initSession success`); 185 }).catch((error: BusinessError) => { 186 console.error(`promise: initSession failed, errCode : ${error.code}, errMsg : ${error.message}`); 187 }) 188 } catch (error) { 189 console.error(`promise: initSession input arg invalid`); 190 } 191} 192 193/* 分段添加密钥操作的数据并进行相应的密钥操作,输出处理数据 */ 194async function updateSession(handle: number, huksOptions: huks.HuksOptions) { 195 console.info("promise: enter updateSession"); 196 try { 197 await huks.updateSession(handle, huksOptions) 198 .then((data) => { 199 console.info(`promise: updateSession success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); 200 }).catch((error: BusinessError) => { 201 console.error(`promise: updateSession failed, errCode : ${error.code}, errMsg : ${error.message}`); 202 }) 203 } catch (error) { 204 console.error(`promise: updateSession input arg invalid`); 205 } 206} 207 208/* 结束密钥会话并进行相应的密钥操作,输出处理数据 */ 209async function finishSession(handle: number, huksOptions: huks.HuksOptions) { 210 console.info("promise: enter finishSession"); 211 try { 212 await huks.finishSession(handle, huksOptions) 213 .then((data) => { 214 finishOutData = data.outData as Uint8Array; 215 console.info(`promise: finishSession success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); 216 }).catch((error: BusinessError) => { 217 console.error(`promise: finishSession failed, errCode : ${error.code}, errMsg : ${error.message}`); 218 }) 219 } catch (error) { 220 console.error(`promise: finishSession input arg invalid`); 221 } 222} 223 224/* 导出密钥 */ 225async function exportKeyItem(keyAlias: string, huksOptions: huks.HuksOptions) { 226 console.info("promise: enter exportKeyItem"); 227 try { 228 await huks.exportKeyItem(keyAlias, huksOptions) 229 .then((data) => { 230 exportKey = data.outData as Uint8Array; 231 console.info(`promise: exportKey success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); 232 }).catch((error: BusinessError) => { 233 console.error(`promise: exportKeyItem failed, errCode : ${error.code}, errMsg : ${error.message}`); 234 }) 235 } catch (error) { 236 console.error(`promise: exportKeyItem input arg invalid`); 237 } 238} 239 240/* 删除密钥操作 */ 241async function deleteKeyItem(keyAlias: string, huksOptions: huks.HuksOptions) { 242 console.info("promise: enter deleteKeyItem"); 243 try { 244 await huks.deleteKeyItem(keyAlias, huksOptions) 245 .then(() => { 246 console.info(`promise: deleteKeyItem success`); 247 }).catch((error: BusinessError) => { 248 console.error(`promise: deleteKeyItem failed, errCode : ${error.code}, errMsg : ${error.message}`); 249 }) 250 } catch (error) { 251 console.error(`promise: deleteKeyItem input arg invalid`); 252 } 253} 254 255async function testAgree() { 256 /* 1.确定密钥别名并集成要参数集。A设备:srcKeyAliasFirst;B设备:srcKeyAliasSecond */ 257 /* 2.设备A生成密钥 */ 258 await generateKeyItem(srcKeyAliasFirst, HuksOptions); 259 /* 3.设备B生成密钥 */ 260 await generateKeyItem(srcKeyAliasSecond, HuksOptions); 261 /* 4.设备A、B导出非对称密钥的公钥 */ 262 await exportKeyItem(srcKeyAliasFirst, HuksOptions); 263 exportKeyFirst = exportKey; 264 await exportKeyItem(srcKeyAliasSecond, HuksOptions); 265 exportKeySecond = exportKey; 266 /* 5.对第一个密钥进行协商(三段式) */ 267 await initSession(srcKeyAliasFirst, HuksOptions); 268 HuksOptions.inData = exportKeySecond; 269 await updateSession(handle, HuksOptions); 270 await finishSession(handle, finishOptionsFirst); 271 /* 6.对第二个密钥进行协商(三段式) */ 272 await initSession(srcKeyAliasSecond, HuksOptions); 273 HuksOptions.inData = exportKeyFirst; 274 await updateSession(handle, HuksOptions); 275 await finishSession(handle, finishOptionsSecond); 276 /* 7.设备A、B删除密钥 */ 277 await deleteKeyItem(srcKeyAliasFirst, HuksOptions); 278 await deleteKeyItem(srcKeyAliasSecond, HuksOptions); 279} 280``` 281 282### DH密钥协商用例 283```ts 284/* 285 * 下面以DH密钥的Promise操作使用为例 286 */ 287import { huks } from '@kit.UniversalKeystoreKit'; 288 289function StringToUint8Array(str: string) { 290 let arr: number[] = [] 291 for (let i = 0, j = str.length; i < j; ++i) { 292 arr.push(str.charCodeAt(i)) 293 } 294 return new Uint8Array(arr) 295} 296 297function Uint8ArrayToBigInt(arr: Uint8Array): bigint { 298 let i = 0 299 const byteMax: bigint = BigInt('0x100') 300 let result: bigint = BigInt('0') 301 while (i < arr.length) { 302 result = result * byteMax 303 result = result + BigInt(arr[i]) 304 i += 1 305 } 306 return result 307} 308 309const dhAgree: Array<huks.HuksParam> = [{ 310 tag: huks.HuksTag.HUKS_TAG_ALGORITHM, 311 value: huks.HuksKeyAlg.HUKS_ALG_DH, 312}, { 313 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 314 value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_AGREE, 315}] 316const dh2048Agree: Array<huks.HuksParam> = [ 317 ...dhAgree, { 318 tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, 319 value: huks.HuksKeySize.HUKS_DH_KEY_SIZE_2048, 320}] 321const dhGenOptions: huks.HuksOptions = { 322 properties: dh2048Agree, 323 inData: new Uint8Array([]) 324} 325const emptyOptions: huks.HuksOptions = { 326 properties: [], 327 inData: new Uint8Array([]) 328} 329 330async function HuksDhAgreeExportKey(keyAlias: string, 331 peerPubKey: huks.HuksReturnResult): Promise<huks.HuksReturnResult> { 332 const initHandle = await huks.initSession(keyAlias, dhGenOptions) 333 const dhAgreeUpdateBobPubKey: huks.HuksOptions = { 334 properties: [ 335 ...dh2048Agree, { 336 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 337 value: huks.HuksKeyStorageType.HUKS_STORAGE_KEY_EXPORT_ALLOWED, 338 }], 339 inData: peerPubKey.outData 340 } 341 await huks.updateSession(initHandle.handle, dhAgreeUpdateBobPubKey) 342 return await huks.finishSession(initHandle.handle, emptyOptions) 343} 344 345async function HuksDhAgreeExportTest( 346 aliasA: string, aliasB: string, 347 pubKeyA: huks.HuksReturnResult, pubKeyB: huks.HuksReturnResult) { 348 349 const agreedKeyFromAlice = await HuksDhAgreeExportKey(aliasA, pubKeyB) 350 console.info(`ok! agreedKeyFromAlice export is 0x${Uint8ArrayToBigInt(agreedKeyFromAlice.outData).toString(16)}`) 351 352 const agreedKeyFromBob = await HuksDhAgreeExportKey(aliasB, pubKeyA) 353 console.info(`ok! agreedKeyFromBob export is 0x${Uint8ArrayToBigInt(agreedKeyFromBob.outData).toString(16)}`) 354} 355 356async function HuksDhAgreeInHuks(keyAlias: string, peerPubKey: huks.HuksReturnResult, 357 aliasAgreedKey: string): Promise<huks.HuksReturnResult> { 358 const onlyUsedInHuks: Array<huks.HuksParam> = [{ 359 tag: huks.HuksTag.HUKS_TAG_KEY_STORAGE_FLAG, 360 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 361 }, { 362 tag: huks.HuksTag.HUKS_TAG_DERIVED_AGREED_KEY_STORAGE_FLAG, 363 value: huks.HuksKeyStorageType.HUKS_STORAGE_ONLY_USED_IN_HUKS, 364 }] 365 const dhAgreeInit: huks.HuksOptions = { 366 properties: [ 367 ...dhAgree, 368 { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256, }, 369 ...onlyUsedInHuks], 370 inData: new Uint8Array([]) 371 } 372 const dhAgreeFinishParams: Array<huks.HuksParam> = [ 373 ...onlyUsedInHuks, 374 { tag: huks.HuksTag.HUKS_TAG_IS_KEY_ALIAS, value: true }, 375 { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, 376 { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 }, 377 { 378 tag: huks.HuksTag.HUKS_TAG_PURPOSE, 379 value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT 380 } 381 ] 382 383 const handle = await huks.initSession(keyAlias, dhAgreeInit) 384 const dhAgreeUpdatePubKey: huks.HuksOptions = { 385 properties: [...dhAgree, ...onlyUsedInHuks], 386 inData: peerPubKey.outData 387 } 388 await huks.updateSession(handle.handle, dhAgreeUpdatePubKey) 389 const dhAgreeAliceFinnish: huks.HuksOptions = { 390 properties: [...dhAgreeFinishParams, { 391 tag: huks.HuksTag.HUKS_TAG_KEY_ALIAS, value: StringToUint8Array(aliasAgreedKey) 392 }], inData: new Uint8Array([]) 393 } 394 return await huks.finishSession(handle.handle, dhAgreeAliceFinnish) 395} 396 397async function HuksDhAgreeInHuksTest( 398 aliasA: string, aliasB: string, 399 pubKeyA: huks.HuksReturnResult, pubKeyB: huks.HuksReturnResult, 400 aliasAgreedKeyFromA: string, aliasAgreedKeyFromB: string) { 401 402 const finishAliceResult = await HuksDhAgreeInHuks(aliasA, pubKeyB, aliasAgreedKeyFromA) 403 console.info(`ok! finishAliceResult in huks is 0x${Uint8ArrayToBigInt(finishAliceResult.outData).toString(16)}`) 404 const aliceAgreedExist = await huks.isKeyItemExist(aliasAgreedKeyFromA, emptyOptions) 405 console.info(`ok! aliceAgreedExist in huks is ${aliceAgreedExist}`) 406 407 const finishBobResult = await HuksDhAgreeInHuks(aliasB, pubKeyA, aliasAgreedKeyFromB) 408 console.info(`ok! finishBobResult in huks is 0x${Uint8ArrayToBigInt(finishBobResult.outData).toString(16)}`) 409 const bobAgreedExist = await huks.isKeyItemExist(aliasAgreedKeyFromB, emptyOptions) 410 console.info(`ok! bobAgreedExist in huks is ${bobAgreedExist}`) 411 412 await huks.deleteKeyItem(aliasAgreedKeyFromA, emptyOptions) 413 await huks.deleteKeyItem(aliasAgreedKeyFromB, emptyOptions) 414} 415 416async function HuksDhAgreeTest() { 417 const aliasAlice = 'alice' 418 const aliasBob = 'bob' 419 420 /* 调用generateKeyItem生成别名为alice与bob的两个密钥 */ 421 await huks.generateKeyItem(aliasAlice, dhGenOptions) 422 await huks.generateKeyItem(aliasBob, dhGenOptions) 423 424 /* 导出非对称密钥alice与bob的公钥 */ 425 const pubKeyAlice = await huks.exportKeyItem(aliasAlice, emptyOptions) 426 const pubKeyBob = await huks.exportKeyItem(aliasBob, emptyOptions) 427 428 /* 开始协商,协商生成的密钥返回给业务管理 */ 429 await HuksDhAgreeExportTest(aliasAlice, aliasBob, pubKeyAlice, pubKeyBob) 430 431 /* 开始协商,协商生成的密钥由HUKS管理 */ 432 await HuksDhAgreeInHuksTest(aliasAlice, aliasBob, pubKeyAlice, pubKeyBob, 'agreedKeyFromAlice', 'agreedKeyFromBob') 433 434 await huks.deleteKeyItem(aliasAlice, emptyOptions) 435 await huks.deleteKeyItem(aliasBob, emptyOptions) 436} 437```