HarmonyOS Developer 加解密算法库框架开发指导
加解密算法库框架概述
加解密算法库框架是一个屏蔽了第三方密码学算法库实现差异的算法框架,提供加解密、签名验签、消息验证码、哈希、安全随机数等相关功能。开发者可以通过调用加解密算法库框架,忽略底层不同三方算法库的差异,实现迅捷开发。
框架实现原理
加解密算法库框架提供的组件分为三层:接口层,Framework层和插件层。接口层负责对外提供统一的JS接口,插件层实现针对具体三方算法库的功能,Framework层通过灵活加载插件层的插件适配并屏蔽三方算法库差异。
基本概念
对称密钥
对称密钥使用同一个密钥对数据进行加密解密操作。即对称加密算法中,数据发送方使用加密密钥对明文进行特殊加密算法处理后,使其变成复杂的加密密文发送出去。接收方收到密文后,若想解读原文,则需要使用同一个加密密钥以及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。
- AES加密
AES的全称是Advanced Encryption Standard,是最常见的对称加密。AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。 - 3DES加密
3DES,也称为 3DESede 或 TripleDES,是三重数据加密算法,相当于是对每个数据块应用三次DES的对称加密算法,它使用3个64位的密钥对数据块进行三次加密。相比DES,3DES因密钥长度变长,安全性有所提高,但其处理速度不高。因此又出现了AES加密算法,AES较于3DES速度更快、安全性更高。
非对称密钥
非对称密钥使用公钥和私钥两个密钥来进行算法操作,公钥对外公开,私钥对外保密。对于加解密操作,一般使用公钥对明文进行加密形成密文,持有私钥的人即可对密文解密形成明文。对于签名验签操作,使用私钥对明文进行签名,公钥持有者可以通过公钥对签名数据做验签,验证数据是否被篡改。
- RSA密钥
RSA密钥以极大整数做因数分解的数学难题作为密钥安全性的基石。生成密钥时,首先需要随机出两个大素数p和q,计算n = p * q并将n做为模,再选择一个大于1且小于(p - 1) * (q - 1)的整数e,确保e与(p - 1)*(q - 1)互素,最后计算d,使得e * d - 1为(p - 1)和(q - 1)的倍数,则可得到公钥(n, e)和私钥(n, d)。
算法库框架除提供了默认的双素数RSA密钥生成外,还提供了多素数密钥生成方式,可以在密钥生成时通过指定primes参数(PRIMES_2, PRIMES_3, PRIMES_4, PRIMES_5)指定素数个数。多素数密钥的优点是可以减少解密、签名的计算量(中国剩余定理),但相对的劣势是密钥强度会越低,算法库依据OpenSSL的素数使用规则制定了相应规格,具体将在约束与限制章节中说明。 - ECC密钥
ECC是一种基于椭圆曲线数学的公开密钥加密算法,其数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性,算法库框架提供了多种椭圆曲线的ECC密钥生成能力。
加解密
- 对称AES加解密
算法库目前提供了AES加解密常用的7种加密模式:ECB、CBC、OFB、CFB、CTR、GCM和CCM。AES为分组加密算法,分组长度大小为128位。实际应用中明文最后一组可能不足128位,不足数据可以使用各种padding模式做数据填充。下文中描述了各个padding的区别:
○ NoPadding:不带填充;
○ PKCS5:填充字符由一个字节序列组成,每个字节填充该填充字节序列的长度,规定是8字节填充;
○ PKCS7:填充字符和PKCS5填充方法一致,但是可以在1-255字节之间任意填充;
说明
由于需要填充至分组大小,所以实际算法库中的PKCS5和PKCS7都是以分组大小作为填充长度的,即AES加密填充至16字节。
- 对称3DES加解密
该算法的加解密过程分别是对明文/密文数据进行三次DES加密或解密,得到相应的密文或明文。
算法库目前提供了3DES加解密常用的4种加密模式:ECB、CBC、OFB和CFB。DES为分组加密算法,分组长度大小为64位。实际应用中明文最后一组可能不足64位,不足数据可以使用各种padding模式做数据填充。下文中描述了各个padding的区别:
○ NoPadding:不带填充;
○ PKCS5:填充字符由一个字节序列组成,每个字节填充该填充字节序列的长度,规定是8字节填充;
○ PKCS7:填充字符和PKCS5填充方法一致,但是可以在1-255字节之间任意填充;
说明
由于需要填充至分组大小,所以实际算法库中的PKCS5和PKCS7都是以分组大小作为填充长度的,即3DES加密填充至8字节。
- 非对称RSA加解密
当持有RSA公钥(n, e)和私钥(n, d)后,RSA加密过程为:密文 = 明文 ^ e mod n, 解密过程为:明文 = 密文 ^ d mod n。算法库目前提供了RSA加解密常用的三种模式:PKCS1、PKCS1_OAEP和NoPadding。RSA为块加密算法,加密长度需要在固定长度进行,实际应用中会使用各种padding模式做数据填充。下文中描述了各个padding的区别:
○ NoPadding:不带填充,输入的数据必须与RSA钥模一样长,输出数据长度与RSA钥模一样长;
○ PKCS1:pkcs1padding V1.5是RSA加解密默认的填充方式,输入的数据必须<=RSA钥模-11,输出数据长度与RSA钥模一样长;
○ PKCS1_OAEP:RSA_PKCS1_OAEP_PADDING填充模式是PKCS#1推出的新填充方式,此模式需要设置两个摘要(md和mgf1_md),输入的数据必须小于RSA钥模 - md摘要长度 - mgf1_md摘要长度 - 2,输出数据长度与RSA钥模一样长;
补充说明: RSA钥模 = (RSA的bits + 7) / 8
签名验签
- RSA签名验签
当持有RSA公钥(n, e)和私钥(n, d)后,RSA签名生成过程为:签名 = 消息 ^ d mod n, 验签过程为:消息 = 签名 ^ d mod n。消息发送方发送数据时,同时发送消息和私钥签名后的签名信息,消息接收方接受到数据后,将签名信息用公钥解密并验证消息是否一致。因发送的消息长度大多大于RSA钥模,因此算法库框架提供了两种padding模式,通过摘要提取消息的散列值再做签名。算法库框架中提供了签名验签相关的两种模式:PKCS1和PSS。下问对两种模式做详细描述:
○ PKCS1: pkcs1padding V1.5是RSA加解密默认的填充方式,使用该模式时需要设置摘要(md);
○ PSS: PSS模式是RSA 算法的基础上叠加上一种填充算法,使用该签名算法时需要使用摘要(md)和掩码函数(mgf1_md);
- ECDSA
椭圆曲线数字签名算法(ECDSA)是基于椭圆曲线密码(ECC)模拟数字签名算法(DSA)。相比普通的离散对数问题(DLP)和大数分解问题(IFP),椭圆曲线密码的单位比特强度要高于其他公钥体制。算法库框架提供了多种椭圆曲线及摘要算法组合的椭圆曲线数字签名算法(ECDSA)能力。
密钥协商
- ECDH
ECDH的全称是椭圆曲线迪菲-赫尔曼秘钥交换,是用来在一个非安全通道中建立起安全的共有加密资料,交换双方可以在不共享任何秘密的情况下协商出一个密钥。算法库框架基于开源算法库提供了多种椭圆曲线的ECDH能力。
摘要
消息摘要MD算法是一种能将任意长度的输入消息,通过哈希算法生成长度固定的摘要的算法。消息摘要算法通过其不可逆的特性能被用于敏感信息的加密。消息摘要算法也被称为哈希算法或单向散列算法。
在摘要算法相同时,生成的摘要值主要有下列特点:
- 当输入消息相同时,生成摘要序列相同;
- 当输入消息的长度不一致时,生成摘要序列长度固定(摘要长度由算法决定);
- 当输入消息不一致时,生成摘要序列几乎不会相同(依然存在相同概率,由摘要长度决定相同概率);
消息摘要算法主要分为三类:MD,SHA与MAC(详见HMAC章节)
MD算法包括MD2,MD4和MD5。
SHA算法主要包括SHA1,SHA224,SHA256,SHA384,SHA512。
消息验证码
HMAC(Hash-based Message Authentication Code)是一种基于密钥的消息认证码算法。HMAC通过指定摘要算法,以通信双方共享密钥与消息作为输入,生成消息认证码用于检验传递报文的完整性,HMAC生成的消息认证码为固定长度。HMAC在消息摘要算法的基础上增加了密钥的输入,确保了信息的正确性。
随机数
随机数在加解密过程中主要用于临时会话密钥的生成与非对称加密算法中密钥的生成。随机数由硬件生成的硬件随机数生成器或由软件生成的伪随机数生成器进行生成。在加解密的场景中,安全随机数生成器需要具备随机性,不可预测性,与不可重现性。密码学安全伪随机数生成器CSPRNG(Cryptography Secure Random Number Generators)生成的随机数满足密码学安全伪随机性
- 内部状态代表随机数生成器内存中的数值,当内部状态相同时,随机数生成器会生成固定的随机数序列
- 种子(seed)是一个用来对伪随机数的内部状态进行初始化的数据,随机数生成器通过种子来生成一系列的随机序列。
约束与限制
- 算法库框架不支持多线程并发操作。
密钥生成规格
对称密钥生成规格
支持的对称密钥生成参数:
对称密钥算法 | 密钥长度(bit) | 字符串参数 |
3DES | 192 | 3DES192 |
AES | 128 | AES128 |
AES | 192 | AES192 |
AES | 256 | AES256 |
说明
非对称密钥生成规格
- RSA密钥生成
支持的非对称密钥生成参数:
非对称密钥算法 | 密钥长度(bit) | 素数个数 | 字符串参数 |
RSA | 512 | 2 | RSA512|PRIMES_2 |
RSA | 768 | 2 | RSA768|PRIMES_2 |
RSA | 1024 | 2 | RSA1024|PRIMES_2 |
RSA | 1024 | 3 | RSA1024|PRIMES_3 |
RSA | 2048 | 2 | RSA2048|PRIMES_2 |
RSA | 2048 | 3 | RSA2048|PRIMES_3 |
RSA | 3072 | 2 | RSA3072|PRIMES_2 |
RSA | 3072 | 3 | RSA3072|PRIMES_3 |
RSA | 4096 | 2 | RSA4096|PRIMES_2 |
RSA | 4096 | 3 | RSA4096|PRIMES_3 |
RSA | 4096 | 4 | RSA4096|PRIMES_4 |
RSA | 8192 | 2 | RSA8192|PRIMES_2 |
RSA | 8192 | 3 | RSA8192|PRIMES_3 |
RSA | 8192 | 4 | RSA8192|PRIMES_4 |
RSA | 8192 | 5 | RSA8192|PRIMES_5 |
说明
- ECC密钥生成
支持的非对称密钥生成参数:
非对称密钥算法 | 密钥长度 |
ECC | ECC224 |
ECC | ECC256 |
ECC | ECC384 |
ECC | ECC521 |
加解密规格
对称加解密
支持的对称加密算法:
对称加解密算法 | 分组模式 | 字符串参数 |
3DES | ECB | 3DES192|ECB|[NoPadding|PKCS5|PKCS7] |
3DES | CBC | 3DES192|CBC|[NoPadding|PKCS5|PKCS7] |
3DES | OFB | 3DES192|OFB|[NoPadding|PKCS5|PKCS7] |
3DES | CFB | 3DES192|CFB|[NoPadding|PKCS5|PKCS7] |
AES | ECB | AES[128|192|256]|ECB|[NoPadding|PKCS5|PKCS7] |
AES | CBC | AES[128|192|256]|CBC|[NoPadding|PKCS5|PKCS7] |
AES | CTR | AES[128|192|256]|CTR|[NoPadding|PKCS5|PKCS7] |
AES | OFB | AES[128|192|256]|OFB|[NoPadding|PKCS5|PKCS7] |
AES | CFB | AES[128|192|256]|CFB|[NoPadding|PKCS5|PKCS7] |
AES | GCM | AES[128|192|256]|GCM|[NoPadding|PKCS5|PKCS7] |
AES | CCM | AES[128|192|256]|CCM|[NoPadding|PKCS5|PKCS7] |
说明
- []中只能任选一项。
- “字符串参数”是“对称加解密算法(含密钥长度)”、“分组模式”、“填充模式”拼接而成,用于在创建对称加解密实例时,指定对称加解密算法规格。
非对称RSA加解密
RSA加解密时,涉及三种填充模式:NoPadding, PKCS1和PKCS1_OAEP。
- 使用NoPadding模式时可以指定的参数: [RSA512|RSA768|RSA1024|RSA2048|RSA3072|RSA4096|RSA8192]|NoPadding
- 使用PKCS1模式时可以指定的参数: [RSA512|RSA768|RSA1024|RSA2048|RSA3072|RSA4096|RSA8192]|PKCS1
- 使用PKCS1_OAEP模式时可以指定的参数:[RSA512|RSA768|RSA1024|RSA2048|RSA3072|RSA4096|RSA8192]|PKCS1_OAEP|[MD5|SHA1|SHA224|SHA256|SHA384|SHA512]|[MGF1_MD5|MGF1_SHA1|MGF1_SHA224|MGF1_SHA256|MGF1_SHA384|MGF1_SHA512]
说明
签名验签规格
RSA签名验签
RSA签名验签时,涉及两种填充模式:PKCS1和PSS。
- 使用PKCS1模式时可以指定的参数: [RSA512|RSA768|RSA1024|RSA2048|RSA3072|RSA4096|RSA8192]|PKCS1|[MD5|SHA1|SHA224|SHA256|SHA384|SHA512]
- 使用PSS模式时可以指定的参数:[RSA512|RSA768|RSA1024|RSA2048|RSA3072|RSA4096|RSA8192]|PSS|[MD5|SHA1|SHA224|SHA256|SHA384|SHA512]|[MGF1_MD5|MGF1_SHA1|MGF1_SHA224|MGF1_SHA256|MGF1_SHA384|MGF1_SHA512]
说明
ECDSA签名验签
支持的ECDSA参数:
非对称密钥算法 | 支持种类 |
ECC | ECC224 |
ECC | ECC256 |
ECC | ECC384 |
ECC | ECC521 |
摘要算法 | 支持种类 |
HASH | SHA1 |
HASH | SHA224 |
HASH | SHA256 |
HASH | SHA384 |
HASH | SHA512 |
密钥协商规格
ECDH
支持的ECDH参数:
非对称密钥算法 | 支持种类 |
ECC | ECC224 |
ECC | ECC256 |
ECC | ECC384 |
ECC | ECC521 |
MD算法规格
加解密算法库框架当前支持MD5算法
SHA算法规格
加解密算法库框架当前支持:SHA1,SHA224,SHA256,SHA384,SHA512
加解密算法库框架开发指导
说明
本开发指导基于API version 9,适用于JS语言开发
使用密钥对象生成与转换操作
场景说明
使用密钥生成操作中,典型的场景有:
- 随机生成算法库密钥对象。该对象可用于后续的加解密等操作。
- 根据指定数据生成算法库密钥对象(也就是将外部或存储的二进制数据转换为算法库的密钥对象)。该对象可用于后续的加解密等操作。
- 获取算法库密钥对象的二进制数据,用于存储或传输。
说明
密钥对象Key包括对称密钥SymKey和非对称密钥(公钥PubKey和私钥PriKey),其中公钥和私钥组成密钥对KeyPair。密钥之间的具体关系可参考API参考。
接口及参数说明
详细接口说明可参考API参考。
以上场景涉及的常用接口如下表所示:
实例名 | 接口名 | 描述 |
cryptoFramework | createAsyKeyGenerator(algName : string) : AsyKeyGenerator | 根据algName设置的非对称密钥规格,创建非对称密钥生成器对象 |
cryptoFramework | createSymKeyGenerator(algName : string) : SymKeyGenerator | 根据algName设置的对称密钥规格,创建对称密钥生成器对象 |
AsyKeyGenerator | generateKeyPair(callback : AsyncCallback<KeyPair>) : void | 使用callback方式,随机生成非对称密钥对象KeyPair |
AsyKeyGenerator | generateKeyPair() : Promise<KeyPair> | 使用Promise方式,随机生成非对称密钥对象KeyPair |
SymKeyGenerator | generateSymKey(callback : AsyncCallback<SymKey>) : void | 使用callback方式,随机生成对称密钥对象SymKey |
SymKeyGenerator | generateSymKey() : Promise<SymKey> | 使用Promise方式,随机生成对称密钥对象SymKey |
AsyKeyGenerator | convertKey(pubKey : DataBlob, priKey : DataBlob, callback : AsyncCallback<KeyPair>) : void | 使用callback方式,根据指定的公钥和私钥二进制数据生成KeyPair对象 (允许公钥/私钥为null,即只传入单一公钥或私钥,生成只携带公钥或私钥的KeyPair对象) |
AsyKeyGenerator | convertKey(pubKey : DataBlob, priKey : DataBlob) : Promise<KeyPair> | 使用Promise方式,根据指定的公钥和私钥二进制数据生成KeyPair对象 (允许公钥/私钥为null,即只传入单一公钥或私钥,生成只携带公钥或私钥的KeyPair对象) |
SymKeyGenerator | convertKey(key : DataBlob, callback : AsyncCallback<SymKey>) : void | 使用callback方式,根据指定的二进制数据,生成对称密钥对象SymKey |
SymKeyGenerator | convertKey(pubKey : DataBlob, priKey : DataBlob) : Promise<KeyPair> | 使用Promise方式,根据指定的二进制数据,生成对称密钥对象SymKey |
Key | getEncoded() : DataBlob; | 获取Key密钥对象的二进制数据(Key的子类实例包括对称密钥SymKey、公钥PubKey、私钥PriKey) |
开发步骤
示例1:随机生成非对称密钥KeyPair,并获得二进制数据(场景1、3)
- 创建非对称密钥生成器;
- 通过非对称密钥生成器随机生成非对称密钥;
- 获取密钥对象的二进制数据;
以使用Promise方式随机生成RSA密钥(1024位,素数个数为2)为例:
import cryptoFramework from '@ohos.security.cryptoFramework';
function generateAsyKey() {
// 创建非对称密钥生成器
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
// 通过非对称密钥生成器,随机生成非对称密钥
let keyGenPromise = rsaGenerator.generateKeyPair();
keyGenPromise.then( keyPair => {
globalKeyPair = keyPair;
let pubKey = globalKeyPair.pubKey;
let priKey = globalKeyPair.priKey;
// 获取非对称密钥的二进制数据
pkBlob = pubKey.getEncoded();
skBlob = priKey.getEncoded();
AlertDialog.show({ message : "pk bin data" + pkBlob.data} );
AlertDialog.show({ message : "sk bin data" + skBlob.data} );
})
}
示例2:随机生成对称密钥SymKey,并获得二进制数据(场景1、3)
- 创建对称密钥生成器;
- 通过对称密钥生成器随机生成对称密钥;
- 获取算法库密钥对象的二进制数据;
以使用Promise方式随机生成AES密钥(256位)为例:
import cryptoFramework from '@ohos.security.cryptoFramework';
// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
return Array.prototype.map
.call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
.join('');
}
function testGenerateAesKey() {
// 创建对称密钥生成器
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
// 通过密钥生成器随机生成对称密钥
let promiseSymKey = symKeyGenerator.generateSymKey();
promiseSymKey.then( key => {
// 获取对称密钥的二进制数据,输出长度为256bit的字节流
let encodedKey = key.getEncoded();
console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
})
}
示例3:根据指定的RSA非对称密钥二进制数据,生成KeyPair对象(场景2)
- 获取RSA公钥或私钥二进制数据,公钥需满足ASN.1语法、X.509规范、DER编码格式,私钥需满足ASN.1语法、PKCS#8规范、DER编码格式。
- 创建AsyKeyGenerator对象,调用convertKey方法,传入公钥二进制和私钥二进制(二者非必选项,可只传入其中一个),转换为KeyPair对象。
import cryptoFramework from '@ohos.security.cryptoFramework';
function convertAsyKey() {
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024");
let pkval = new Uint8Array([48,129,159,48,13,6,9,42,134,72,134,247,13,1,1,1,5,0,3,129,141,0,48,129,137,2,129,129,0,174,203,113,83,113,3,143,213,194,79,91,9,51,142,87,45,97,65,136,24,166,35,5,179,42,47,212,79,111,74,134,120,73,67,21,19,235,80,46,152,209,133,232,87,192,140,18,206,27,106,106,169,106,46,135,111,118,32,129,27,89,255,183,116,247,38,12,7,238,77,151,167,6,102,153,126,66,28,253,253,216,64,20,138,117,72,15,216,178,37,208,179,63,204,39,94,244,170,48,190,21,11,73,169,156,104,193,3,17,100,28,60,50,92,235,218,57,73,119,19,101,164,192,161,197,106,105,73,2,3,1,0,1]);
let pkBlob = {data : pkval};
rsaGenerator.convertKey(pkBlob, null, function(err, keyPair) {
if (keyPair == null) {
AlertDialog.show({message : "Convert keypair fail"});
}
AlertDialog.show({message : "Convert KeyPair success"});
})
}
说明
当前convertKey操作,公钥只支持转换满足X.509规范的DER格式,私钥只支持PKCS#8规范的DER格式;
示例4:根据指定的ECC非对称密钥二进制数据,生成KeyPair对象(场景2、3)
- 获取ECC二进制密钥数据,封装成DataBlob对象。
- 调用convertKey方法,传入公钥二进制和私钥二进制(二者非必选项,可只传入其中一个),转换为KeyPair对象。
function convertEccAsyKey() {
let pubKeyArray = new Uint8Array([4,196,55,233,100,227,224,38,38,5,128,81,53,112,129,7,59,189,116,105,182,87,190,85,31,248,172,116,213,7,206,85,190,65,169,193,138,173,232,187,74,54,78,251,29,131,192,223,251,227,170,138,80,7,98,193,216,168,235,114,255,188,70,134,104]);
let priKeyArray = new Uint8Array([255,70,89,220,189,19,41,157,175,173,83,60,74,216,195,96,24,181,231,23,112,247,150,126,15,155,24,79,33,97,31,225]);
let pubKeyBlob = { data: pubKeyArray };
let priKeyBlob = { data: priKeyArray };
let generator = cryptoFrameWork.createAsyKeyGenerator("ECC256");
generator.convertKey(pubKeyBlob, priKeyBlob, (error, data) => {
if (error) {
AlertDialog.show({message : "Convert keypair fail"});
}
AlertDialog.show({message : "Convert KeyPair success"});
})
}
示例5:根据指定的对称密钥二进制数据,生成SymKey对象(场景2、3)
- 创建对称密钥生成器;
- 通过对称密钥生成器,根据指定的对称密钥二进制数据,生成SymKey对象;
- 获取算法库密钥对象的二进制数据;
以使用callback方式生成3DES密钥(3DES密钥只能为192位)为例:
import cryptoFramework from '@ohos.security.cryptoFramework';
// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
return Array.prototype.map
.call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
.join('');
}
function genKeyMaterialBlob() {
let arr = [
0xba, 0x3d, 0xc2, 0x71, 0x21, 0x1e, 0x30, 0x56,
0xad, 0x47, 0xfc, 0x5a, 0x46, 0x39, 0xee, 0x7c,
0xba, 0x3b, 0xc2, 0x71, 0xab, 0xa0, 0x30, 0x72]; // keyLen = 192 (24 bytes)
let keyMaterial = new Uint8Array(arr);
return {data : keyMaterial};
}
function testConvertAesKey() {
// 生成对称密钥生成器
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('3DES192');
// 根据用户指定的数据,生成对称密钥
let keyMaterialBlob = genKeyMaterialBlob();
try {
symKeyGenerator.convertKey(keyMaterialBlob, (error, key) => {
if (error) { // 业务逻辑执行错误通过callback的第一个参数返回错误信息
console.error(`convertKey error, ${error.code}, ${error.message}`);
return;
}
console.info(`key algName: ${key.algName}`);
console.info(`key format: ${key.format}`);
let encodedKey = key.getEncoded(); // 获取对称密钥的二进制数据,输出长度为192bit的字节流
console.info('key getEncoded hex: ' + uint8ArrayToShowStr(encodedKey.data));
})
} catch (error) { // 参数检查的错误以同步的方式立即抛出异常
console.error(`convertKey failed, ${error.code}, ${error.message}`);
return;
}
}
使用加解密操作
场景说明
在数据存储或传输场景中,可以使用加解密操作用于保证数据的机密性,防止敏感数据泄露。使用加解密操作中,典型的场景有:
- 使用对称密钥的加解密操作
- 使用非对称密钥的加解密操作
接口及参数说明
详细接口说明可参考API参考。
以上场景设计的常用接口如下表所示:
实例名 | 接口名 | 描述 |
cryptoFramework | createCipher(transformation : string) : Cipher | 根据transformation设置的算法参数创建Cipher对象 |
Cipher | init(opMode : CryptoMode, key : Key, params : ParamsSpec, callback : AsyncCallback<void>) : void | 使用callback方式设置密钥并初始化Cipher对象 |
Cipher | init(opMode : CryptoMode, key : Key, params : ParamsSpec) : Promise<void> | 使用Promise方式设置密钥并初始化Cipher对象 |
Cipher | update(data : DataBlob, callback : AsyncCallback<DataBlob>) : void | 使用callback方式添加加解密数据 |
Cipher | update(data : DataBlob) : Promise<DataBlob> | 使用Promise方式添加加解密数据 |
Cipher | doFinal(data : DataBlob, callback : AsyncCallback<DataBlob>) : void | 使用callback方式结束对所有数据的加解密 |
Cipher | doFinal(data : DataBlob) : Promise<DataBlob> | 使用Promise方式结束对所有数据的加解密 |
开发步骤
示例1:使用对称密钥的加解密操作
- 创建对称密钥生成器。
- 通过密钥生成器生成对称密钥。
- 创建加解密生成器。
- 通过加解密生成器加密或解密数据。
以AES GCM以Promise方式加解密为例:
import cryptoFramework from '@ohos.security.cryptoFramework';
var globalCipher;
var globalGcmParams;
var globalKey;
var globalCipherText;
function genGcmParamsSpec() {
let arr = [0, 0, 0, 0 , 0, 0, 0, 0, 0, 0 , 0, 0]; // 12 bytes
let dataIv = new Uint8Array(arr);
let ivBlob = {data : dataIv};
arr = [0, 0, 0, 0 , 0, 0, 0, 0]; // 8 bytes
let dataAad = new Uint8Array(arr);
let aadBlob = {data : dataAad};
arr = [0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0]; // 16 bytes
let dataTag = new Uint8Array(arr);
let tagBlob = {data : dataTag};
let gcmParamsSpec = {iv : ivBlob, aad : aadBlob, authTag : tagBlob, algoName : "GcmParamsSpec"};
return gcmParamsSpec;
}
// 可理解的字符串转成字节流
function stringToUint8Array(str) {
let arr = [];
for (let i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
return Array.prototype.map
.call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
.join('');
}
// 字节流转成可理解的字符串
function uint8ArrayToString(array) {
let arrayString = '';
for (let i = 0; i < array.length; i++) {
arrayString += String.fromCharCode(array[i]);
}
return arrayString;
}
function genKeyMaterialBlob() {
let arr = [
0xba, 0x3d, 0xc2, 0x71, 0x21, 0x1e, 0x30, 0x56,
0xad, 0x47, 0xfc, 0x5a, 0x46, 0x39, 0xee, 0x7c,
0xba, 0x3b, 0xc2, 0x71, 0xab, 0xa0, 0x30, 0x72]; // keyLen = 192 (24 bytes)
let keyMaterial = new Uint8Array(arr);
return {data : keyMaterial};
}
// AES GCM模式示例,自动生成密钥(promise写法)
function testAesGcm() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('testAesGcm');
}, 10)
}).then(() => {
// 生成对称密钥生成器
let symAlgoName = 'AES128';
let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgoName);
if (symKeyGenerator == null) {
console.error('createSymKeyGenerator failed');
return;
}
console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);
// 通过密钥生成器随机生成128位长度的对称密钥
let promiseSymKey = symKeyGenerator.generateSymKey();
// 构造参数
globalGcmParams = genGcmParamsSpec();
// 生成加解密生成器
let cipherAlgoName = 'AES128|GCM|PKCS7';
try {
globalCipher = cryptoFramework.createCipher(cipherAlgoName);
console.info(`cipher algName: ${globalCipher.algName}`);
} catch (error) {
console.error(`createCipher failed, ${error.code}, ${error.message}`);
return;
}
return promiseSymKey;
}).then(key => {
let encodedKey = key.getEncoded();
console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
globalKey = key;
return key;
}).then(key => {
// 初始化加解密操作环境:开始加密
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
let promiseInit = globalCipher.init(mode, key, globalGcmParams); // init
return promiseInit;
}).then(() => {
let plainText = {data : stringToUint8Array('this is test!')};
let promiseUpdate = globalCipher.update(plainText); // update
return promiseUpdate;
}).then(updateOutput => {
globalCipherText = updateOutput;
let promiseFinal = globalCipher.doFinal(null); // doFinal
return promiseFinal;
}).then(authTag => {
// 获取加密后的认证信息
globalGcmParams.authTag = authTag;
return;
}).then(() => {
// 初始化加解密操作环境:开始解密
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
let promiseInit = globalCipher.init(mode, globalKey, globalGcmParams); // init
return promiseInit;
}).then(() => {
let promiseUpdate = globalCipher.update(globalCipherText); // update
return promiseUpdate;
}).then(updateOutput => {
console.info('decrypt plainText: ' + uint8ArrayToString(updateOutput.data));
let promiseFinal = globalCipher.doFinal(null); // doFinal
return promiseFinal;
}).then(finalOutput => {
if (finalOutput == null) {
console.info('GCM finalOutput is null');
}
}).catch(error => {
console.error(`catch error, ${error.code}, ${error.message}`);
})
}
以3DES ECB以callback方式加解密(采用已有数据生成密钥)为例:
import cryptoFramework from '@ohos.security.cryptoFramework';
var globalCipher;
var globalGcmParams;
var globalKey;
var globalCipherText;
// 可理解的字符串转成字节流
function stringToUint8Array(str) {
let arr = [];
for (let i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
return Array.prototype.map
.call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
.join('');
}
// 字节流转成可理解的字符串
function uint8ArrayToString(array) {
let arrayString = '';
for (let i = 0; i < array.length; i++) {
arrayString += String.fromCharCode(array[i]);
}
return arrayString;
}
function genKeyMaterialBlob() {
let arr = [
0xba, 0x3d, 0xc2, 0x71, 0x21, 0x1e, 0x30, 0x56,
0xad, 0x47, 0xfc, 0x5a, 0x46, 0x39, 0xee, 0x7c,
0xba, 0x3b, 0xc2, 0x71, 0xab, 0xa0, 0x30, 0x72]; // keyLen = 192 (24 bytes)
let keyMaterial = new Uint8Array(arr);
return {data : keyMaterial};
}
// 3DES ECB模式示例,采用已有数据生成密钥(callback写法)
function test3DesEcb() {
// 生成对称密钥生成器
let symAlgoName = '3DES192';
let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgoName);
if (symKeyGenerator == null) {
console.error('createSymKeyGenerator failed');
return;
}
console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);
// 生成加解密生成器
let cipherAlgoName = '3DES192|ECB|PKCS7';
try {
globalCipher = cryptoFramework.createCipher(cipherAlgoName);
console.info(`cipher algName: ${globalCipher.algName}`);
} catch (error) {
console.error(`createCipher failed, ${error.code}, ${error.message}`);
return;
}
// 根据指定的数据,生成对称密钥
let keyMaterialBlob = genKeyMaterialBlob();
try {
symKeyGenerator.convertKey(keyMaterialBlob, (error, key) => {
if (error) {
console.error(`convertKey error, ${error.code}, ${error.message}`);
return;
}
console.info(`key algName: ${key.algName}`);
console.info(`key format: ${key.format}`);
let encodedKey = key.getEncoded();
console.info('key getEncoded hex: ' + uint8ArrayToShowStr(encodedKey.data));
globalKey = key;
// 初始化加解密操作环境:开始加密
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
// init
globalCipher.init(mode, key, null, (err, ) => {
let plainText = {data : stringToUint8Array('this is test!')};
// update
globalCipher.update(plainText, (err, updateOutput) => {
globalCipherText = updateOutput;
//doFinal
globalCipher.doFinal(null, (err, finalOutput) => {
if (error) {
console.error(`doFinal error, ${error.code}, ${error.message}`);
return;
}
if (finalOutput != null) {
globalCipherText = Array.from(globalCipherText.data);
finalOutput = Array.from(finalOutput.data);
globalCipherText = globalCipherText.concat(finalOutput);
globalCipherText = new Uint8Array(globalCipherText);
globalCipherText = {data : globalCipherText};
}
// 初始化加解密操作环境:开始解密
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
// init
globalCipher.init(mode, globalKey, null, (err, ) => {
// update
globalCipher.update(globalCipherText, (err, updateOutput) => {
console.info('decrypt plainText: ' + uint8ArrayToString(updateOutput.data));
// doFinal
globalCipher.doFinal(null, (error, finalOutput) => {
if (finalOutput != null) {
console.info("decrypt plainText:" + uint8ArrayToString(finalOutput.data));
}
})
})
})
})
})
})
})
} catch (error) {
console.error(`convertKey failed, ${error.code}, ${error.message}`);
return;
}
}
示例2:使用非对称密钥的加解密操作
- 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
- 生成Cipher对象。通过createCipher接口创建Cipher对象,执行初始化操作,设置密钥及加解密模式。
- 执行加解密操作。通过调用Cipher对象提供的doFinal接口,执行加密操作生成密文或执行解密操作生成明文。
import cryptoFramework from "@ohos.security.cryptoFramework"
let plan = "This is cipher test.";
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpArray = new Uint8Array(arr);
return tmpArray;
}
function encryptMessageProMise() {
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
let keyGenPromise = rsaGenerator.generateKeyPair();
keyGenPromise.then(rsaKeyPair => {
let pubKey = rsaKeyPair.pubKey;
return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null);
}).then(() => {
let input = { data : stringToUint8Array(plan) };
return cipher.doFinal(input);
}).then(dataBlob => {
console.info("EncryptOutPut is " + dataBlob.data);
});
}
function encryptMessageCallback() {
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
rsaGenerator.generateKeyPair(function (err, keyPair) {
let pubKey = keyPair.pubKey;
cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null, function (err, data) {
let input = {data : stringToUint8Array(plan) };
cipher.doFinal(input, function (err, data) {
console.info("EncryptOutPut is " + data.data);
})
})
})
}
说明
- 使用RSA加解密时,Cipher对象不可重复调用init方法初始化,在创建了一个加密Cipher对象后,如果要进行解密,则需要重新创建另一个Cipher对象执行解密操作。
- RSA加密有长度限制,允许加密明文的最大长度见加解密算法库框架概述中的基本概念章节。
- RSA解密每次允许解密的密文长度为,RSA密钥的位数/8。
使用签名验签操作
场景说明
当需要判断接收的数据是否被篡改且是否为指定对象发送的数据时,可以使用签名验签操作。使用签名验签操作中,典型的场景有:
- 使用RSA签名验签操作
- 使用ECC签名验签操作
接口及参数说明
详细接口说明可参考API参考。
实例名 | 接口名 | 描述 |
cryptoFramework | createSign(algName : string) : Sign | 根据String设置的参数创建Sign对象 |
Sign | init(priKey : PriKey, callback : AsyncCallback<void>) : void | 使用callback方式设置密钥并初始化Sign对象 |
Sign | init(priKey : PriKey) : Promise<void> | 使用Promise方式设置密钥并初始化Sign对象 |
Sign | update(data : DataBlob, callback : AsyncCallback<void>) : void | 使用callback方式添加签名数据 |
Sign | update(data : DataBlob) : Promise<void> | 用Promise方式添加签名数据 |
Sign | sign(data : DataBlob, callback : AsyncCallback<DataBlob>) : void | 使用callback方式签名所有数据 |
Sign | sign(data : DataBlob) : Promise<DataBlob> | 使用Promise方式签名所有数据 |
cryptoFramework | function createVerify(algName : string) : Verify | 根据String设置的参数创建Verify对象 |
Verify | init(priKey : PriKey, callback : AsyncCallback<void>) : void | 使用callback方式设置密钥并初始化Verify对象 |
Verify | init(priKey : PriKey) : Promise<void> | 使用Promise方式设置密钥并初始化Verify对象 |
Verify | update(data : DataBlob, callback : AsyncCallback<void>) : void | 使用callback方式添加验签数据 |
Verify | update(data : DataBlob) : Promise<void> | 用Promise方式添加验签数据 |
Verify | verify(data : DataBlob, signatureData : DataBlob, callback : AsyncCallback<boolean>) : void | 使用callback方式验签所有数据 |
Verify | verify(data : DataBlob, signatureData : DataBlob) : Promise<boolean> | 使用Promise方式验签所有数据 |
开发步骤
示例1:使用RSA签名验签操作
- 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
- 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
- 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用sign接口生成数据的签名。
- 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
- 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用verify接口传入签名进行验签。
import cryptoFramework from "@ohos.security.cryptoFramework"
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpArray = new Uint8Array(arr);
return tmpArray;
}
let globalKeyPair;
let SignMessageBlob;
let plan1 = "This is Sign test plan1";
let plan2 = "This is Sign test plan1";
let input1 = { data : stringToUint8Array(plan1) };
let input2 = { data : stringToUint8Array(plan2) };
function signMessagePromise() {
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
let signer = cryptoFramework.createSign("RSA1024|PKCS1|SHA256");
let keyGenPromise = rsaGenerator.generateKeyPair();
keyGenPromise.then( keyPair => {
globalKeyPair = keyPair;
let priKey = globalKeyPair.priKey;
return signer.init(priKey);
}).then(() => {
return signer.update(input1);
}).then(() => {
return signer.sign(input2);
}).then(dataBlob => {
SignMessageBlob = dataBlob;
console.info("sign output is " + SignMessageBlob.data);
});
}
function verifyMessagePromise() {
let verifyer = cryptoFramework.createVerify("RSA1024|PKCS1|SHA256");
let verifyInitPromise = verifyer.init(globalKeyPair.pubKey);
verifyInitPromise.then(() => {
return verifyer.update(input1);
}).then(() => {
return verifyer.verify(input2, SignMessageBlob);
}).then(res => {
console.log("Verify result is " + res);
});
}
function signMessageCallback() {
let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
let signer = cryptoFramework.createSign("RSA1024|PKCS1|SHA256");
rsaGenerator.generateKeyPair(function (err, keyPair) {
globalKeyPair = keyPair;
let priKey = globalKeyPair.priKey;
signer.init(priKey, function (err, data) {
signer.update(input1, function (err, data) {
signer.sign(input2, function (err, data) {
SignMessageBlob = data;
console.info("sign output is " + SignMessageBlob.data);
});
});
});
});
}
function verifyMessageCallback() {
let verifyer = cryptoFramework.createVerify("RSA1024|PKCS1|SHA256");
verifyer.init(globalKeyPair.pubKey, function (err, data) {
verifyer.update(input1, function(err, data) {
verifyer.verify(input2, SignMessageBlob, function(err, data) {
console.info("verify result is " + data);
});
});
})
}
示例2:使用ECDSA操作
- 生成ECC密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成ECC非对称密钥。
- 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
- 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用doFinal接口生成数据的签名。
- 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
- 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用doFinal接口传入签名进行验签。
import cryptoFramework from "@ohos.security.cryptoFramework"
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpArray = new Uint8Array(arr);
return tmpArray;
}
let globalKeyPair;
let SignMessageBlob;
let plan1 = "This is Sign test plan1";
let plan2 = "This is Sign test plan1";
let input1 = { data : stringToUint8Array(plan1) };
let input2 = { data : stringToUint8Array(plan2) };
function signMessagePromise() {
let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
let signer = cryptoFramework.createSign("ECC256|SHA256");
let keyGenPromise = eccGenerator.generateKeyPair();
keyGenPromise.then( keyPair => {
globalKeyPair = keyPair;
let priKey = globalKeyPair.priKey;
return signer.init(priKey);
}).then(() => {
return signer.update(input1);
}).then(() => {
return signer.sign(input2);
}).then(dataBlob => {
SignMessageBlob = dataBlob;
console.info("sign output is " + SignMessageBlob.data);
});
}
function verifyMessagePromise() {
let verifyer = cryptoFramework.createVerify("ECC256|SHA256");
let verifyInitPromise = verifyer.init(globalKeyPair.pubKey);
verifyInitPromise.then(() => {
return verifyer.update(input1);
}).then(() => {
return verifyer.verify(input2, SignMessageBlob);
}).then(res => {
console.log("Verify result is " + res);
});
}
function signMessageCallback() {
let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
let signer = cryptoFramework.createSign("ECC256|SHA256");
eccGenerator.generateKeyPair(function (err, keyPair) {
globalKeyPair = keyPair;
let priKey = globalKeyPair.priKey;
signer.init(priKey, function (err, data) {
signer.update(input1, function (err, data) {
signer.sign(input2, function (err, data) {
SignMessageBlob = data;
console.info("sign output is " + SignMessageBlob.data);
});
});
});
});
}
function verifyMessageCallback() {
let verifyer = cryptoFramework.createVerify("ECC256|SHA256");
verifyer.init(globalKeyPair.pubKey, function (err, data) {
verifyer.update(input1, function(err, data) {
verifyer.verify(input2, SignMessageBlob, function(err, data) {
console.info("verify result is " + data);
});
});
})
}
使用摘要操作
场景说明
用户指定摘要算法(如SHA256)生成Md实例,并输入单段或多段需要摘要的信息,进行摘要计算更新,并返回消息摘要计算结果,在指定算法后可获取当前算法名与摘要计算长度(字节)
使用摘要操作的主要场景为:
用户指定摘要算法(如SHA256)生成Md实例,并输入单段或多段需要摘要的信息,进行摘要计算更新,并返回消息摘要计算结果,在指定算法后可获取当前算法名与摘要计算长度(字节)
接口及参数说明
详细接口说明可参考API参考。
实例名 | 接口名 | 描述 |
cryptoFramework | function createMd(algName : string) : Md; | 指定摘要算法,生成摘要操作实例Md |
Md | update(input : DataBlob, callback : AsyncCallback<void>) : void; | 接受用户输入数据,通过Callback的方式,异步更新摘要 |
Md | update(input : DataBlob) : Promise<void>; | 接受用户输入数据,通过Promise的方式,异步更新摘要 |
Md | digest(callback : AsyncCallback<DataBlob>) : void; | 通过Callback的方式,返回结果 |
Md | digest() : Promise<DataBlob>; | 通过Promise的方式,返回结果 |
Md | getMdLength() : number; | 获取摘要的长度(由指定的摘要算法决定) |
Md | readonly algName : string; | 获取当前设置的摘要算法名 |
开发步骤
- 设置算法,通过接口createMd生成摘要操作实例
- 接受用户数据,通过接口update,更新摘要,此步骤可重复
- 通过接口digest,返回摘要计算结果
- 获取当前摘要算法名与摘要计算长度
import cryptoFramework from "@ohos.security.cryptoFramework"
// turn string into uint8Arr
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array;
}
// generate dataBlob with given length
function GenDataBlob(dataBlobLen) {
var dataBlob;
if (dataBlobLen == 12) {
dataBlob = {data: stringToUint8Array("my test data")};
} else {
console.error("GenDataBlob: dataBlobLen is invalid");
dataBlob = {data: stringToUint8Array("my test data")};
}
return dataBlob;
}
// md with promise async
function doMdByPromise(algName) {
var md;
try {
md = cryptoFramework.createMd(algName);
} catch (error) {
console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
}
console.error("[Promise]: Md algName is: " + md.algName);
var promiseMdUpdate = md.update(GenDataBlob(12));
promiseMdUpdate.then(() => {
var PromiseMdDigest = md.digest();
return PromiseMdDigest;
}).then(mdOutput => {
console.error("[Promise]: MD result: " + mdOutput.data);
var mdLen = md.getMdLength();
console.error("[Promise]: MD len: " + mdLen);
}).catch(error => {
console.error("[Promise]: error: " + error.message);
});
}
// md with callback async
function doMdByCallback(algName) {
var md;
try {
md = cryptoFramework.createMd(algName);
} catch (error) {
console.error("[Callback]: error code: " + error.code + ", message is: " + error.message);
}
console.error("[Callback]: Md algName is: " + md.algName);
md.update(GenDataBlob(12), (err,) => {
if (err) {
console.error("[Callback]: err: " + err.code);
}
md.digest((err1, mdOutput) => {
if (err1) {
console.error("[Callback]: err: " + err1.code);
} else {
console.error("[Callback]: MD result: " + mdOutput.data);
var mdLen = md.getMdLength();
console.error("[Callback]: MD len: " + mdLen);
}
});
});
}
使用密钥协商操作
场景说明
使用密钥协商操作中,典型的场景有:
通信双方可以在一个公开的信道上通过相互传送一些消息,共同建立一个安全的共享秘密密钥。
接口及参数说明
详细接口说明可参考API参考。
实例名 | 接口名 | 描述 |
cryptoFramework | createKeyAgreement(algName : string) : KeyAgreement | 根据String设置的参数创建KeyAgreement对象 |
KeyAgreement | generateSecret(priKey : PriKey, pubKey : PubKey, callback : AsyncCallback<DataBlob>) : void | 使用callback方式进行密钥协商 |
KeyAgreement | generateSecret(priKey : PriKey, pubKey : PubKey) : Promise<DataBlob> | 使用Promise方式进行密钥协商 |
开发步骤
- 生成ECC密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成ECC非对称密钥。
- 基于ECC密钥的私钥及公钥执行ECDH操作。
import cryptoFramework from "@ohos.security.cryptoFramework"
let globalKeyPair;
function ecdhPromise() {
let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
let eccKeyAgreement = cryptoFramework.createKeyAgreement("ECC256");
let keyGenPromise = eccGenerator.generateKeyPair();
keyGenPromise.then( keyPair => {
globalKeyPair = keyPair;
return eccKeyAgreement.generateSecret(keyPair.priKey, keyPair.pubKey);
}).then((secret) => {
console.info("ecdh output is " + secret.data);
}).catch((error) => {
console.error("ecdh error.");
});
}
function ecdhCallback() {
let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
let eccKeyAgreement = cryptoFramework.createKeyAgreement("ECC256");
eccGenerator.generateKeyPair(function (err, keyPair) {
globalKeyPair = keyPair;
eccKeyAgreement.generateSecret(keyPair.priKey, keyPair.pubKey, function (err, secret) {
if (err) {
console.error("ecdh error.");
return;
}
console.info("ecdh output is " + secret.data);
});
});
}
使用消息认证码操作
场景说明
消息认证码操作主要应用于身份认证的场景:
Mac(message authentication code)可以对消息进行完整性校验,通过使用双方共享的密钥,识别出信息伪装篡改等行为
用户指定摘要算法(如SHA256)生成消息认证码Mac实例,输入对称密钥初始化Mac,并传入单段或多段需要摘要的信息,进行消息认证码计算,并获取消息认证码计算结果,在指定算法后可获取当前算法名与消息认证码计算长度(字节)。
接口及参数说明
详细接口说明可参考API参考。
实例名 | 接口名 | 描述 |
cryptoFramework | function createMac(algName : string) : Mac; | 指定摘要算法,生成消息认证码实例Mac |
Mac | init(key : SymKey, callback : AsyncCallback<void>) : void; | 接收输入对称密钥,通过Callback的方式,异步初始化MAC |
Mac | init(key : SymKey) : Promise<void>; | 接收输入对称密钥,通过Promise的方式,异步初始化MAC |
Mac | update(input : DataBlob, callback : AsyncCallback<void>) : void; | 接受输入数据,通过Callback的方式,异步更新MAC |
Mac | update(input : DataBlob) : Promise<void>; | 接受输入数据,通过Promise的方式,异步更新MAC |
Mac | doFinal(callback : AsyncCallback<DataBlob>) : void; | 通过Callback的方式,返回MAC计算结果 |
Mac | doFinal() : Promise<DataBlob>; | 通过Promise的方式,返回MAC计算结果 |
Mac | getMacLength() : number; | 获取MAC的长度(由指定的摘要算法决定) |
Mac | readonly algName : string; | 获取当前设置的摘要算法名 |
开发步骤
- 设置算法,通过接口createMac生成消息认证码操作实例
- 接受输入对称密钥,通过接口init,初始化Mac
- 接受数据,通过接口update,更新Mac,此步骤可重复
- 通过接口doFinal,返回Mac计算结果
- 获取当前摘要算法名与Mac计算长度
import cryptoFramework from "@ohos.security.cryptoFramework"
// turn string into uint8Arr
function stringToUint8Array(str) {
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array;
}
// generate blob with this func
function GenDataBlob(dataBlobLen) {
var dataBlob;
if (dataBlobLen == 12) {
dataBlob = {data: stringToUint8Array("my test data")};
} else {
console.error("GenDataBlob: dataBlobLen is invalid");
dataBlob = {data: stringToUint8Array("my test data")};
}
return dataBlob;
}
// process by promise
function doHmacByPromise(algName) {
var mac;
try {
mac = cryptoFramework.createMac(algName);
} catch (error) {
console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
}
console.error("[Promise]: Mac algName is: " + mac.algName);
var KeyBlob = {
data : stringToUint8Array("12345678abcdefgh")
}
var symKeyGenerator = cryptoFramework.createSymKeyGenerator("AES128");
var promiseConvertKey = symKeyGenerator.convertKey(KeyBlob);
promiseConvertKey.then(symKey => {
var promiseMacInit = mac.init(symKey);
return promiseMacInit;
}).then(() => {
var promiseMacUpdate = mac.update(GenDataBlob(12));
return promiseMacUpdate;
}).then(() => {
var PromiseMacDoFinal = mac.doFinal();
return PromiseMacDoFinal;
}).then(macOutput => {
console.error("[Promise]: HMAC result: " + macOutput.data);
var macLen = mac.getMacLength();
console.error("[Promise]: MAC len: " + macLen);
}).catch(error => {
console.error("[Promise]: error: " + error.message);
});
}
// process by callback
function doHmacByCallback(algName) {
var mac;
try {
mac = cryptoFramework.createMac(algName);
} catch (error) {
AlertDialog.show({message: "[Callback]: error code: " + error.code + ", message is: " + error.message});
console.error("[Callback]: error code: " + error.code + ", message is: " + error.message);
}
var KeyBlob = {
data : stringToUint8Array("12345678abcdefgh")
}
var symKeyGenerator = cryptoFramework.createSymKeyGenerator("AES128");
symKeyGenerator.convertKey(KeyBlob, (err, symKey) => {
if (err) {
console.error("[Callback]: err: " + err.code);
}
mac.init(symKey, (err1, ) => {
if (err1) {
console.error("[Callback]: err: " + err1.code);
}
mac.update(GenDataBlob(12), (err2, ) => {
if (err2) {
console.error("[Callback]: err: " + err2.code);
}
mac.doFinal((err3, macOutput) => {
if (err3) {
console.error("[Callback]: err: " + err3.code);
} else {
console.error("[Callback]: HMAC result: " + macOutput.data);
var macLen = mac.getMacLength();
console.error("[Callback]: MAC len: " + macLen);
}
});
});
});
});
}
使用随机数操作
场景说明
使用随机数操作的主要场景为:
- 用户生成随机数Random实例,输入随机数生成的长度(字节),生成指定长度的随机数。
- 用户使用生成的随机数作为参数,进行种子设置。
接口及参数说明
详细接口说明可参考API参考。
实例名 | 接口名 | 描述 |
cryptoFramework | function createRandom() : Random; | 生成随机数Random实例 |
Random | generateRandom(len : number, callback: AsyncCallback<DataBlob>) : void; | 接受输入长度,通过Callback,异步生成随机数 |
Random | generateRandom(len : number) : Promise<DataBlob>; | 接受输入长度,通过Promise,异步生成随机数 |
Random | setSeed(seed : DataBlob) : void; | 接受输入Blob,设置种子 |
开发步骤
- 通过接口createRandom生成随机数操作实例
- 接受输入长度,通过接口generateRandom,生成指定长度的随机数
- 接受DataBlob数据,通过接口setSeed,为随机数生成池设置种子
import cryptoFramework from "@ohos.security.cryptoFramework"
// process by promise
function doRandByPromise(len) {
var rand;
try {
rand = cryptoFramework.createRandom();
} catch (error) {
console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
}
var promiseGenerateRand = rand.generateRandom(len);
promiseGenerateRand.then(randData => {
console.error("[Promise]: rand result: " + randData.data);
try {
rand.setSeed(randData);
} catch (error) {
console.log("setSeed failed, errCode: " + error.code + ", errMsg: " + error.message);
}
}).catch(error => {
console.error("[Promise]: error: " + error.message);
});
}
// process by callback
function doRandByCallback(len) {
var rand;
try {
rand = cryptoFramework.createRandom();
} catch (error) {
console.error("[Callback]: error code: " + error.code + ", message is: " + error.message);
}
rand.generateRandom(len, (err, randData) => {
if (err) {
console.error("[Callback]: err: " + err.code);
} else {
console.error("[Callback]: generate random result: " + randData.data);
try {
rand.setSeed(randData);
} catch (error) {
console.log("setSeed failed, errCode: " + error.code + ", errMsg: " + error.message);
}
}
});
}