HarmonyOS Developer 通用密钥库开发指导
密钥访问控制
HUKS提供了全面完善的密钥访问控制能力,确保存储在HUKS中的密钥被合法正确的访问。
首先,业务只能访问属于自己的密钥,即只能访问通过HUKS生成或导入的密钥。
其次,支持密钥的用户身份认证访问控制,对于高安敏感的业务密钥,需要在使用密钥的时候,再次要求用户即时的验证锁屏密码或生物特征,验证通过后,才能使用业务密钥。
除此之外,HUKS还支持严格限制密钥的使用用途,如支持只允许AES密钥进行加密解密,只允许RSA密钥进行签名验签。
用户身份认证访问控制
生成或导入密钥时,可以指定密钥必须经过用户身份认证后才能使用。您可以指定用于解锁设备锁屏的凭据(指纹、人脸)的子集进行身份认证。在生成/导入密钥后,即使应用进程被攻击也不会导致未经用户授权的密钥访问,一般用于高敏感且高级别安全保护的场景,比如免密登录、免密支付、自动填充密码保护场景。
除用户身份认证外,应用还须将密钥的授权访问类型(即失效条件)设置为以下两种类型之一:
- 清除锁屏密码后密钥永久无效。设置此模式的前提是当前用户已经设置了锁屏密码,在生成/导入密钥后,一旦清除了锁屏密码,此类密钥将永久失效。注意,修改密码不会导致失效情况发生。此模式适合那些需要锁屏密码授权访问或用户强相关的数据保护的场景。
- 用户新录入生物特征后永久无效。此模式需要当前用户至少录入了一个生物特征(如指纹)才能生效,在生成/导入密钥后,一旦录入新的生物特征,这些密钥将永久失效。注意,仅删除生物特征不会导致失效情况发生。如果您不希望新录入的生物特征后,用户还可以授权访问原有数据(密钥保护的数据),那么可以使用此模式,如免密登录,免密支付等场景。
此外,为了保证密钥使用时用户认证结果的有效性(不可重放),HUKS支持挑战值校验:在身份认证前,需要从HUKS获取挑战值(调用huks.initSession()返回的HuksSessionHandle中)传给用户身份认证方法(userIAM_userAuth.getAuthInstance),然后在密钥操作时校验认证令牌的挑战值。
开发流程
设置了二次用户身份认证的密钥,需要先初始化密钥会话并获取挑战值,然后将HUKS生成的挑战值传至用户身份认证方法进行用户身份认证,认证通过后获取一个认证令牌,将认证令牌传至HUKS进行密钥操作。
接口说明
- 生成或导入密钥时,在密钥属性集中需指定三个参数:用户认证类型HuksUserAuthType、授权访问类型HuksAuthAccessType、挑战值类型HuksChallengeType。
表3用户认证类型:两种类型的子集
名称 | 值 | 说明 |
HUKS_USER_AUTH_TYPE_FINGERPRINT | 0x0001 | 用户认证类型为指纹,允许和人脸同时设置 |
HUKS_USER_AUTH_TYPE_FACE | 0x0002 | 用户认证类型为人脸 ,允许和指纹同时设置 |
表4 安全访问类型:二选一
名称 | 值 | 说明 |
HUKS_AUTH_ACCESS_INVALID_CLEAR_PASSWORD | 1 | 清除锁屏密码后密钥无法访问。 |
HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL | 2 | 新录入生物特征后密钥无法访问,用户认证类型须包含生物认证类型。 |
表5 挑战值类型:三选一
名称 | 值 | 说明 |
HUKS_CHALLENGE_TYPE_NORMAL | 0 | 普通类型,每次密钥的使用需要独立的一次用户认证 |
HUKS_CHALLENGE_TYPE_CUSTOM | 1 | 自定义类型,支持和多个密钥共享一次用户认证 |
HUKS_CHALLENGE_TYPE_NONE | 2 | 无挑战值类型,用户认证时不需要挑战值 |
注意
当指定挑战值类型为HUKS_CHALLENGE_TYPE_NONE 时,不需要传递挑战值,但是存在新的限制:在用户身份认证后,一段时间内允许访问该密钥,超时后不能访问,需要重新认证才能访问。因此应用需要额外指定超时时间HUKS_TAG_AUTH_TIMEOUT属性(最大60秒)。
- 使用密钥时,先初始化密钥会话,然后根据密钥生成/导入阶段指定的挑战值类型属性是否需要获取挑战值,或组装新的挑战值。
- 表6使用密钥的接口介绍
接口名 | 描述 |
initSession(keyAlias: string, options: HuksOptions, callback: AsyncCallback<HuksSessionHandle>) : void | 初始化密钥会话,获取挑战值 |
updateSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback<HuksReturnResult>) : void | 分段操作数据,传递认证令牌 |
finishSession(handle: number, options: HuksOptions, token: Uint8Array, callback: AsyncCallback<HuksReturnResult>) : void | 结束密钥会话,传递认证令牌 |
开发步骤
- 生成密钥并指定指纹访问控制和相关属性
import huks from '@ohos.security.huks';
/*
* 确定密钥别名和封装密钥属性参数集
*/
let keyAlias = 'dh_key_fingerprint_access';
let properties = new Array();
properties[0] = {
tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
value: huks.HuksKeyAlg.HUKS_ALG_SM4,
}
properties[1] = {
tag: huks.HuksTag.HUKS_TAG_PURPOSE,
value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
}
properties[2] = {
tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
}
properties[3] = {
tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
value: huks.HuksCipherMode.HUKS_MODE_CBC,
}
properties[4] = {
tag: huks.HuksTag.HUKS_TAG_PADDING,
value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
}
// 指定密钥身份认证的类型:指纹
properties[5] = {
tag: huks.HuksTag.HUKS_TAG_USER_AUTH_TYPE,
value: huks.HuksUserAuthType.HUKS_USER_AUTH_TYPE_FINGERPRINT
}
// 指定密钥安全授权的类型(失效类型):新录入生物特征(指纹)后无效
properties[6] = {
tag: huks.HuksTag.HUKS_TAG_KEY_AUTH_ACCESS_TYPE,
value: huks.HuksAuthAccessType.HUKS_AUTH_ACCESS_INVALID_NEW_BIO_ENROLL
}
// 指定挑战值的类型:默认类型
properties[7] = {
tag: huks.HuksTag.HUKS_TAG_CHALLENGE_TYPE,
value: huks.HuksChallengeType.HUKS_CHALLENGE_TYPE_NORMAL
}
let huksOptions = {
properties: properties,
inData: new Uint8Array(new Array())
}
/*
* 生成密钥
*/
function generateKeyItem(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) {
return new Promise((resolve, reject) => {
try {
huks.generateKeyItem(keyAlias, huksOptions, function (error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
} catch (error) {
throwObject.isThrow = true;
throw(error);
}
});
}
async function publicGenKeyFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
console.info(`enter callback generateKeyItem`);
let throwObject = {isThrow: false};
try {
await generateKeyItem(keyAlias, huksOptions, throwObject)
.then((data) => {
console.info(`callback: generateKeyItem success, data = ${JSON.stringify(data)}`);
})
.catch(error => {
if (throwObject.isThrow) {
throw(error);
} else {
console.error(`callback: generateKeyItem failed, code: ${error.code}, msg: ${error.message}`);
}
});
} catch (error) {
console.error(`callback: generateKeyItem input arg invalid, code: ${error.code}, msg: ${error.message}`);
}
}
async function TestGenKeyForFingerprintAccessControl() {
await publicGenKeyFunc(keyAlias, huksOptions);
}
- 初始化密钥会话获取挑战值并发起指纹认证获取认证令牌
import huks from '@ohos.security.huks';
import userIAM_userAuth from '@ohos.userIAM.userAuth';
/*
* 确定密钥别名和封装密钥属性参数集
*/
let srcKeyAlias = 'sm4_key_fingerprint_access';
let handle;
let challenge;
let fingerAuthToken;
let authType = userIAM_userAuth.UserAuthType.FINGERPRINT;
let authTrustLevel = userIAM_userAuth.AuthTrustLevel.ATL1;
/* 集成生成密钥参数集 & 加密参数集 */
let properties = new Array();
properties[0] = {
tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
value: huks.HuksKeyAlg.HUKS_ALG_SM4,
}
properties[1] = {
tag: huks.HuksTag.HUKS_TAG_PURPOSE,
value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT,
}
properties[2] = {
tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
}
properties[3] = {
tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
value: huks.HuksCipherMode.HUKS_MODE_CBC,
}
properties[4] = {
tag: huks.HuksTag.HUKS_TAG_PADDING,
value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
}
let huksOptions = {
properties: properties,
inData: new Uint8Array(new Array())
}
function initSession(keyAlias:string, huksOptions:huks.HuksOptions, throwObject) : Promise<huks.HuksSessionHandle> {
return new Promise((resolve, reject) => {
try {
huks.initSession(keyAlias, huksOptions, function (error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
} catch (error) {
throwObject.isThrow = true;
throw(error);
}
});
}
async function publicInitFunc(keyAlias:string, huksOptions:huks.HuksOptions) {
console.info(`enter callback doInit`);
let throwObject = {isThrow: false};
try {
await initSession(keyAlias, huksOptions, throwObject)
.then ((data) => {
console.info(`callback: doInit success, data = ${JSON.stringify(data)}`);
handle = data.handle;
challenge = data.challenge;
})
.catch((error) => {
if (throwObject.isThrow) {
throw(error);
} else {
console.error(`callback: doInit failed, code: ${error.code}, msg: ${error.message}`);
}
});
} catch (error) {
console.error(`callback: doInit input arg invalid, code: ${error.code}, msg: ${error.message}`);
}
}
function userIAMAuthFinger(huksChallenge:Uint8Array) {
// 获取认证对象
let auth;
try {
auth = userIAM_userAuth.getAuthInstance(huksChallenge, authType, authTrustLevel);
console.log("get auth instance success");
} catch (error) {
console.log("get auth instance failed" + error);
}
// 订阅认证结果
try {
auth.on("result", {
callback: (result: userIAM_userAuth.AuthResultInfo) => {
/* 认证成功获取认证令牌 */
fingerAuthToken = result.token;
}
});
console.log("subscribe authentication event success");
} catch (error) {
console.log("subscribe authentication event failed " + error);
}
// 开始认证
try {
auth.start();
console.info("authV9 start auth success");
} catch (error) {
console.info("authV9 start auth failed, error = " + error);
}
}
async function testInitAndAuthFinger() {
/* 初始化密钥会话获取挑战值 */
await publicInitFunc(srcKeyAlias, huksOptions);
/* 调用userIAM进行身份认证 */
userIAMAuthFinger(challenge);
}
- 传入认证令牌进行数据操作
/*
* 以下以SM4 128密钥的Callback操作使用为例
*/
import huks from '@ohos.security.huks';
/*
* 确定密钥别名和封装密钥属性参数集
*/
let srcKeyAlias = 'sm4_key_fingerprint_access';
let IV = '1234567890123456';
let cipherInData = 'Hks_SM4_Cipher_Test_101010101010101010110_string';
let handle;
let fingerAuthToken;
let updateResult = new Array();
let finishOutData;
/* 集成生成密钥参数集 & 加密参数集 */
let propertiesEncrypt = new Array();
propertiesEncrypt[0] = {
tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
value: huks.HuksKeyAlg.HUKS_ALG_SM4,
}
propertiesEncrypt[1] = {
tag: huks.HuksTag.HUKS_TAG_PURPOSE,
value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT,
}
propertiesEncrypt[2] = {
tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
value: huks.HuksKeySize.HUKS_SM4_KEY_SIZE_128,
}
propertiesEncrypt[3] = {
tag: huks.HuksTag.HUKS_TAG_PADDING,
value: huks.HuksKeyPadding.HUKS_PADDING_NONE,
}
propertiesEncrypt[4] = {
tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
value: huks.HuksCipherMode.HUKS_MODE_CBC,
}
propertiesEncrypt[5] = {
tag: huks.HuksTag.HUKS_TAG_IV,
value: StringToUint8Array(IV),
}
let encryptOptions = {
properties: propertiesEncrypt,
inData: new Uint8Array(new Array())
}
function StringToUint8Array(str) {
let arr = [];
for (let i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
function updateSession(handle:number, huksOptions:huks.HuksOptions, token:Uint8Array, throwObject) : Promise<huks.HuksReturnResult> {
return new Promise((resolve, reject) => {
try {
huks.updateSession(handle, huksOptions, token, function (error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
} catch (error) {
throwObject.isThrow = true;
throw(error);
}
});
}
async function publicUpdateFunc(handle:number, token:Uint8Array, huksOptions:huks.HuksOptions) {
console.info(`enter callback doUpdate`);
let throwObject = {isThrow: false};
try {
await updateSession(handle, huksOptions, token, throwObject)
.then ((data) => {
console.info(`callback: doUpdate success, data = ${JSON.stringify(data)}`);
})
.catch(error => {
if (throwObject.isThrow) {
throw(error);
} else {
console.error(`callback: doUpdate failed, code: ${error.code}, msg: ${error.message}`);
}
});
} catch (error) {
console.error(`callback: doUpdate input arg invalid, code: ${error.code}, msg: ${error.message}`);
}
}
function finishSession(handle:number, huksOptions:huks.HuksOptions, token:Uint8Array, throwObject) : Promise<huks.HuksReturnResult> {
return new Promise((resolve, reject) => {
try {
huks.finishSession(handle, huksOptions, token, function (error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
});
} catch (error) {
throwObject.isThrow = true;
throw(error);
}
});
}
async function publicFinishFunc(handle:number, token:Uint8Array, huksOptions:huks.HuksOptions) {
console.info(`enter callback doFinish`);
let throwObject = {isThrow: false};
try {
await finishSession(handle, huksOptions, token, throwObject)
.then ((data) => {
finishOutData = data.outData;
console.info(`callback: doFinish success, data = ${JSON.stringify(data)}`);
})
.catch(error => {
if (throwObject.isThrow) {
throw(error);
} else {
console.error(`callback: doFinish failed, code: ${error.code}, msg: ${error.message}`);
}
});
} catch (error) {
console.error(`callback: doFinish input arg invalid, code: ${error.code}, msg: ${error.message}`);
}
}
async function testSm4Cipher() {
encryptOptions.inData = StringToUint8Array(cipherInData);
/* 传入认证令牌 */
await publicUpdateFunc(handle, fingerAuthToken, encryptOptions);
encryptUpdateResult = updateResult;
encryptOptions.inData = new Uint8Array(new Array());
/* 传入认证令牌 */
await publicFinishFunc(handle, fingerAuthToken, encryptOptions);
if (finishOutData === cipherInData) {
console.info('test finish encrypt err ');
} else {
console.info('test finish encrypt success');
}
}
常见问题
- Cannot find name 'huks'.
不能找到huks,使用了接口函数但没导入security.huks.d.ts,添加import huks from '@ohos.security.huks';即可。 - Property 'finishSession' does not exist on type 'typeof huks'. Did you mean 'finish'?
不能在huks库中找到finishSession,finishSession是API9版本的,请更新SDK版本或替换新版本的security.huks.d.ts文件。
通用密钥库密码算法规格
支持的算法类型及参数组合
导入\生成密钥规格
算法 | API级别 | 支持的密钥长度 |
AES | 8+ | 128、192、256 |
RSA | 8+ | 2048、3072、4096 |
HMAC | 8+ | 8-4096(含),必须是8的倍数 |
ECC | 8+ | 256、384、521 |
ED25519 | 8+ | 256 |
X25519 | 8+ | 256 |
SM2 | 9+ | 256 |
SM3 | 9+ | 256 |
SM4 | 9+ | 128 |
加密解密
算法 | API级别 | 备注 |
AES/CBC/NoPadding AES/CTR/NoPadding AES/GCM/NoPadding AES/CBC/PKCS7 | 8+ | 1. CBC\ECB\CTR模式IV参数必选 2. GCM模式下Nonce、AAD、AEAD参数必选 |
RSA/ECB/NoPadding RSA/ECB/PKCS1_V1_5 RSA/ECB/OAEP | 8+ | - |
SM4/CTR/NoPadding SM4/CBC/NoPadding | 9+ | - |
签名验签
算法 | API级别 | 备注 |
RSA/SHA256/PKCS1_V1_5 RSA/SHA384/PKCS1_V1_5 RSA/SHA512/PKCS1_V1_5 RSA/SHA256/PSS RSA/SHA384/PSS | 8+ | - |
ECC/SHA256 ECC/SHA384 ECC/SHA512 | 8+ | - |
ED25519 | 8+ | - |
SM2/SM3 | 9+ | - |
密钥协商
算法 | API级别 | 备注 |
ECDH | 8+ | 协商密钥类型为ECC类型密钥 |
X25519 | 8+ | - |
密钥派生
算法 | API级别 | 派生密钥及长度 | 备注 |
HKDF/SHA256 HKDF/SHA384 HKDF/SHA512 | 8+ | 算法:AES、HMAC、SM4 长度:256、384、512 | 派生出的密钥可以存储到HUKS或者直接返回明文 |
PBKDF2/SHA256 PBKDF2/SHA384 PBKDF2/SHA512 | 8+ | 算法:AES、HMAC、SM4 长度:256、384、512 | 派生出的密钥可以存储到HUKS或者直接返回明文 |
密钥材料格式
针对不同密码算法的密钥对、公钥、私钥,HUKS定义了一套密钥材料格式。
密钥对材料
密钥对材料 = 密钥对材料Header + 密钥对材料原文
以RSA密钥为例,应用需要申请一个Uint8Array,按照RSA密钥对材料内存格式,将各个变量赋值到对应的位置:
图4 RSA密钥材料内存结构
let rsa2048KeyPairMaterial = new Uint8Array([
0x01, 0x00, 0x00, 0x00, // 密钥算法:huks.HuksKeyAlg.HUKS_ALG_RSA = 1
0x00, 0x08, 0x00, 0x00, // 密钥大小(比特):2048
0x00, 0x01, 0x00, 0x00, // 模数n长度(字节):256
0x03, 0x00, 0x00, 0x00, // 公钥指数e长度(字节):3
0x00, 0x01, 0x00, 0x00, // 私钥指数d长度(字节):256
// 模数n
0xc5, 0x35, 0x62, 0x48, 0xc4, 0x92, 0x87, 0x73, 0x0d, 0x42, 0x96, 0xfc, 0x7b, 0x11, 0x05, 0x06,
0x0f, 0x8d, 0x66, 0xc1, 0x0e, 0xad, 0x37, 0x44, 0x92, 0x95, 0x2f, 0x6a, 0x55, 0xba, 0xec, 0x1d,
0x54, 0x62, 0x0a, 0x4b, 0xd3, 0xc7, 0x05, 0xe4, 0x07, 0x40, 0xd9, 0xb7, 0xc2, 0x12, 0xcb, 0x9a,
0x90, 0xad, 0xe3, 0x24, 0xe8, 0x5e, 0xa6, 0xf8, 0xd0, 0x6e, 0xbc, 0xd1, 0x69, 0x7f, 0x6b, 0xe4,
0x2b, 0x4e, 0x1a, 0x65, 0xbb, 0x73, 0x88, 0x6b, 0x7c, 0xaf, 0x7e, 0xd0, 0x47, 0x26, 0xeb, 0xa5,
0xbe, 0xd6, 0xe8, 0xee, 0x9c, 0xa5, 0x66, 0xa5, 0xc9, 0xd3, 0x25, 0x13, 0xc4, 0x0e, 0x6c, 0xab,
0x50, 0xb6, 0x50, 0xc9, 0xce, 0x8f, 0x0a, 0x0b, 0xc6, 0x28, 0x69, 0xe9, 0x83, 0x69, 0xde, 0x42,
0x56, 0x79, 0x7f, 0xde, 0x86, 0x24, 0xca, 0xfc, 0xaa, 0xc0, 0xf3, 0xf3, 0x7f, 0x92, 0x8e, 0x8a,
0x12, 0x52, 0xfe, 0x50, 0xb1, 0x5e, 0x8c, 0x01, 0xce, 0xfc, 0x7e, 0xf2, 0x4f, 0x5f, 0x03, 0xfe,
0xa7, 0xcd, 0xa1, 0xfc, 0x94, 0x52, 0x00, 0x8b, 0x9b, 0x7f, 0x09, 0xab, 0xa8, 0xa4, 0xf5, 0xb4,
0xa5, 0xaa, 0xfc, 0x72, 0xeb, 0x17, 0x40, 0xa9, 0xee, 0xbe, 0x8f, 0xc2, 0xd1, 0x80, 0xc2, 0x0d,
0x44, 0xa9, 0x59, 0x44, 0x59, 0x81, 0x3b, 0x5d, 0x4a, 0xde, 0xfb, 0xae, 0x24, 0xfc, 0xa3, 0xd9,
0xbc, 0x57, 0x55, 0xc2, 0x26, 0xbc, 0x19, 0xa7, 0x9a, 0xc5, 0x59, 0xa3, 0xee, 0x5a, 0xef, 0x41,
0x80, 0x7d, 0xf8, 0x5e, 0xc1, 0x1d, 0x32, 0x38, 0x41, 0x5b, 0xb6, 0x92, 0xb8, 0xb7, 0x03, 0x0d,
0x3e, 0x59, 0x0f, 0x1c, 0xb3, 0xe1, 0x2a, 0x95, 0x1a, 0x3b, 0x50, 0x4f, 0xc4, 0x1d, 0xcf, 0x73,
0x7c, 0x14, 0xca, 0xe3, 0x0b, 0xa7, 0xc7, 0x1a, 0x41, 0x4a, 0xee, 0xbe, 0x1f, 0x43, 0xdd, 0xf9,
// 公钥指数e
0x01, 0x00, 0x01,
// 私钥指数d
0x88, 0x4b, 0x82, 0xe7, 0xe3, 0xe3, 0x99, 0x75, 0x6c, 0x9e, 0xaf, 0x17, 0x44, 0x3e, 0xd9, 0x07,
0xfd, 0x4b, 0xae, 0xce, 0x92, 0xc4, 0x28, 0x44, 0x5e, 0x42, 0x79, 0x08, 0xb6, 0xc3, 0x7f, 0x58,
0x2d, 0xef, 0xac, 0x4a, 0x07, 0xcd, 0xaf, 0x46, 0x8f, 0xb4, 0xc4, 0x43, 0xf9, 0xff, 0x5f, 0x74,
0x2d, 0xb5, 0xe0, 0x1c, 0xab, 0xf4, 0x6e, 0xd5, 0xdb, 0xc8, 0x0c, 0xfb, 0x76, 0x3c, 0x38, 0x66,
0xf3, 0x7f, 0x01, 0x43, 0x7a, 0x30, 0x39, 0x02, 0x80, 0xa4, 0x11, 0xb3, 0x04, 0xd9, 0xe3, 0x57,
0x23, 0xf4, 0x07, 0xfc, 0x91, 0x8a, 0xc6, 0xcc, 0xa2, 0x16, 0x29, 0xb3, 0xe5, 0x76, 0x4a, 0xa8,
0x84, 0x19, 0xdc, 0xef, 0xfc, 0xb0, 0x63, 0x33, 0x0b, 0xfa, 0xf6, 0x68, 0x0b, 0x08, 0xea, 0x31,
0x52, 0xee, 0x99, 0xef, 0x43, 0x2a, 0xbe, 0x97, 0xad, 0xb3, 0xb9, 0x66, 0x7a, 0xae, 0xe1, 0x8f,
0x57, 0x86, 0xe5, 0xfe, 0x14, 0x3c, 0x81, 0xd0, 0x64, 0xf8, 0x86, 0x1a, 0x0b, 0x40, 0x58, 0xc9,
0x33, 0x49, 0xb8, 0x99, 0xc6, 0x2e, 0x94, 0x70, 0xee, 0x09, 0x88, 0xe1, 0x5c, 0x4e, 0x6c, 0x22,
0x72, 0xa7, 0x2a, 0x21, 0xdd, 0xd7, 0x1d, 0xfc, 0x63, 0x15, 0x0b, 0xde, 0x06, 0x9c, 0xf3, 0x28,
0xf3, 0xac, 0x4a, 0xa8, 0xb5, 0x50, 0xca, 0x9b, 0xcc, 0x0a, 0x04, 0xfe, 0x3f, 0x98, 0x68, 0x81,
0xac, 0x24, 0x53, 0xea, 0x1f, 0x1c, 0x6e, 0x5e, 0xca, 0xe8, 0x31, 0x0d, 0x08, 0x12, 0xf3, 0x26,
0xf8, 0x5e, 0xeb, 0x10, 0x27, 0xae, 0xaa, 0xc3, 0xad, 0x6c, 0xc1, 0x89, 0xdb, 0x7d, 0x5a, 0x12,
0x55, 0xad, 0x11, 0x19, 0xa1, 0xa9, 0x8f, 0x0b, 0x6d, 0x78, 0x8d, 0x1c, 0xdf, 0xe5, 0x63, 0x82,
0x0b, 0x7d, 0x23, 0x04, 0xb4, 0x75, 0x8c, 0xed, 0x77, 0xfc, 0x1a, 0x85, 0x29, 0x11, 0xe0, 0x61,
]);
其中,密钥算法的值取自枚举类HuksKeyAlg。
- RSA密钥对材料格式:
密钥算法 | 密钥大小 | 模数n长度Ln | 公钥指数e长度Le | 私钥指数d长度Ld | n | e | d |
4字节 | 4字节 | 4字节 | 4字节 | 4字节 | Ln字节 | Le字节 | Ld字节 |
- ECC密钥对材料格式:
密钥算法 | 密钥大小 | 坐标x长度Lx | 坐标y长度Ly | 坐标z长度Lz | x | y | z |
4字节 | 4字节 | 4字节 | 4字节 | 4字节 | Lx字节 | Ly字节 | Lz字节 |
- DSA密钥对材料格式:
密钥算法 | 密钥大小 | 私钥x长度Lx | 公钥y长度Ly | 素数p长度Lp | 素因子q长度Lq | g长度Lg | x | y | p | q | g |
4字节 | 4字节 | 4字节 | 4字节 | 4字节 | 4字节 | 4字节 | Lx字节 | Ly字节 | Lp字节 | Lq字节 | Lg字节 |
- DH密钥对材料格式:
密钥算法 | 密钥大小 | 公钥pk长度Lpk | 私钥sk长度Lsk | 保留字段 | pk | sk |
4字节 | 4字节 | 4字节 | 4字节 | 4字节 | Lpk字节 | Lsk字节 |
- Curve25519密钥对材料格式:
密钥算法 | 密钥大小 | 公钥pk长度Lpk | 私钥sk长度Lsk | 保留字段 | pk | sk |
4字节 | 4字节 | 4字节 | 4字节 | 4字节 | Lpk字节 | Lsk字节 |
公钥材料
在公钥导出/导入时,密钥材料采用标准的X.509规范的DER格式封装。
如下是一个DER编码的ECC公钥:
let eccP256PubKey = new Uint8Array([
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xc0, 0xfe, 0x1c, 0x67, 0xde,
0x86, 0x0e, 0xfb, 0xaf, 0xb5, 0x85, 0x52, 0xb4, 0x0e, 0x1f, 0x6c, 0x6c, 0xaa, 0xc5, 0xd9, 0xd2,
0x4d, 0xb0, 0x8a, 0x72, 0x24, 0xa1, 0x99, 0xaf, 0xfc, 0x3e, 0x55, 0x5a, 0xac, 0x99, 0x3d, 0xe8,
0x34, 0x72, 0xb9, 0x47, 0x9c, 0xa6, 0xd8, 0xfb, 0x00, 0xa0, 0x1f, 0x9f, 0x7a, 0x41, 0xe5, 0x44,
0x3e, 0xb2, 0x76, 0x08, 0xa2, 0xbd, 0xe9, 0x41, 0xd5, 0x2b, 0x9e]);
私钥材料
复用密钥对的材料格式,私钥材料的封装是把密钥对材料Header中的公钥部分的长度字段置0,同时密钥对材料原文部分拼接私钥材料即可。
私钥材料 = 密钥对材料Header + 私钥材料原文
以RSA私钥材料为例:
let rsa2048PrivateKeyMaterial = new Uint8Array([
0x01, 0x00, 0x00, 0x00, // 密钥算法:huks.HuksKeyAlg.HUKS_ALG_RSA = 1
0x00, 0x08, 0x00, 0x00, // 密钥大小(比特):2048
0x00, 0x01, 0x00, 0x00, // 模数n长度(字节):256
0x00, 0x00, 0x00, 0x00, // 公钥指数e长度(字节):0
0x00, 0x01, 0x00, 0x00, // 私钥指数d长度(字节):256
// 模数n
0xc5, 0x35, 0x62, 0x48, 0xc4, 0x92, 0x87, 0x73, 0x0d, 0x42, 0x96, 0xfc, 0x7b, 0x11, 0x05, 0x06,
0x0f, 0x8d, 0x66, 0xc1, 0x0e, 0xad, 0x37, 0x44, 0x92, 0x95, 0x2f, 0x6a, 0x55, 0xba, 0xec, 0x1d,
0x54, 0x62, 0x0a, 0x4b, 0xd3, 0xc7, 0x05, 0xe4, 0x07, 0x40, 0xd9, 0xb7, 0xc2, 0x12, 0xcb, 0x9a,
0x90, 0xad, 0xe3, 0x24, 0xe8, 0x5e, 0xa6, 0xf8, 0xd0, 0x6e, 0xbc, 0xd1, 0x69, 0x7f, 0x6b, 0xe4,
0x2b, 0x4e, 0x1a, 0x65, 0xbb, 0x73, 0x88, 0x6b, 0x7c, 0xaf, 0x7e, 0xd0, 0x47, 0x26, 0xeb, 0xa5,
0xbe, 0xd6, 0xe8, 0xee, 0x9c, 0xa5, 0x66, 0xa5, 0xc9, 0xd3, 0x25, 0x13, 0xc4, 0x0e, 0x6c, 0xab,
0x50, 0xb6, 0x50, 0xc9, 0xce, 0x8f, 0x0a, 0x0b, 0xc6, 0x28, 0x69, 0xe9, 0x83, 0x69, 0xde, 0x42,
0x56, 0x79, 0x7f, 0xde, 0x86, 0x24, 0xca, 0xfc, 0xaa, 0xc0, 0xf3, 0xf3, 0x7f, 0x92, 0x8e, 0x8a,
0x12, 0x52, 0xfe, 0x50, 0xb1, 0x5e, 0x8c, 0x01, 0xce, 0xfc, 0x7e, 0xf2, 0x4f, 0x5f, 0x03, 0xfe,
0xa7, 0xcd, 0xa1, 0xfc, 0x94, 0x52, 0x00, 0x8b, 0x9b, 0x7f, 0x09, 0xab, 0xa8, 0xa4, 0xf5, 0xb4,
0xa5, 0xaa, 0xfc, 0x72, 0xeb, 0x17, 0x40, 0xa9, 0xee, 0xbe, 0x8f, 0xc2, 0xd1, 0x80, 0xc2, 0x0d,
0x44, 0xa9, 0x59, 0x44, 0x59, 0x81, 0x3b, 0x5d, 0x4a, 0xde, 0xfb, 0xae, 0x24, 0xfc, 0xa3, 0xd9,
0xbc, 0x57, 0x55, 0xc2, 0x26, 0xbc, 0x19, 0xa7, 0x9a, 0xc5, 0x59, 0xa3, 0xee, 0x5a, 0xef, 0x41,
0x80, 0x7d, 0xf8, 0x5e, 0xc1, 0x1d, 0x32, 0x38, 0x41, 0x5b, 0xb6, 0x92, 0xb8, 0xb7, 0x03, 0x0d,
0x3e, 0x59, 0x0f, 0x1c, 0xb3, 0xe1, 0x2a, 0x95, 0x1a, 0x3b, 0x50, 0x4f, 0xc4, 0x1d, 0xcf, 0x73,
0x7c, 0x14, 0xca, 0xe3, 0x0b, 0xa7, 0xc7, 0x1a, 0x41, 0x4a, 0xee, 0xbe, 0x1f, 0x43, 0xdd, 0xf9,
// 私钥指数d
0x88, 0x4b, 0x82, 0xe7, 0xe3, 0xe3, 0x99, 0x75, 0x6c, 0x9e, 0xaf, 0x17, 0x44, 0x3e, 0xd9, 0x07,
0xfd, 0x4b, 0xae, 0xce, 0x92, 0xc4, 0x28, 0x44, 0x5e, 0x42, 0x79, 0x08, 0xb6, 0xc3, 0x7f, 0x58,
0x2d, 0xef, 0xac, 0x4a, 0x07, 0xcd, 0xaf, 0x46, 0x8f, 0xb4, 0xc4, 0x43, 0xf9, 0xff, 0x5f, 0x74,
0x2d, 0xb5, 0xe0, 0x1c, 0xab, 0xf4, 0x6e, 0xd5, 0xdb, 0xc8, 0x0c, 0xfb, 0x76, 0x3c, 0x38, 0x66,
0xf3, 0x7f, 0x01, 0x43, 0x7a, 0x30, 0x39, 0x02, 0x80, 0xa4, 0x11, 0xb3, 0x04, 0xd9, 0xe3, 0x57,
0x23, 0xf4, 0x07, 0xfc, 0x91, 0x8a, 0xc6, 0xcc, 0xa2, 0x16, 0x29, 0xb3, 0xe5, 0x76, 0x4a, 0xa8,
0x84, 0x19, 0xdc, 0xef, 0xfc, 0xb0, 0x63, 0x33, 0x0b, 0xfa, 0xf6, 0x68, 0x0b, 0x08, 0xea, 0x31,
0x52, 0xee, 0x99, 0xef, 0x43, 0x2a, 0xbe, 0x97, 0xad, 0xb3, 0xb9, 0x66, 0x7a, 0xae, 0xe1, 0x8f,
0x57, 0x86, 0xe5, 0xfe, 0x14, 0x3c, 0x81, 0xd0, 0x64, 0xf8, 0x86, 0x1a, 0x0b, 0x40, 0x58, 0xc9,
0x33, 0x49, 0xb8, 0x99, 0xc6, 0x2e, 0x94, 0x70, 0xee, 0x09, 0x88, 0xe1, 0x5c, 0x4e, 0x6c, 0x22,
0x72, 0xa7, 0x2a, 0x21, 0xdd, 0xd7, 0x1d, 0xfc, 0x63, 0x15, 0x0b, 0xde, 0x06, 0x9c, 0xf3, 0x28,
0xf3, 0xac, 0x4a, 0xa8, 0xb5, 0x50, 0xca, 0x9b, 0xcc, 0x0a, 0x04, 0xfe, 0x3f, 0x98, 0x68, 0x81,
0xac, 0x24, 0x53, 0xea, 0x1f, 0x1c, 0x6e, 0x5e, 0xca, 0xe8, 0x31, 0x0d, 0x08, 0x12, 0xf3, 0x26,
0xf8, 0x5e, 0xeb, 0x10, 0x27, 0xae, 0xaa, 0xc3, 0xad, 0x6c, 0xc1, 0x89, 0xdb, 0x7d, 0x5a, 0x12,
0x55, 0xad, 0x11, 0x19, 0xa1, 0xa9, 0x8f, 0x0b, 0x6d, 0x78, 0x8d, 0x1c, 0xdf, 0xe5, 0x63, 0x82,
0x0b, 0x7d, 0x23, 0x04, 0xb4, 0x75, 0x8c, 0xed, 0x77, 0xfc, 0x1a, 0x85, 0x29, 0x11, 0xe0, 0x61,
]);