cryptoFramework-guidelines.md 109.9 KB
Newer Older
W
wangyongzhong2 已提交
1 2
# 加解密算法库框架开发指导

X
xwb 已提交
3
> **说明:**
W
wangyongzhong2 已提交
4 5 6
>
> 本开发指导基于API version 9,OH SDK版本3.2.7以上,适用于JS语言开发

X
xwb 已提交
7
## 密钥对象生成与转换操作
W
wangyongzhong2 已提交
8

X
xwb 已提交
9
### 场景说明
W
wangyongzhong2 已提交
10 11 12 13

使用密钥生成操作中,典型的场景有:

1. 随机生成算法库密钥对象。该对象可用于后续的加解密等操作。
X
xwb 已提交
14 15 16 17
2. 根据密钥参数生成指定的算法库密钥对象。该对象可用于后续的加解密等操作。
3. 根据指定数据生成算法库密钥对象(也就是将外部或存储的二进制数据转换为算法库的密钥对象)。该对象可用于后续的加解密等操作。
4. 获取算法库密钥对象的二进制数据,用于存储或传输。
5. 对于非对称密钥,获取密钥对象的参数属性,用于存储或运输。
W
wangyongzhong2 已提交
18

X
xwb 已提交
19 20 21
> **说明:**
>
> 密钥对象Key包括对称密钥SymKey和非对称密钥(公钥PubKey和私钥PriKey),其中公钥和私钥组成密钥对KeyPair。密钥之间的具体关系可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)。
W
wangyongzhong2 已提交
22

X
xwb 已提交
23
### 接口及参数说明
W
wangyongzhong2 已提交
24 25 26 27 28 29 30 31

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)

以上场景涉及的常用接口如下表所示:

|实例名|接口名|描述|
|---|---|---|
|cryptoFramework|createAsyKeyGenerator(algName : string) : AsyKeyGenerator|根据algName设置的非对称密钥规格,创建非对称密钥生成器对象|
X
xwb 已提交
32
|cryptoFramework|createAsyKeyGeneratorBySpec(asyKeySpec: AsyKeySpec): AsyKeyGeneratorBySpec;|根据密钥参数设置的非对称密钥规格,创建非对称密钥生成器对象|
W
wangyongzhong2 已提交
33 34 35 36 37 38 39 40 41 42 43
|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对象<br/>(允许公钥/私钥为null,即只传入单一公钥或私钥,生成只携带公钥或私钥的KeyPair对象) |
| AsyKeyGenerator          | convertKey(pubKey : DataBlob, priKey : DataBlob) : Promise\<KeyPair> | 使用Promise方式,根据指定的公钥和私钥二进制数据生成KeyPair对象<br/>(允许公钥/私钥为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) |

X
xwb 已提交
44
### 随机生成RSA密钥对,并获得二进制数据
X
xwb 已提交
45

W
wangyongzhong2 已提交
46 47
示例1:随机生成非对称密钥KeyPair,并获得二进制数据(场景1、3)

X
xwb 已提交
48 49 50
1. 创建非对称密钥生成器。
2. 通过非对称密钥生成器随机生成非对称密钥。
3. 获取密钥对象的二进制数据。
W
wangyongzhong2 已提交
51 52 53

以使用Promise方式随机生成RSA密钥(1024位,素数个数为2)为例:

X
xwb 已提交
54
```js
W
wangyongzhong2 已提交
55 56 57 58
import cryptoFramework from '@ohos.security.cryptoFramework';

function generateAsyKey() {
  // 创建非对称密钥生成器
胡啸天 已提交
59
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024|PRIMES_2');
W
wangyongzhong2 已提交
60 61
  // 通过非对称密钥生成器,随机生成非对称密钥
  let keyGenPromise = rsaGenerator.generateKeyPair();
胡啸天 已提交
62
  keyGenPromise.then(keyPair => {
W
wutiantian_gitee 已提交
63 64
    let pubKey = keyPair.pubKey;
    let priKey = keyPair.priKey;
W
wangyongzhong2 已提交
65
    // 获取非对称密钥的二进制数据
W
wutiantian_gitee 已提交
66 67
    let pkBlob = pubKey.getEncoded();
    let skBlob = priKey.getEncoded();
胡啸天 已提交
68 69
    AlertDialog.show({ message: 'pk bin data' + pkBlob.data });
    AlertDialog.show({ message: 'sk bin data' + skBlob.data });
W
wangyongzhong2 已提交
70 71 72 73
  })
}
```

X
xwb 已提交
74
### 随机生成AES密钥,并获得二进制数据
X
xwb 已提交
75

W
wangyongzhong2 已提交
76 77
示例2:随机生成对称密钥SymKey,并获得二进制数据(场景1、3)

X
xwb 已提交
78 79 80
1. 创建对称密钥生成器。
2. 通过对称密钥生成器随机生成对称密钥。
3. 获取算法库密钥对象的二进制数据。
W
wangyongzhong2 已提交
81 82 83

以使用Promise方式随机生成AES密钥(256位)为例:

X
xwb 已提交
84
```js
W
wangyongzhong2 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98
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();
胡啸天 已提交
99 100
  promiseSymKey.then(key => {
    // 获取对称密钥的二进制数据,输出长度为256bit,以16进制表示,长度为64,即32字节
W
wangyongzhong2 已提交
101 102 103 104 105 106
    let encodedKey = key.getEncoded();
    console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
  })
}
```

X
xwb 已提交
107 108
### 根据RSA密钥二进制数据,生成密钥对

W
wangyongzhong2 已提交
109 110 111 112 113
示例3:根据指定的RSA非对称密钥二进制数据,生成KeyPair对象(场景2)

1. 获取RSA公钥或私钥二进制数据,公钥需满足ASN.1语法、X.509规范、DER编码格式,私钥需满足ASN.1语法、PKCS#8规范、DER编码格式。
2. 创建AsyKeyGenerator对象,调用convertKey方法,传入公钥二进制和私钥二进制(二者非必选项,可只传入其中一个),转换为KeyPair对象。

X
xwb 已提交
114
```js
W
wangyongzhong2 已提交
115 116 117
import cryptoFramework from '@ohos.security.cryptoFramework';

function convertAsyKey() {
胡啸天 已提交
118 119 120 121 122 123
  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, (err, keyPair) => {
    if (err) {
      AlertDialog.show({ message: 'Convert keyPair fail' });
124
      return;
W
wangyongzhong2 已提交
125
    }
胡啸天 已提交
126
    AlertDialog.show({ message: 'Convert keyPair success' });
W
wangyongzhong2 已提交
127 128 129 130
  })
}
```

X
xwb 已提交
131 132 133
> **说明:**
>
> 当前convertKey操作,公钥只支持转换满足X.509规范的DER格式,私钥只支持PKCS#8规范的DER格式。
W
wangyongzhong2 已提交
134

X
xwb 已提交
135
### 根据ECC密钥二进制数据,生成密钥对
X
xwb 已提交
136

W
wangyongzhong2 已提交
137 138 139 140 141
示例4:根据指定的ECC非对称密钥二进制数据,生成KeyPair对象(场景2、3)

1. 获取ECC二进制密钥数据,封装成DataBlob对象。
2. 调用convertKey方法,传入公钥二进制和私钥二进制(二者非必选项,可只传入其中一个),转换为KeyPair对象。

X
xwb 已提交
142
```js
143 144
import cryptoFramework from '@ohos.security.cryptoFramework';

W
wangyongzhong2 已提交
145
function convertEccAsyKey() {
胡啸天 已提交
146 147 148 149 150 151 152 153
  let pubKeyArray = new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 83, 96, 142, 9, 86, 214, 126, 106, 247, 233, 92, 125, 4, 128, 138, 105, 246, 162, 215, 71, 81, 58, 202, 121, 26, 105, 211, 55, 130, 45, 236, 143, 55, 16, 248, 75, 167, 160, 167, 106, 2, 152, 243, 44, 68, 66, 0, 167, 99, 92, 235, 215, 159, 239, 28, 106, 124, 171, 34, 145, 124, 174, 57, 92]);
  let priKeyArray = new Uint8Array([48, 49, 2, 1, 1, 4, 32, 115, 56, 137, 35, 207, 0, 60, 191, 90, 61, 136, 105, 210, 16, 27, 4, 171, 57, 10, 61, 123, 40, 189, 28, 34, 207, 236, 22, 45, 223, 10, 189, 160, 10, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7]);
  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' });
154
      return;
胡啸天 已提交
155 156 157
    }
    AlertDialog.show({ message: 'Convert keyPair success' });
  })
W
wangyongzhong2 已提交
158 159 160
}
```

X
xwb 已提交
161
### 根据3DES密钥二进制数据,生成密钥
X
xwb 已提交
162

W
wangyongzhong2 已提交
163 164
示例5:根据指定的对称密钥二进制数据,生成SymKey对象(场景2、3)

X
xwb 已提交
165 166 167
1. 创建对称密钥生成器。
2. 通过对称密钥生成器,根据指定的对称密钥二进制数据,生成SymKey对象。
3. 获取算法库密钥对象的二进制数据。
W
wangyongzhong2 已提交
168 169 170

以使用callback方式生成3DES密钥(3DES密钥只能为192位)为例:

X
xwb 已提交
171
```js
W
wangyongzhong2 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184
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,
胡啸天 已提交
185
    0xba, 0x3b, 0xc2, 0x71, 0xab, 0xa0, 0x30, 0x72]; // keyLen = 192 (24 bytes)
W
wangyongzhong2 已提交
186
  let keyMaterial = new Uint8Array(arr);
胡啸天 已提交
187
  return { data: keyMaterial };
W
wangyongzhong2 已提交
188 189
}

胡啸天 已提交
190
function testConvertSymKey() {
W
wangyongzhong2 已提交
191 192 193 194 195 196
  // 生成对称密钥生成器
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator('3DES192');
  // 根据用户指定的数据,生成对称密钥
  let keyMaterialBlob = genKeyMaterialBlob();
  try {
    symKeyGenerator.convertKey(keyMaterialBlob, (error, key) => {
胡啸天 已提交
197
      if (error) { // 业务逻辑执行错误通过callback的第一个参数返回错误信息,即抛出异步异常
W
wangyongzhong2 已提交
198 199 200 201 202
        console.error(`convertKey error, ${error.code}, ${error.message}`);
        return;
      }
      console.info(`key algName: ${key.algName}`);
      console.info(`key format: ${key.format}`);
胡啸天 已提交
203
      let encodedKey = key.getEncoded(); // 获取对称密钥的二进制数据,输出长度为192bit的字节流,以16进制表示,长度为48,即24字节
W
wangyongzhong2 已提交
204 205
      console.info('key getEncoded hex: ' + uint8ArrayToShowStr(encodedKey.data));
    })
胡啸天 已提交
206
  } catch (error) { // 参数检查的错误以同步的方式立即抛出异常
W
wangyongzhong2 已提交
207 208 209 210 211
    console.error(`convertKey failed, ${error.code}, ${error.message}`);
    return;
  }
}
```
X
xwb 已提交
212

W
wutiantian_gitee 已提交
213 214
### 随机生成SM2密钥对,并获得二进制数据

W
wutiantian_gitee 已提交
215 216 217 218
> **说明:**
>
> 从API version 10开始, 支持SM2非对称密钥随机生成。

W
wutiantian_gitee 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
示例6:随机生成非对称密钥KeyPair,并获得二进制数据(场景1、3)

1. 创建非对称密钥生成器。
2. 通过非对称密钥生成器随机生成非对称密钥。
3. 获取密钥对象的二进制数据。

以使用Promise方式随机生成SM2密钥(256位)为例:

```js
import cryptoFramework from '@ohos.security.cryptoFramework';

function generateAsyKey() {
  // 创建非对称密钥生成器
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("SM2_256");
  // 通过非对称密钥生成器,随机生成非对称密钥
  let keyGenPromise = rsaGenerator.generateKeyPair();
胡啸天 已提交
235
  keyGenPromise.then(keyPair => {
W
wutiantian_gitee 已提交
236 237 238 239 240
    let pubKey = keyPair.pubKey;
    let priKey = keyPair.priKey;
    // 获取非对称密钥的二进制数据
    let pkBlob = pubKey.getEncoded();
    let skBlob = priKey.getEncoded();
胡啸天 已提交
241 242
    AlertDialog.show({ message: "pk bin data" + pkBlob.data });
    AlertDialog.show({ message: "sk bin data" + skBlob.data });
W
wutiantian_gitee 已提交
243 244 245 246 247 248
  })
}
```

### 随机生成SM4密钥,并获得二进制数据

W
wutiantian_gitee 已提交
249 250 251 252
 > **说明:**
 >
 > 从API version 10开始, 支持SM4密钥随机生成。

W
wutiantian_gitee 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
示例7:随机生成对称密钥SymKey,并获得二进制数据(场景1、3)

1. 创建对称密钥生成器。
2. 通过对称密钥生成器随机生成对称密钥。
3. 获取算法库密钥对象的二进制数据。

以使用Promise方式随机生成SM4密钥(128位)为例:

```js
import cryptoFramework from '@ohos.security.cryptoFramework';

// 字节流以16进制输出
function uint8ArrayToShowStr(uint8Array) {
  return Array.prototype.map
    .call(uint8Array, (x) => ('00' + x.toString(16)).slice(-2))
    .join('');
}

胡啸天 已提交
271
function testGenerateSM4Key() {
W
wutiantian_gitee 已提交
272 273 274 275
  // 创建对称密钥生成器
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator("SM4_128");
  // 通过密钥生成器随机生成对称密钥
  let promiseSymKey = symKeyGenerator.generateSymKey();
胡啸天 已提交
276 277
  promiseSymKey.then(key => {
    // 获取对称密钥的二进制数据,输出长度为128bit的字节流,以16进制表示,长度为32,即16字节
W
wutiantian_gitee 已提交
278 279 280 281 282 283 284 285
    let encodedKey = key.getEncoded();
    console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
  })
}
```

### 根据SM2密钥二进制数据,生成密钥对

W
wutiantian_gitee 已提交
286 287 288 289
 > **说明:**
 >
 > 从API version 10开始, 支持SM2密钥转换。

W
wutiantian_gitee 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
示例8:根据指定的SM2非对称密钥二进制数据,生成KeyPair对象(场景2、3)

1. 获取SM2二进制密钥数据,封装成DataBlob对象。
2. 调用convertKey方法,传入公钥二进制和私钥二进制(二者非必选项,可只传入其中一个),转换为KeyPair对象。

```js
import cryptoFramework from '@ohos.security.cryptoFramework';

function convertSM2AsyKey() {
    let pubKeyArray = new Uint8Array([48,89,48,19,6,7,42,134,72,206,61,2,1,6,8,42,129,28,207,85,1,130,45,3,66,0,4,90,3,58,157,190,248,76,7,132,200,151,208,112,230,96,140,90,238,211,155,128,109,248,40,83,214,78,42,104,106,55,148,249,35,61,32,221,135,143,100,45,97,194,176,52,73,136,174,40,70,70,34,103,103,161,99,27,187,13,187,109,244,13,7]);
    let priKeyArray = new Uint8Array([48,49,2,1,1,4,32,54,41,239,240,63,188,134,113,31,102,149,203,245,89,15,15,47,202,170,60,38,154,28,169,189,100,251,76,112,223,156,159,160,10,6,8,42,129,28,207,85,1,130,45]);
    let pubKeyBlob = { data: pubKeyArray };
    let priKeyBlob = { data: priKeyArray };
    let generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
    generator.convertKey(pubKeyBlob, priKeyBlob, (error, data) => {
        if (error) {
            AlertDialog.show({message : "Convert keypair fail"});
307
            return;
W
wutiantian_gitee 已提交
308 309 310 311 312 313
        }
        AlertDialog.show({message : "Convert KeyPair success"});
    })
}
```

X
xwb 已提交
314 315 316 317 318 319 320
## 非对称密钥对象根据参数生成与获取参数

### 场景说明

使用密钥生成操作中,典型的场景有:
1. 根据非对称密钥参数生成指定的算法库密钥对象。该对象可用于后续的加解密等操作。
2. 对于非对称密钥,获取密钥对象的参数属性,用于存储或运输。
X
xwb 已提交
321 322 323

> **说明:**
>
X
xwb 已提交
324 325 326 327 328 329 330 331 332 333 334
> 1. 从API version 10开始, 支持使用密钥参数来生成非对称密钥。
> 2. 非对称密钥(公钥PubKey和私钥PriKey),其中公钥和私钥组成密钥对KeyPair。非对称密钥参数具体可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)。

### 接口及参数说明

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md#asykeygeneratorbyspec10)

以上场景涉及的常用接口如下表所示:

|实例名|接口名|描述|
|---|---|---|
Z
zengyawen 已提交
335 336 337 338 339 340
|AsyKeyGeneratorBySpec|generateKeyPair(callback: AsyncCallback\<KeyPair>): void;|使用callback方式,根据密钥参数生成非对称密钥对象KeyPair
|AsyKeyGeneratorBySpec|generateKeyPair(): Promise\<KeyPair>;|使用Promise方式,根据密钥参数生成非对称密钥对象KeyPair
|AsyKeyGeneratorBySpec|generatePriKey(callback: AsyncCallback\<KeyPair>): void;|使用callback方式,根据密钥参数生成非对称私钥对象PriKey
|AsyKeyGeneratorBySpec|generatePriKey(): Promise\<KeyPair>;|使用Promise方式,根据密钥参数生成非对称私钥对象PriKey
|AsyKeyGeneratorBySpec|generatePubKey(callback: AsyncCallback\<KeyPair>): void;|使用callback方式,根据密钥参数生成非对称公钥对象PubKey
|AsyKeyGeneratorBySpec|generatePubKey(): Promise\<KeyPair>;|使用Promise方式,根据密钥参数生成非对称公钥对象PubKey
X
xwb 已提交
341 342 343 344
| PriKey | getAsyKeySpec(itemType: AsyKeySpecItem): bigint \| string \| number;  | 获取非对称密钥私钥对象的密钥参数属性 |
| PubKey | getAsyKeySpec(itemType: AsyKeySpecItem): bigint \| string \| number;  | 获取非对称密钥公钥对象的密钥参数属性 |

### 根据参数生成ECC密钥对,并获得密钥参数开发步骤
X
xwb 已提交
345

X
xwb 已提交
346 347
示例1:根据参数生成ECC密钥对,并获得密钥参数(场景1、2)

X
xwb 已提交
348 349 350
1. 创建根据密钥参数的非对称密钥生成器。
2. 通过根据密钥参数的非对称密钥生成器由指定密钥参数生成非对称密钥对。
3. 获取密钥对象的密钥参数属性。
X
xwb 已提交
351 352

以使用Promise方式根据密钥参数生成ECC密钥为例:
X
xwb 已提交
353 354

```js
X
xwb 已提交
355 356 357 358 359 360 361 362 363 364 365 366 367
import cryptoFramework from '@ohos.security.cryptoFramework';

// 打印bigint信息
function showBigIntInfo(bnName, bnValue) {
  console.warn(bnName + ":");
  console.warn(".   十进制: " + bnValue.toString());
  console.warn(".   16进制: " + bnValue.toString(16));
  console.warn(".   长度(bits): " + bnValue.toString(2).length);
}

// 根据密钥参数属性,构造ECC公私钥共有参数的sepc结构体
function genEccCommonSpec() {
  let fieldFp = {
胡啸天 已提交
368 369
    fieldType: "Fp",
    p: BigInt("0xffffffffffffffffffffffffffffffff000000000000000000000001")
X
xwb 已提交
370 371 372
  }

  let G = {
胡啸天 已提交
373 374
    x: BigInt("0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"),
    y: BigInt("0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34")
X
xwb 已提交
375 376 377
  }

  let eccCommonSpec = {
胡啸天 已提交
378 379 380 381 382 383 384 385
    algName: "ECC",
    specType: cryptoFramework.AsyKeySpecType.COMMON_PARAMS_SPEC,
    field: fieldFp,
    a: BigInt("0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe"),
    b: BigInt("0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4"),
    g: G,
    n: BigInt("0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d"),
    h: 1
X
xwb 已提交
386 387 388 389 390 391 392 393 394
  }
  return eccCommonSpec;
}

// 打印ECC密钥参数属性
function showEccSpecDetailInfo(key, keyType) {
  console.info("show detail of " + keyType + ":");
  try {
    let p = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_FP_P_BN);
395
    showBigIntInfo("--- p", p); // length is 224, hex : ffffffffffffffffffffffffffffffff000000000000000000000001
X
xwb 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

    let a = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_A_BN);
    showBigIntInfo("--- a", a); // length is 224, hex : fffffffffffffffffffffffffffffffefffffffffffffffffffffffe

    let b = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_B_BN);
    showBigIntInfo("--- b", b); // length is 224, hex : b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4

    let gX = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_G_X_BN);
    showBigIntInfo("--- gX", gX); // length is 224, hex : b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21

    let gY = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_G_Y_BN);
    showBigIntInfo("--- gY", gY); // length is 224, hex : bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34

    let n = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_N_BN);
    showBigIntInfo("--- n", n); // length is 224, hex : ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d

    let h = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_H_NUM);
413
    console.warn("--- h: " + h); // key h: 1
X
xwb 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

    let fieldType = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_FIELD_TYPE_STR);
    console.warn("--- field type: " + fieldType); // key field type: Fp

    let fieldSize = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_FIELD_SIZE_NUM);
    console.warn("--- field size: " + fieldSize); // key field size: 224

    let curveName = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_CURVE_NAME_STR);
    console.warn("--- curve name: " + curveName); // key curve name: NID_secp224r1

    if (keyType == "priKey") {
      let sk = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_SK_BN);
      showBigIntInfo("--- sk", sk);
    } else if (keyType == "pubKey") {
      let pkX = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_X_BN);
      showBigIntInfo("--- pkX", pkX);
      let pkY = key.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.ECC_PK_Y_BN);
      showBigIntInfo("--- pkY", pkY);
    }
  } catch (error) {
    console.error("getAsyKeySpec error");
    console.error("error code: " + error.code + ", message is: " + error.message);
  }
}

// 测试根据ECC公私钥公共密钥参数生成ECC密钥对,并获得其密钥参数属性
胡啸天 已提交
440
function testEccUseCommKeySpecGet() {
X
xwb 已提交
441 442 443 444
  try {
    let commKeySpec = genEccCommonSpec(); // 使用参数属性,构造ECC公私钥公共密钥参数对象
    let generatorBySpec = cryptoFramework.createAsyKeyGeneratorBySpec(commKeySpec); // 使用密钥参数对象创建生成器
    let keyPairPromise = generatorBySpec.generateKeyPair(); // 使用生成器创建ECC密钥对
胡啸天 已提交
445
    keyPairPromise.then(keyPair => {
X
xwb 已提交
446 447 448
      showEccSpecDetailInfo(keyPair.priKey, "priKey"); // 对私钥获取相关密钥参数属性
      showEccSpecDetailInfo(keyPair.pubKey, "pubKey"); // 对公钥获取相关密钥参数属性
    }).catch(error => {
胡啸天 已提交
449
      // 逻辑错误等异步异常在此捕获
X
xwb 已提交
450 451 452
      console.error("generateComm error");
      console.error("error code: " + error.code + ", message is: " + error.message);
    })
胡啸天 已提交
453 454
  } catch (error) {
    // 参数错误等同步异常在此捕获
X
xwb 已提交
455 456 457 458 459 460 461
    console.error("testEccUseCommSpec error");
    console.error("error code: " + error.code + ", message is: " + error.message);
  }
}
```

### 根据参数生成RSA公钥,并获得密钥参数属性
X
xwb 已提交
462

X
xwb 已提交
463 464
示例2:根据参数生成RSA公钥,并获得密钥参数(场景1、2)

X
xwb 已提交
465 466 467
1. 创建根据密钥参数的非对称密钥生成器。
2. 通过根据密钥参数的非对称密钥生成器由指定密钥参数生成非对称密钥的公钥。
3. 获取密钥对象的密钥参数属性。
X
xwb 已提交
468 469

以使用Callback方式根据密钥参数生成RSA公钥为例:
X
xwb 已提交
470
```js
X
xwb 已提交
471
// RSA公钥密钥参数生成函数
胡啸天 已提交
472 473 474 475 476 477 478 479
function genRsaPubKeySpec(nIn: bigint, eIn: bigint) {
  let rsaCommSpec = { n: nIn, algName: "RSA", specType: cryptoFramework.AsyKeySpecType.COMMON_PARAMS_SPEC };
  let rsaPubKeySpec = {
    params: rsaCommSpec,
    pk: eIn,
    algName: "RSA",
    specType: cryptoFramework.AsyKeySpecType.PUBLIC_KEY_SPEC
  };
X
xwb 已提交
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
  return rsaPubKeySpec;
}

// 根据密钥参数属性,构造RSA公钥密钥参数对象
function genRsa2048PubKeySpec() {
  let nIn = BigInt("0x9260d0750ae117eee55c3f3deaba74917521a262ee76007cdf8a56755ad73a1598a1408410a01434c3f5bc54a88b57fa19fc4328daea0750a4c44e88cff3b2382621b80f670464433e4336e6d003e8cd65bff211da144b88291c2259a00a72b711c116ef7686e8fee34e4d933c868187bdc26f7be071493c86f7a5941c3510806ad67b0f94d88f5cf5c02a092821d8626e8932b65c5bd8c92049c210932b7afa7ac59c0e886ae5c1edb00d8ce2c57633db26bd6639bff73cee82be9275c402b4cf2a4388da8cf8c64eefe1c5a0f5ab8057c39fa5c0589c3e253f0960332300f94bea44877b588e1edbde97cf2360727a09b775262d7ee552b3319b9266f05a25");
  let eIn = BigInt("0x010001");
  return genRsaPubKeySpec(nIn, eIn);
}

// 将RSA公钥密钥参数属性与预期值比较
function compareRsaPubKeyBySpec(rsaKeySpec, n, e) {
  if (rsaKeySpec.params.n != n) {
    return false;
  }
  if (rsaKeySpec.pk != e) {
    return false;
  }
  return true;
}

// 测试根据RSA公钥密钥参数生成RSA公钥,并获得其密钥参数属性,与预期值做比较
function rsaUsePubKeySpecGetCallback() {
  let rsaPubKeySpec = genRsa2048PubKeySpec();
  let rsaGeneratorSpec = cryptoFramework.createAsyKeyGeneratorBySpec(rsaPubKeySpec);
  rsaGeneratorSpec.generatePubKey((error, key) => {
    let pubKey = key;
    let nBN = pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.RSA_N_BN);
    let eBN = pubKey.getAsyKeySpec(cryptoFramework.AsyKeySpecItem.RSA_PK_BN);
    if (compareRsaPubKeyBySpec(rsaPubKeySpec, nBN, eBN) != true) {
胡啸天 已提交
510
      AlertDialog.show({ message: "error pub key big number" });
X
xwb 已提交
511 512 513 514 515 516 517 518 519
    } else {
      console.info("n, e in the pubKey are same as the spec.");
    }
    if (error) {
      console.error("generate pubKey error" + "error code: " + error.code + "error message" + error.message);
    }
  });
}
```
W
wangyongzhong2 已提交
520 521 522

## 使用加解密操作

X
xwb 已提交
523
### 场景说明
W
wangyongzhong2 已提交
524 525

在数据存储或传输场景中,可以使用加解密操作用于保证数据的机密性,防止敏感数据泄露。使用加解密操作中,典型的场景有:
X
xwb 已提交
526 527 528
1. 使用对称密钥的加解密操作。
2. 使用非对称密钥的加解密操作。
3. 使用RSA, PKCS1_OAEP填充模式时,获取、设置CipherSpecItem参数。
W
wangyongzhong2 已提交
529

X
xwb 已提交
530 531
> **说明:**
>
X
xwb 已提交
532 533
> 1. 从API version 10开始, 支持RSA使用PKCS1_OAEP填充模式时,获取、设置[CipherSpecItem](../reference/apis/js-apis-cryptoFramework.md#cipherspecitem10)参数。
> 2. 从API version 10开始,支持加解密时字符串参数不带密钥长度。
W
wangyongzhong2 已提交
534

X
xwb 已提交
535
### 接口及参数说明
536

X
xwb 已提交
537
详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)<br/>由于密码算法的复杂性,在选取不同规格和参数时,开发差异较大,无法通过代码示例一一列举,请仔细阅读API参考资料中的相关接口,确保使用正确。
W
wangyongzhong2 已提交
538 539 540 541 542 543 544 545 546 547 548 549

以上场景设计的常用接口如下表所示:

|实例名|接口名|描述|
|---|---|---|
|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方式结束对所有数据的加解密|
X
xwb 已提交
550 551
|Cipher|getCipherSpec(itemType: CipherSpecItem): string \| Uint8Array|获取加解密的参数,当前仅支持RSA算法|
|Cipher|setCipherSpec(itemType: CipherSpecItem, itemValue: Uint8Array): void|设置加解密的参数,当前仅支持RSA算法|
W
wangyongzhong2 已提交
552

X
xwb 已提交
553
### AES GCM以Promise方式加解密开发步骤:
W
wangyongzhong2 已提交
554

X
xwb 已提交
555
示例1:使用AES对称密钥的加解密操作
W
wangyongzhong2 已提交
556 557

1. 创建对称密钥生成器。
X
xwb 已提交
558
2. 通过密钥生成器随机生成对称密钥。
W
wangyongzhong2 已提交
559 560 561
3. 创建加解密生成器。
4. 通过加解密生成器加密或解密数据。

X
xwb 已提交
562
```js
W
wangyongzhong2 已提交
563 564 565 566 567 568 569 570
import cryptoFramework from '@ohos.security.cryptoFramework';

var globalCipher;
var globalGcmParams;
var globalKey;
var globalCipherText;

function genGcmParamsSpec() {
胡啸天 已提交
571
  let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes
W
wangyongzhong2 已提交
572
  let dataIv = new Uint8Array(arr);
胡啸天 已提交
573
  let ivBlob = { data: dataIv };
W
wangyongzhong2 已提交
574

胡啸天 已提交
575
  arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes
W
wangyongzhong2 已提交
576
  let dataAad = new Uint8Array(arr);
胡啸天 已提交
577
  let aadBlob = { data: dataAad };
W
wangyongzhong2 已提交
578

胡啸天 已提交
579
  arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
W
wangyongzhong2 已提交
580
  let dataTag = new Uint8Array(arr);
胡啸天 已提交
581
  let tagBlob = { data: dataTag }; // GCM的authTag在加密时从doFinal结果中获取,在解密时填入init函数的params参数中
X
xwb 已提交
582

胡啸天 已提交
583
  let gcmParamsSpec = { iv: ivBlob, aad: aadBlob, authTag: tagBlob, algName: "GcmParamsSpec" };
W
wangyongzhong2 已提交
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
  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;
}

// AES GCM模式示例,自动生成密钥(promise写法)
function testAesGcm() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('testAesGcm');
    }, 10)
  }).then(() => {
    // 生成对称密钥生成器
    let symAlgName = 'AES128';
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
    if (symKeyGenerator == null) {
      console.error('createSymKeyGenerator failed');
      return;
    }
    console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);
    // 通过密钥生成器随机生成128位长度的对称密钥
    let promiseSymKey = symKeyGenerator.generateSymKey();
    // 构造参数
    globalGcmParams = genGcmParamsSpec();

    // 生成加解密生成器
    let cipherAlgName = 'AES128|GCM|PKCS7';
    try {
      globalCipher = cryptoFramework.createCipher(cipherAlgName);
      console.info(`cipher algName: ${globalCipher.algName}`);
    } catch (error) {
      console.error(`createCipher failed, ${error.code}, ${error.message}`);
      return;
    }
    return promiseSymKey;
  }).then(key => {
胡啸天 已提交
643 644 645 646
    let encodedKey = key.getEncoded();
    console.info('key hex:' + uint8ArrayToShowStr(encodedKey.data));
    globalKey = key;
    return key;
W
wangyongzhong2 已提交
647
  }).then(key => {
胡啸天 已提交
648 649 650 651
    // 初始化加解密操作环境:开始加密
    let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
    let promiseInit = globalCipher.init(mode, key, globalGcmParams); // init
    return promiseInit;
W
wangyongzhong2 已提交
652
  }).then(() => {
胡啸天 已提交
653 654 655
    let plainText = {data : stringToUint8Array('this is test!')};
    let promiseUpdate = globalCipher.update(plainText); // update
    return promiseUpdate;
W
wangyongzhong2 已提交
656
  }).then(updateOutput => {
胡啸天 已提交
657 658 659
    globalCipherText = updateOutput;
    let promiseFinal = globalCipher.doFinal(null); // doFinal
    return promiseFinal;
W
wangyongzhong2 已提交
660
  }).then(authTag => {
胡啸天 已提交
661 662 663
    // GCM模式需要从doFinal的输出中取出加密后的认证信息并填入globalGcmParams,在解密时传入init()
    globalGcmParams.authTag = authTag;
    return;
W
wangyongzhong2 已提交
664
  }).then(() => {
胡啸天 已提交
665 666 667 668
    // 初始化加解密操作环境:开始解密
    let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
    let promiseInit = globalCipher.init(mode, globalKey, globalGcmParams); // init
    return promiseInit;
W
wangyongzhong2 已提交
669
  }).then(() => {
胡啸天 已提交
670 671
    let promiseUpdate = globalCipher.update(globalCipherText); // update
    return promiseUpdate;
W
wangyongzhong2 已提交
672
  }).then(updateOutput => {
胡啸天 已提交
673 674 675
    console.info('decrypt plainText: ' + uint8ArrayToString(updateOutput.data));
    let promiseFinal = globalCipher.doFinal(null); // doFinal
    return promiseFinal;
W
wangyongzhong2 已提交
676
  }).then(finalOutput => {
胡啸天 已提交
677 678 679
    if (finalOutput == null) { // 使用finalOutput.data前,先判断结果是否为null
      console.info('GCM finalOutput is null');
    }
W
wangyongzhong2 已提交
680
  }).catch(error => {
胡啸天 已提交
681
    console.error(`catch error, ${error.code}, ${error.message}`);
W
wangyongzhong2 已提交
682 683 684 685
  })
}
```

X
xwb 已提交
686 687 688 689 690
### 3DES ECB以callback方式加解密开发步骤:

示例2:使用3DES对称密钥的加解密操作

1. 创建对称密钥生成器。
X
xwb 已提交
691
2. 通过已有二进制数据生成密钥。
X
xwb 已提交
692 693
3. 创建加解密生成器。
4. 通过加解密生成器加密或解密数据。
W
wangyongzhong2 已提交
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774

```js
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 symAlgName = '3DES192';
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
  if (symKeyGenerator == null) {
    console.error('createSymKeyGenerator failed');
    return;
  }
  console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);

  // 生成加解密生成器
  let cipherAlgName = '3DES192|ECB|PKCS7';
  try {
    globalCipher = cryptoFramework.createCipher(cipherAlgName);
    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
胡啸天 已提交
775 776
      globalCipher.init(mode, key, null, (err,) => {
        let plainText = { data: stringToUint8Array('this is test!') };
W
wangyongzhong2 已提交
777 778 779 780 781 782 783 784 785 786 787 788 789 790
        // 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);
胡啸天 已提交
791
              globalCipherText = { data: globalCipherText };
W
wangyongzhong2 已提交
792 793 794 795
            }
            // 初始化加解密操作环境:开始解密
            let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
            // init
胡啸天 已提交
796
            globalCipher.init(mode, globalKey, null, (err,) => {
W
wangyongzhong2 已提交
797 798 799 800 801
              // update
              globalCipher.update(globalCipherText, (err, updateOutput) => {
                console.info('decrypt plainText: ' + uint8ArrayToString(updateOutput.data));
                // doFinal
                globalCipher.doFinal(null, (error, finalOutput) => {
胡啸天 已提交
802 803
                  if (finalOutput != null) { // 使用finalOutput.data前,先判断结果是否为null
                    console.info('decrypt plainText: ' + uint8ArrayToString(finalOutput.data));
W
wangyongzhong2 已提交
804 805 806 807 808 809 810 811 812 813 814 815 816 817
                  }
                })
              })
            })
          })
        })
      })
    })
  } catch (error) {
    console.error(`convertKey failed, ${error.code}, ${error.message}`);
    return;
  }
}
```
818

X
xwb 已提交
819 820 821 822 823
### AES GCM以promise方式,分段update()加解密开发步骤:

示例3:使用AES对称密钥的分段update()加解密操作

1. 创建对称密钥生成器。
X
xwb 已提交
824
2. 通过已有二进制数据生成密钥。
X
xwb 已提交
825 826 827
3. 创建加解密生成器。
4. 通过加解密生成器加密或解密数据。

W
wangyongzhong2 已提交
828 829
以AES GCM以promise方式,分段update()实现加解密为例:

X
xwb 已提交
830
```js
W
wangyongzhong2 已提交
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
import cryptoFramework from '@ohos.security.cryptoFramework';

var globalCipher;
var globalGcmParams;
var globalKey;
var globalCipherText;
var globalPlainText;

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, algName : "GcmParamsSpec"};
  return gcmParamsSpec;
}

// 字节流以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;
}

// 算法库不限定update的次数和每次加解密的数据量,业务可根据自身内存情况对明文/密文进行多次分段。
function testAesMultiUpdate() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('testAesMultiUpdate');
    }, 10)
  }).then(() => {
    // 生成对称密钥生成器
    let symAlgName = 'AES128';
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
    if (symKeyGenerator == null) {
      console.error('createSymKeyGenerator failed');
      return;
    }
    console.info(`symKeyGenerator algName: ${symKeyGenerator.algName}`);
    // 通过密钥生成器随机生成128位长度的对称密钥
    let promiseSymKey = symKeyGenerator.generateSymKey();
    // 构造参数
    globalGcmParams = genGcmParamsSpec();

    // 生成加解密生成器
    let cipherAlgName = 'AES128|GCM|PKCS7';
    try {
      globalCipher = cryptoFramework.createCipher(cipherAlgName);
      console.info(`cipher algName: ${globalCipher.algName}`);
    } catch (error) {
      console.error(`createCipher failed, ${error.code}, ${error.message}`);
      return;
    }
    return promiseSymKey;
胡啸天 已提交
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
  })
    .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(async () => {
      let plainText = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // 假设明文总共43字节
      let messageArr = [];
      let updateLength = 20; // 假设每20字节分段update一次
      globalCipherText = [];

      for (let i = 0; i <= plainText.length; i++) {
        if ((i % updateLength == 0 || i == plainText.length) && messageArr.length != 0) {
          let message = new Uint8Array(messageArr);
          let messageBlob = { data: message };
          let updateOutput = await globalCipher.update(messageBlob); // 分段update
          // 把update的结果拼接起来,得到密文(有些情况下还需拼接doFinal的结果,这取决于分组模式
          // 和填充模式,本例中GCM模式的doFinal结果只包含authTag而不含密文,所以不需要拼接)
          globalCipherText = globalCipherText.concat(Array.from(updateOutput.data));
          messageArr = [];
        }
        if (i < plainText.length) {
          messageArr.push(plainText.charCodeAt(i));
        }
      }
      return;
    })
    .then(() => {
      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(async () => {
      let updateLength = 20;
      let updateTimes = Math.ceil(globalCipherText.length / updateLength); // 上取整
      globalPlainText = "";
      for (let i = 0; i < updateTimes; i++) {
        let messageArr = globalCipherText.slice(i * updateLength, (i + 1) * updateLength);
W
wangyongzhong2 已提交
957
        let message = new Uint8Array(messageArr);
胡啸天 已提交
958 959 960
        let messageBlob = { data: message };
        let updateOutput = await globalCipher.update(messageBlob); // 分段update
        globalPlainText += uint8ArrayToString(updateOutput.data); // 恢复出原始明文
W
wangyongzhong2 已提交
961
      }
胡啸天 已提交
962 963 964 965 966 967 968 969 970
      return;
    })
    .then(() => {
      let promiseFinal = globalCipher.doFinal(null); // doFinal
      return promiseFinal;
    })
    .then(finalOutput => {
      if (finalOutput == null) {
        console.info('GCM finalOutput is null');
W
wangyongzhong2 已提交
971
      }
胡啸天 已提交
972 973 974
      console.info(`decrypt output: ${globalPlainText}`);
    })
    .catch(error => {
W
wangyongzhong2 已提交
975
      console.error(`catch error, ${error.code}, ${error.message}`);
胡啸天 已提交
976
    })
W
wangyongzhong2 已提交
977 978
}
```
X
xwb 已提交
979

X
xwb 已提交
980
### RSA加解密开发步骤
X
xwb 已提交
981

X
xwb 已提交
982
示例4:使用RSA非对称密钥的加解密操作
W
wangyongzhong2 已提交
983 984 985 986 987

1. 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
2. 生成Cipher对象。通过createCipher接口创建Cipher对象,执行初始化操作,设置密钥及加解密模式。
3. 执行加解密操作。通过调用Cipher对象提供的doFinal接口,执行加密操作生成密文或执行解密操作生成明文。

X
xwb 已提交
988
```js
W
wangyongzhong2 已提交
989 990 991 992
import cryptoFramework from "@ohos.security.cryptoFramework"

let plan = "This is cipher test.";

X
xwb 已提交
993
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
994
function stringToUint8Array(str) {
X
xwb 已提交
995 996
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
W
wangyongzhong2 已提交
997 998
    arr.push(str.charCodeAt(i));
  }
X
xwb 已提交
999
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
1000 1001
}

X
xwb 已提交
1002
// 以Promise方式加密
W
wutiantian_gitee 已提交
1003
function encryptMessagePromise() {
X
xwb 已提交
1004
  // 生成非对称密钥生成器
W
wangyongzhong2 已提交
1005
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
X
xwb 已提交
1006
  // 生成加解密生成器
W
wangyongzhong2 已提交
1007
  let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
X
xwb 已提交
1008
  // 通过非对称秘钥生成器生成非对称密钥对
W
wangyongzhong2 已提交
1009 1010 1011
  let keyGenPromise = rsaGenerator.generateKeyPair();
  keyGenPromise.then(rsaKeyPair => {
    let pubKey = rsaKeyPair.pubKey;
X
xwb 已提交
1012
    // 初始化加解密操作环境:使用公钥开始加密
W
wangyongzhong2 已提交
1013 1014
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null);
  }).then(() => {
X
xwb 已提交
1015
    // doFinal
胡啸天 已提交
1016
    let input = { data: stringToUint8Array(plan) };
W
wangyongzhong2 已提交
1017 1018
    return cipher.doFinal(input);
  }).then(dataBlob => {
X
xwb 已提交
1019
    // 获取加密后的信息
W
wangyongzhong2 已提交
1020 1021 1022 1023
    console.info("EncryptOutPut is " + dataBlob.data);
  });
}

X
xwb 已提交
1024
// 以Callback方式加密
W
wangyongzhong2 已提交
1025
function encryptMessageCallback() {
X
xwb 已提交
1026
  // 生成非对称密钥生成器
W
wangyongzhong2 已提交
1027
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
X
xwb 已提交
1028
  // 生成加解密生成器
W
wangyongzhong2 已提交
1029
  let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
X
xwb 已提交
1030
  // 通过非对称秘钥生成器生成非对称密钥对
W
wangyongzhong2 已提交
1031 1032
  rsaGenerator.generateKeyPair(function (err, keyPair) {
    let pubKey = keyPair.pubKey;
X
xwb 已提交
1033
    // 初始化加解密操作环境:使用公钥开始加密
W
wangyongzhong2 已提交
1034
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null, function (err, data) {
胡啸天 已提交
1035
      let input = { data: stringToUint8Array(plan) };
X
xwb 已提交
1036
      // doFinal
W
wangyongzhong2 已提交
1037
      cipher.doFinal(input, function (err, data) {
X
xwb 已提交
1038
        // 获取加密后的信息
W
wangyongzhong2 已提交
1039 1040 1041 1042 1043 1044
        console.info("EncryptOutPut is " + data.data);
      })
    })
  })
}

X
xwb 已提交
1045
// 以Promise方式加解密
W
wutiantian_gitee 已提交
1046
function decryptMessagePromise() {
X
xwb 已提交
1047
  // 生成非对称密钥生成器
W
wangyongzhong2 已提交
1048
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
X
xwb 已提交
1049
  // 生成加解密生成器,用于加密
W
wangyongzhong2 已提交
1050
  let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
X
xwb 已提交
1051
  // 生成加解密生成器,用于解密
W
wangyongzhong2 已提交
1052
  let decoder = cryptoFramework.createCipher("RSA1024|PKCS1");
X
xwb 已提交
1053
  // 通过非对称秘钥生成器生成非对称密钥对
W
wangyongzhong2 已提交
1054 1055 1056
  let keyGenPromise = rsaGenerator.generateKeyPair();
  let keyPair;
  let cipherDataBlob;
胡啸天 已提交
1057
  let input = { data: stringToUint8Array(plan) };
W
wangyongzhong2 已提交
1058 1059
  keyGenPromise.then(rsaKeyPair => {
    keyPair = rsaKeyPair;
X
xwb 已提交
1060
    // 初始化加解密操作环境:使用公钥开始加密
W
wangyongzhong2 已提交
1061
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null);
胡啸天 已提交
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
  })
    .then(() => {
      // 加密doFinal
      return cipher.doFinal(input);
    })
    .then(dataBlob => {
      // 获取加密后的信息,并用于解密的入参
      console.info("EncryptOutPut is " + dataBlob.data);
      AlertDialog.show({ message: "output" + dataBlob.data });
      cipherDataBlob = dataBlob;
      // 初始化加解密操作环境:使用私钥开始解密
      return decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null);
    })
    .then(() => {
      // 解密doFinal
      return decoder.doFinal(cipherDataBlob);
    })
    .then(decodeData => {
      // 验证解密后,数据与原先数据是否保持一致
      if (decodeData.data.toString() === input.data.toString()) {
        AlertDialog.show({ message: "decrypt success" });
        return;
      }
      AlertDialog.show({ message: "decrypt fail" });
    });
W
wangyongzhong2 已提交
1087 1088
}

X
xwb 已提交
1089
// 以Callback方式加解密
W
wangyongzhong2 已提交
1090
function decryptMessageCallback() {
X
xwb 已提交
1091
  // 生成非对称密钥生成器
W
wangyongzhong2 已提交
1092
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
X
xwb 已提交
1093
  // 生成加解密生成器,用于加密
W
wangyongzhong2 已提交
1094
  let cipher = cryptoFramework.createCipher("RSA1024|PKCS1");
X
xwb 已提交
1095
  // 生成加解密生成器,用于解密
W
wangyongzhong2 已提交
1096 1097
  let decoder = cryptoFramework.createCipher("RSA1024|PKCS1");
  let plainText = "this is cipher text";
胡啸天 已提交
1098
  let input = { data: stringToUint8Array(plainText) };
W
wangyongzhong2 已提交
1099 1100
  let cipherData;
  let keyPair;
X
xwb 已提交
1101
  // 通过非对称秘钥生成器生成非对称密钥对
W
wangyongzhong2 已提交
1102 1103
  rsaGenerator.generateKeyPair(function (err, newKeyPair) {
    keyPair = newKeyPair;
X
xwb 已提交
1104
    // 初始化加解密操作环境:使用公钥开始加密
W
wangyongzhong2 已提交
1105
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null, function (err, data) {
X
xwb 已提交
1106
      // 加密doFinal
W
wangyongzhong2 已提交
1107
      cipher.doFinal(input, function (err, data) {
X
xwb 已提交
1108
        // 获取加密后的信息,并用于解密的入参
胡啸天 已提交
1109
        AlertDialog.show({ message: "EncryptOutPut is " + data.data });
W
wangyongzhong2 已提交
1110
        cipherData = data;
X
xwb 已提交
1111
        // 初始化加解密操作环境:使用私钥开始解密
W
wangyongzhong2 已提交
1112
        decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null, function (err, data) {
X
xwb 已提交
1113
          // 解密doFinal
W
wangyongzhong2 已提交
1114
          decoder.doFinal(cipherData, function (err, data) {
X
xwb 已提交
1115
            // 验证解密后,数据与原先数据是否保持一致
W
wangyongzhong2 已提交
1116
            if (input.data.toString() === data.data.toString()) {
胡啸天 已提交
1117
              AlertDialog.show({ message: "decrype success" });
W
wangyongzhong2 已提交
1118 1119
              return;
            }
胡啸天 已提交
1120
            AlertDialog.show({ message: "decrype fail" });
W
wangyongzhong2 已提交
1121 1122 1123 1124 1125 1126 1127
          });
        });
      });
    });
  });
}
```
X
xwb 已提交
1128

X
xwb 已提交
1129
### RSA分段加解密开发步骤
X
xwb 已提交
1130

X
xwb 已提交
1131
示例5:使用RSA非对称密钥的分段加解密操作
1132

X
xwb 已提交
1133 1134 1135
1. 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
2. 生成Cipher对象。通过createCipher接口创建Cipher对象,执行初始化操作,设置密钥及加解密模式。
3. 执行加解密操作。通过调用Cipher对象提供的doFinal接口,执行加密操作生成密文或执行解密操作生成明文,多次调用doFinal实现分段。
X
xwb 已提交
1136 1137

```js
W
wangyongzhong2 已提交
1138 1139
import cryptoFramework from "@ohos.security.cryptoFramework"

X
xwb 已提交
1140
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
1141
function stringToUint8Array(str) {
X
xwb 已提交
1142 1143
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
W
wangyongzhong2 已提交
1144 1145
    arr.push(str.charCodeAt(i));
  }
X
xwb 已提交
1146
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
}

// 字节流转成可理解的字符串
function uint8ArrayToString(array) {
  let arrayString = '';
  for (let i = 0; i < array.length; i++) {
    arrayString += String.fromCharCode(array[i]);
  }
  return arrayString;
}

function encryptLongMessagePromise() {
  let globalPlainText = "This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!";
  let globalCipherOutput;
  let globalDecodeOutput;
  var globalKeyPair;
  let plainTextSplitLen = 64; // RSA每次加解密允许的原文长度大小与密钥位数和填充模式等有关,详细规格内容见overview文档
  let cipherTextSplitLen = 128; // RSA密钥每次加密生成的密文数据长度计算方式:密钥位数/8
  let keyGenName = "RSA1024";
  let cipherAlgName = "RSA1024|PKCS1";
  let asyKeyGenerator = cryptoFramework.createAsyKeyGenerator(keyGenName); // 创建非对称密钥生成器对象
  let cipher = cryptoFramework.createCipher(cipherAlgName); // 创建加密Cipher对象
  let decoder = cryptoFramework.createCipher(cipherAlgName); // 创建解密Decoder对象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("testRsaMultiDoFinal");
    }, 10);
  }).then(() => {
    return asyKeyGenerator.generateKeyPair(); // 生成rsa密钥
  }).then(keyPair => {
    globalKeyPair = keyPair; // 保存到密钥对全局变量
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, globalKeyPair.pubKey, null);
  }).then(async () => {
    globalCipherOutput = [];
    // 将原文按64字符进行拆分,循环调用doFinal进行加密,使用1024bit密钥时,每次加密生成128B长度的密文
    for (let i = 0; i < (globalPlainText.length / plainTextSplitLen); i++) {
      let tempStr = globalPlainText.substr(i * plainTextSplitLen, plainTextSplitLen);
      let tempBlob = { data : stringToUint8Array(tempStr) };
      let tempCipherOutput = await cipher.doFinal(tempBlob);
      globalCipherOutput = globalCipherOutput.concat(Array.from(tempCipherOutput.data));
    }
    console.info(`globalCipherOutput len is ${globalCipherOutput.length}, data is: ${globalCipherOutput.toString()}`);
    return;
  }).then(() =>{
    return decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, globalKeyPair.priKey, null);
  }).then(async() => {
    globalDecodeOutput = [];
    // 将密文按128B进行拆分解密,得到原文后进行拼接
    for (let i = 0; i < (globalCipherOutput.length / cipherTextSplitLen); i++) {
      let tempBlobData = globalCipherOutput.slice(i * cipherTextSplitLen, (i + 1) * cipherTextSplitLen);
      let message = new Uint8Array(tempBlobData);
      let tempBlob = { data : message };
      let tempDecodeOutput = await decoder.doFinal(tempBlob);
      globalDecodeOutput += uint8ArrayToString(tempDecodeOutput.data);
    }
    if (globalDecodeOutput === globalPlainText) {
      console.info(`encode and decode success`);
    } else {
      console.info(`encode and decode error`);
    }
    return;
  }).catch(error => {
    console.error(`catch error, ${error.code}, ${error.message}`);
  })
}
```

X
xwb 已提交
1221 1222 1223 1224 1225
> **说明:**
>
> 1. 使用RSA加解密时,Cipher对象不可重复调用init方法初始化,在创建了一个加密Cipher对象后,如果要进行解密,则需要重新创建另一个Cipher对象执行解密操作。
> 2. RSA加密有长度限制,允许加密明文的最大长度见[加解密算法库框架概述](cryptoFramework-overview.md)中的基本概念加解密章节。
> 3. RSA解密每次允许解密的密文长度为,RSA密钥的位数/8。
X
xwb 已提交
1226 1227

### RSA加解密PKCS1_OAEP模式开发步骤
X
xwb 已提交
1228

X
xwb 已提交
1229 1230
示例6:使用RSA非对称密钥使用PKCS1_OAEP模式的以Promise形式的加解密操作

X
xwb 已提交
1231
1. 根据密钥参数生成RSA密钥。通过createAsyKeyGeneratorBySpec接口创建AsyKeyGeneratorBySpec对象,并生成RSA非对称密钥对(也可以使用createAsyKeyGenerator接口随机生成或转换得到RSA密钥对象)。
X
xwb 已提交
1232 1233
2. 生成Cipher对象。通过createCipher接口创建Cipher对象,执行初始化操作,设置密钥及加解密模式,在Update前通过setCipherSpec设置PKCS1_OAEP填充字节流P。
3. 执行加解密操作。通过调用Cipher对象提供的doFinal接口,执行加密操作生成密文或执行解密操作生成明文,需要加解密Cipher对象的字节流P一致。
X
xwb 已提交
1234 1235

```js
X
xwb 已提交
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
import cryptoFramework from "@ohos.security.cryptoFramework"

// 可理解的字符串转成字节流
function stringToUint8Array(str) {
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

// 根据密钥参数属性构造RSA非对称密钥对密钥参数
胡啸天 已提交
1248 1249 1250 1251 1252 1253 1254 1255 1256
function genRsaKeyPairSpec(nIn: bigint, eIn: bigint, dIn: bigint) {
  let rsaCommSpec = { n: nIn, algName: "RSA", specType: cryptoFramework.AsyKeySpecType.COMMON_PARAMS_SPEC };
  let rsaKeyPairSpec = {
    params: rsaCommSpec,
    sk: dIn,
    pk: eIn,
    algName: "RSA",
    specType: cryptoFramework.AsyKeySpecType.KEY_PAIR_SPEC
  };
X
xwb 已提交
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
  return rsaKeyPairSpec;
}

// 生成RSA2048密钥对参数
function genRsa2048KeyPairSpec() {
  let nIn = BigInt("0x9260d0750ae117eee55c3f3deaba74917521a262ee76007cdf8a56755ad73a1598a1408410a01434c3f5bc54a88b57fa19fc4328daea0750a4c44e88cff3b2382621b80f670464433e4336e6d003e8cd65bff211da144b88291c2259a00a72b711c116ef7686e8fee34e4d933c868187bdc26f7be071493c86f7a5941c3510806ad67b0f94d88f5cf5c02a092821d8626e8932b65c5bd8c92049c210932b7afa7ac59c0e886ae5c1edb00d8ce2c57633db26bd6639bff73cee82be9275c402b4cf2a4388da8cf8c64eefe1c5a0f5ab8057c39fa5c0589c3e253f0960332300f94bea44877b588e1edbde97cf2360727a09b775262d7ee552b3319b9266f05a25");
  let eIn = BigInt("0x010001");
  let dIn = BigInt("0x6a7df2ca63ead4dda191d614b6b385e0d9056a3d6d5cfe07db1daabee022db08212d97613d3328e0267c9dd23d787abde2afcb306aeb7dfce69246cc73f5c87fdf06030179a2114b767db1f083ff841c025d7dc00cd82435b9a90f695369e94df23d2ce458bc3b3283ad8bba2b8fa1ba62e2dce9accff3799aae7c840016f3ba8e0048c0b6cc4339af7161003a5beb864a0164b2c1c9237b64bc87556994351b27506c33d4bcdfce0f9c491a7d6b0628c7c852be4f0a9c3132b2ed3a2c8881e9aab07e20e17deb074691be677776a78b5c502e05d9bdde72126b3738695e2dd1a0a98a14247c65d8a7ee79432a092cb0721a12df798e44f7cfce0c498147a9b1");
  return genRsaKeyPairSpec(nIn, eIn, dIn);
}

function rsaUseSpecDecryptOAEPPromise() {
  let plan = "This is cipher test.";
  // 获得RSA密钥对密钥参数对象
  let rsaKeyPairSpec = genRsa2048KeyPairSpec();
  // 根据RSA密钥对参数生成RSA密钥对
  let rsaGeneratorSpec = cryptoFramework.createAsyKeyGeneratorBySpec(rsaKeyPairSpec);
  let keyGenPromise = rsaGeneratorSpec.generateKeyPair();
  let cipher = cryptoFramework.createCipher("RSA|PKCS1_OAEP|SHA256|MGF1_SHA1");
  let decoder = cryptoFramework.createCipher("RSA|PKCS1_OAEP|SHA256|MGF1_SHA1");
  let keyPair;
  let cipherDataBlob;
  // RSA加解密PKCS1-OAEP模式填充字节流P
胡啸天 已提交
1280 1281
  let pSource = new Uint8Array([1, 2, 3, 4]);
  let input = { data: stringToUint8Array(plan) };
X
xwb 已提交
1282 1283 1284 1285 1286
  // 生成密钥
  keyGenPromise.then(rsaKeyPair => {
    keyPair = rsaKeyPair;
    // 进行加密操作初始化
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null);
胡啸天 已提交
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340
  })
    .then(() => {
      // get和set操作可以放在Cipher对象init之后,此处对cipher进行set和get操作
      cipher.setCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_PSRC_UINT8ARR, pSource);
      let retP = cipher.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_PSRC_UINT8ARR);
      // 比较get出来的P字节流与set进去的P字节流是否一致
      if (retP.toString() != pSource.toString()) {
        AlertDialog.show({ message: "error init pSource" + retP });
      } else {
        console.info("pSource changed ==" + retP);
      }
      // 进行OAEP其他参数的get操作
      let md = cipher.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MD_NAME_STR);
      console.info("md == " + md);
      let mgf = cipher.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF_NAME_STR);
      console.info("mgf == " + mgf);
      let mgf1Md = cipher.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_MD_STR);
      console.info("mgf1Md == " + mgf1Md);
      return cipher.doFinal(input);
    })
    .then(dataBlob => {
      console.info("EncryptOutPut is " + dataBlob.data);
      cipherDataBlob = dataBlob;
      // get和set操作可以放在Cipher对象init之前,且与init之后等价,此处对decoder进行set和get操作
      decoder.setCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_PSRC_UINT8ARR, pSource);
      let retP = decoder.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_PSRC_UINT8ARR);
      // 比较get出来的P字节流与set进去的P字节流是否一致
      if (retP.toString() != pSource.toString()) {
        AlertDialog.show({ message: "error init pSource" + retP });
      } else {
        console.info("pSource changed ==" + retP);
      }
      // 进行OAEP其他参数的get操作
      let md = decoder.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MD_NAME_STR);
      console.info("md == " + md);
      let mgf = decoder.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF_NAME_STR);
      console.info("mgf == " + mgf);
      let mgf1Md = decoder.getCipherSpec(cryptoFramework.CipherSpecItem.OAEP_MGF1_MD_STR);
      console.info("mgf1Md == " + mgf1Md);
      // 初始化解密操作
      return decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null);
    })
    .then(() => {
      return decoder.doFinal(cipherDataBlob);
    })
    .then(decodeData => {
      // 解密成功
      if (decodeData.data.toString() === input.data.toString()) {
        console.info("oaep decrypt success");
        AlertDialog.show({ message: " oaep decrypt success" });
      } else {
        AlertDialog.show({ message: "oeap decrypt fail" });
      }
    });
X
xwb 已提交
1341 1342 1343
}
```

W
wutiantian_gitee 已提交
1344 1345
### SM2加解密开发步骤

W
wutiantian_gitee 已提交
1346 1347 1348 1349
> **说明:**
>
> 从API version 10开始, 支持SM2加解密。

W
wutiantian_gitee 已提交
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
示例7:使用SM2非对称密钥的加解密操作

1. 生成SM2密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成SM2非对称密钥。
2. 生成Cipher对象。通过createCipher接口创建Cipher对象,执行初始化操作,设置密钥及加解密模式。
3. 执行加解密操作。通过调用Cipher对象提供的doFinal接口,执行加密操作生成密文或执行解密操作生成明文。

```js
import cryptoFramework from "@ohos.security.cryptoFramework"

let plan = "This is cipher test.";

// 可理解的字符串转成字节流
function stringToUint8Array(str) {
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

// 以Promise方式加密
function encryptMessagePromise() {
  // 生成非对称密钥生成器
  let sm2Generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
  // 生成加解密生成器
  let cipher = cryptoFramework.createCipher("SM2_256|SM3");
  // 通过非对称秘钥生成器生成非对称密钥对
  let keyGenPromise = sm2Generator.generateKeyPair();
  keyGenPromise.then(sm2KeyPair => {
    let pubKey = sm2KeyPair.pubKey;
    // 初始化加解密操作环境:使用公钥开始加密
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null);
  }).then(() => {
    // doFinal
胡啸天 已提交
1384
    let input = { data: stringToUint8Array(plan) };
W
wutiantian_gitee 已提交
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402
    return cipher.doFinal(input);
  }).then(dataBlob => {
    // 获取加密后的信息
    console.info("EncryptOutPut is " + dataBlob.data);
  });
}

// 以Callback方式加密
function encryptMessageCallback() {
  // 生成非对称密钥生成器
  let sm2Generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
  // 生成加解密生成器
  let cipher = cryptoFramework.createCipher("SM2_256|SM3");
  // 通过非对称秘钥生成器生成非对称密钥对
  sm2Generator.generateKeyPair(function (err, keyPair) {
    let pubKey = keyPair.pubKey;
    // 初始化加解密操作环境:使用公钥开始加密
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, pubKey, null, function (err, data) {
胡啸天 已提交
1403
      let input = { data: stringToUint8Array(plan) };
W
wutiantian_gitee 已提交
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
      // doFinal
      cipher.doFinal(input, function (err, data) {
        // 获取加密后的信息
        console.info("EncryptOutPut is " + data.data);
      })
    })
  })
}

// 以Promise方式加解密
function decryptMessagePromise() {
  // 生成非对称密钥生成器
胡啸天 已提交
1416
  let sm2Generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
W
wutiantian_gitee 已提交
1417 1418 1419 1420 1421
  // 生成加解密生成器,用于加密
  let cipher = cryptoFramework.createCipher("SM2_256|SM3");
  // 生成加解密生成器,用于解密
  let decoder = cryptoFramework.createCipher("SM2_256|SM3");
  // 通过非对称秘钥生成器生成非对称密钥对
胡啸天 已提交
1422
  let keyGenPromise = sm2Generator.generateKeyPair();
W
wutiantian_gitee 已提交
1423 1424
  let keyPair;
  let cipherDataBlob;
胡啸天 已提交
1425
  let input = { data: stringToUint8Array(plan) };
W
wutiantian_gitee 已提交
1426 1427 1428 1429
  keyGenPromise.then(rsaKeyPair => {
    keyPair = rsaKeyPair;
    // 初始化加解密操作环境:使用公钥开始加密
    return cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null);
胡啸天 已提交
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
  })
    .then(() => {
      // 加密doFinal
      return cipher.doFinal(input);
    })
    .then(dataBlob => {
      // 获取加密后的信息,并用于解密的入参
      console.info("EncryptOutPut is " + dataBlob.data);
      AlertDialog.show({ message: "output" + dataBlob.data });
      cipherDataBlob = dataBlob;
      // 初始化加解密操作环境:使用私钥开始解密
      return decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null);
    })
    .then(() => {
      // 解密doFinal
      return decoder.doFinal(cipherDataBlob);
    })
    .then(decodeData => {
      // 验证解密后,数据与原先数据是否保持一致
      if (decodeData.data.toString() === input.data.toString()) {
        AlertDialog.show({ message: "decrypt success" });
        return;
      }
      AlertDialog.show({ message: "decrypt fail" });
    });
W
wutiantian_gitee 已提交
1455 1456 1457 1458 1459
}

// 以Callback方式加解密
function decryptMessageCallback() {
  // 生成非对称密钥生成器
胡啸天 已提交
1460
  let sm2Generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
W
wutiantian_gitee 已提交
1461 1462 1463 1464 1465
  // 生成加解密生成器,用于加密
  let cipher = cryptoFramework.createCipher("SM2_256|SM3");
  // 生成加解密生成器,用于解密
  let decoder = cryptoFramework.createCipher("SM2_256|SM3");
  let plainText = "this is cipher text";
胡啸天 已提交
1466
  let input = { data: stringToUint8Array(plainText) };
W
wutiantian_gitee 已提交
1467 1468 1469
  let cipherData;
  let keyPair;
  // 通过非对称秘钥生成器生成非对称密钥对
胡啸天 已提交
1470
  sm2Generator.generateKeyPair(function (err, newKeyPair) {
W
wutiantian_gitee 已提交
1471 1472 1473 1474 1475 1476
    keyPair = newKeyPair;
    // 初始化加解密操作环境:使用公钥开始加密
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null, function (err, data) {
      // 加密doFinal
      cipher.doFinal(input, function (err, data) {
        // 获取加密后的信息,并用于解密的入参
胡啸天 已提交
1477
        AlertDialog.show({ message: "EncryptOutPut is " + data.data });
W
wutiantian_gitee 已提交
1478 1479 1480 1481 1482 1483 1484
        cipherData = data;
        // 初始化加解密操作环境:使用私钥开始解密
        decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null, function (err, data) {
          // 解密doFinal
          decoder.doFinal(cipherData, function (err, data) {
            // 验证解密后,数据与原先数据是否保持一致
            if (input.data.toString() === data.data.toString()) {
胡啸天 已提交
1485
              AlertDialog.show({ message: "decrype success" });
W
wutiantian_gitee 已提交
1486 1487
              return;
            }
胡啸天 已提交
1488
            AlertDialog.show({ message: "decrype fail" });
W
wutiantian_gitee 已提交
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498
          });
        });
      });
    });
  });
}
```

### SM4 ECB以callback方式加解密开发步骤:

W
wutiantian_gitee 已提交
1499 1500 1501 1502
> **说明:**
>
> 从API version 10开始, 支持SM4加解密。

W
wutiantian_gitee 已提交
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532
示例8:使用SM4对称密钥的加解密操作

1. 创建对称密钥生成器。
2. 通过已有二进制数据生成密钥。
3. 创建加解密生成器。
4. 通过加解密生成器加密或解密数据。

```js
import cryptoFramework from '@ohos.security.cryptoFramework';

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 uint8ArrayToString(array) {
  let arrayString = '';
  for (let i = 0; i < array.length; i++) {
    arrayString += String.fromCharCode(array[i]);
  }
  return arrayString;
}

// SM4 ECB模式示例,callback写法
function testSM4Ecb() {
  // 生成非对称密钥生成器
胡啸天 已提交
1533
  let sm4Generator = cryptoFramework.createSymKeyGenerator('SM4_128');
W
wutiantian_gitee 已提交
1534 1535 1536 1537 1538
  // 生成加解密生成器,用于加密
  let cipher = cryptoFramework.createCipher("SM4_128|ECB|PKCS7");
  // 生成加解密生成器,用于解密
  let decoder = cryptoFramework.createCipher("SM4_128|ECB|PKCS7");
  let plainText = "this is cipher text";
胡啸天 已提交
1539
  let input = { data: stringToUint8Array(plainText) };
W
wutiantian_gitee 已提交
1540 1541 1542
  let cipherData;
  let key;
  // 通过非对称秘钥生成器生成非对称密钥对
胡啸天 已提交
1543
  sm4Generator.generateSymKey(function (err, newKey) {
W
wutiantian_gitee 已提交
1544 1545 1546 1547 1548 1549
    key = newKey;
    // 初始化加解密操作环境:使用公钥开始加密
    cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, null, function (err, data) {
      // 加密doFinal
      cipher.doFinal(input, function (err, data) {
        // 获取加密后的信息,并用于解密的入参
胡啸天 已提交
1550
        AlertDialog.show({ message: "EncryptOutPut is " + data.data });
W
wutiantian_gitee 已提交
1551 1552 1553 1554 1555 1556 1557
        cipherData = data;
        // 初始化加解密操作环境:使用私钥开始解密
        decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, key, null, function (err, data) {
          // 解密doFinal
          decoder.doFinal(cipherData, function (err, data) {
            // 验证解密后,数据与原先数据是否保持一致
            if (input.data.toString() === data.data.toString()) {
胡啸天 已提交
1558
              AlertDialog.show({ message: "decrype success" });
W
wutiantian_gitee 已提交
1559 1560
              return;
            }
胡啸天 已提交
1561
            AlertDialog.show({ message: "decrype fail" });
W
wutiantian_gitee 已提交
1562 1563 1564 1565 1566 1567 1568 1569
          });
        });
      });
    });
  });
}
```

W
wangyongzhong2 已提交
1570 1571
## 使用签名验签操作

X
xwb 已提交
1572
### 场景说明
W
wangyongzhong2 已提交
1573 1574 1575 1576

当需要判断接收的数据是否被篡改且是否为指定对象发送的数据时,可以使用签名验签操作。使用签名验签操作中,典型的场景有:
1. 使用RSA签名验签操作
2. 使用ECC签名验签操作
X
xwb 已提交
1577
3. 使用RSA签名验签,PSS模式时,获取、设置SignSpecItem参数。
W
wutiantian_gitee 已提交
1578
4. 使用SM2签名验签操作
X
xwb 已提交
1579

X
xwb 已提交
1580 1581
> **说明:**
>
X
xwb 已提交
1582 1583
> 1. 从API version 10开始,支持RSA使用PSS填充模式时,获取、设置[SignSpecItem](../reference/apis/js-apis-cryptoFramework.md#signspecitem10)参数。
> 2. 从API version 10开始,支持签名验签时字符串参数不带密钥长度。
W
wangyongzhong2 已提交
1584

X
xwb 已提交
1585
### 接口及参数说明
W
wangyongzhong2 已提交
1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)<br/>由于密码算法的复杂性,在选取不同规格和参数时,开发差异较大,无法通过代码示例一一列举,请仔细阅读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方式签名所有数据|
X
xwb 已提交
1598 1599
|Sign|getSignSpec(itemType: SignSpecItem): string \| number|获得签名的参数,当前仅支持RSA算法|
|Sign|setSignSpec(itemType: SignSpecItem, itemValue: number): void|设置签名的参数,当前仅支持RSA算法|
W
wangyongzhong2 已提交
1600
|cryptoFramework|function createVerify(algName : string) : Verify|根据String设置的参数创建Verify对象|
X
xwb 已提交
1601 1602
|Verify|init(pubKey : PubKey, callback : AsyncCallback\<void>) : void|使用callback方式设置密钥并初始化Verify对象|
|Verify|init(pubKey : PubKey) : Promise\<void>|使用Promise方式设置密钥并初始化Verify对象|
W
wangyongzhong2 已提交
1603 1604 1605 1606
|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方式验签所有数据|
X
xwb 已提交
1607 1608
|Verify|getVerifySpec(itemType: SignSpecItem): string \| number|获得验签的参数,当前仅支持RSA算法|
|Verify|setVerifySpec(itemType: SignSpecItem, itemValue: number): void|设置验签的参数,当前仅支持RSA算法|
W
wangyongzhong2 已提交
1609

X
xwb 已提交
1610
### RSA签名验签开发步骤
W
wangyongzhong2 已提交
1611 1612

示例1:使用RSA签名验签操作
X
xwb 已提交
1613

W
wangyongzhong2 已提交
1614 1615 1616 1617 1618
1. 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
2. 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
3. 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用sign接口生成数据的签名。
4. 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
5. 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用verify接口传入签名进行验签。
X
xwb 已提交
1619 1620

```js
W
wangyongzhong2 已提交
1621 1622
import cryptoFramework from "@ohos.security.cryptoFramework"

X
xwb 已提交
1623
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
1624
function stringToUint8Array(str) {
X
xwb 已提交
1625 1626 1627
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
1628
  }
X
xwb 已提交
1629
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
1630 1631 1632 1633 1634 1635
}

let globalKeyPair;
let SignMessageBlob;
let plan1 = "This is Sign test plan1";
let plan2 = "This is Sign test plan1";
胡啸天 已提交
1636 1637
let input1 = { data: stringToUint8Array(plan1) };
let input2 = { data: stringToUint8Array(plan2) };
W
wangyongzhong2 已提交
1638 1639 1640

function signMessagePromise() {
  let rsaGenerator = cryptoFramework.createAsyKeyGenerator("RSA1024|PRIMES_2");
X
xwb 已提交
1641
  let signer = cryptoFramework.createSign("RSA1024|PKCS1|SHA256"); // API version 10开始,支持"RSA|PKCS1|SHA256"
W
wangyongzhong2 已提交
1642
  let keyGenPromise = rsaGenerator.generateKeyPair();
胡啸天 已提交
1643
  keyGenPromise.then(keyPair => {
W
wangyongzhong2 已提交
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
    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);
  });
}

1657
// 调用完sign操作后调用verify
W
wangyongzhong2 已提交
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671
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");
X
xwb 已提交
1672
  let signer = cryptoFramework.createSign("RSA1024|PKCS1|SHA256"); // API version 10开始,支持"RSA|PKCS1|SHA256"
W
wangyongzhong2 已提交
1673 1674 1675
  rsaGenerator.generateKeyPair(function (err, keyPair) {
    globalKeyPair = keyPair;
    let priKey = globalKeyPair.priKey;
胡啸天 已提交
1676 1677 1678
    signer.init(priKey, err => {
      signer.update(input1, err => {
        signer.sign(input2, (err, data) => {
W
wangyongzhong2 已提交
1679 1680 1681 1682 1683 1684 1685 1686
          SignMessageBlob = data;
          console.info("sign output is " + SignMessageBlob.data);
        });
      });
    });
  });
}

1687
// 调用完sign操作后调用verify
W
wangyongzhong2 已提交
1688 1689
function verifyMessageCallback() {
  let verifyer = cryptoFramework.createVerify("RSA1024|PKCS1|SHA256");
胡啸天 已提交
1690 1691 1692
  verifyer.init(globalKeyPair.pubKey, err => {
    verifyer.update(input1, err => {
      verifyer.verify(input2, SignMessageBlob, function (err, data) {
W
wangyongzhong2 已提交
1693 1694 1695 1696 1697 1698 1699
        console.info("verify result is " + data);
      });
    });
  })
}
```

X
xwb 已提交
1700
### ECDSA签名验签开发步骤
1701

X
xwb 已提交
1702
示例2:使用ECDSA操作
X
xwb 已提交
1703

W
wangyongzhong2 已提交
1704 1705 1706 1707 1708 1709
1. 生成ECC密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成ECC非对称密钥。
2. 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
3. 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用doFinal接口生成数据的签名。
4. 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
5. 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用doFinal接口传入签名进行验签。

X
xwb 已提交
1710
```js
W
wangyongzhong2 已提交
1711 1712
import cryptoFramework from "@ohos.security.cryptoFramework"

X
xwb 已提交
1713
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
1714
function stringToUint8Array(str) {
X
xwb 已提交
1715 1716
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
1717 1718
    arr.push(str.charCodeAt(i));
  }
X
xwb 已提交
1719
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
1720 1721 1722 1723 1724 1725
}

let globalKeyPair;
let SignMessageBlob;
let plan1 = "This is Sign test plan1";
let plan2 = "This is Sign test plan1";
胡啸天 已提交
1726 1727
let input1 = { data: stringToUint8Array(plan1) };
let input2 = { data: stringToUint8Array(plan2) };
W
wangyongzhong2 已提交
1728 1729 1730 1731 1732

function signMessagePromise() {
  let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
  let signer = cryptoFramework.createSign("ECC256|SHA256");
  let keyGenPromise = eccGenerator.generateKeyPair();
胡啸天 已提交
1733
  keyGenPromise.then(keyPair => {
W
wangyongzhong2 已提交
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
    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;
胡啸天 已提交
1765 1766 1767
    signer.init(priKey, err => {
      signer.update(input1, err => {
        signer.sign(input2, (err, data) => {
W
wangyongzhong2 已提交
1768 1769 1770 1771 1772 1773 1774 1775 1776 1777
          SignMessageBlob = data;
          console.info("sign output is " + SignMessageBlob.data);
        });
      });
    });
  });
}

function verifyMessageCallback() {
  let verifyer = cryptoFramework.createVerify("ECC256|SHA256");
胡啸天 已提交
1778 1779 1780
  verifyer.init(globalKeyPair.pubKey, err => {
    verifyer.update(input1, err => {
      verifyer.verify(input2, SignMessageBlob, function (err, data) {
W
wangyongzhong2 已提交
1781 1782 1783 1784 1785 1786
        console.info("verify result is " + data);
      });
    });
  })
}
```
1787

X
xwb 已提交
1788
### RSA分段签名验签开发步骤
W
wangyongzhong2 已提交
1789

X
xwb 已提交
1790
示例3:使用RSA签名验签操作
X
xwb 已提交
1791

X
xwb 已提交
1792 1793 1794 1795 1796 1797
1. 生成RSA密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成RSA非对称密钥。
2. 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
3. 执行签名操作。通过Sign类提供的update接口,多次添加签名数据,并调用sign接口生成数据的签名,完成分段签名。
4. 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
5. 执行验签操作。多次通过Verify类提供的update接口,添加签名数据,并调用verify接口传入签名进行验签,完成分段验签。

X
xwb 已提交
1798
```js
W
wangyongzhong2 已提交
1799 1800
import cryptoFramework from "@ohos.security.cryptoFramework"

X
xwb 已提交
1801
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
1802
function stringToUint8Array(str) {
X
xwb 已提交
1803 1804
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
W
wangyongzhong2 已提交
1805 1806
    arr.push(str.charCodeAt(i));
  }
X
xwb 已提交
1807
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
}

function signLongMessagePromise() {
  let globalPlainText = "This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!" +
  "This is a long plainTest! This is a long plainTest! This is a long plainTest! This is a long plainTest!";
  let globalSignData;
  let textSplitLen = 64; // 自定义的数据拆分长度
  let keyGenName = "RSA1024";
胡啸天 已提交
1822
  let signAlgName = "RSA1024|PKCS1|SHA256";
W
wangyongzhong2 已提交
1823 1824
  let globalKeyPair;
  let asyKeyGenerator = cryptoFramework.createAsyKeyGenerator(keyGenName); // 创建非对称密钥生成器对象
胡啸天 已提交
1825 1826
  let signer = cryptoFramework.createSign(signAlgName); // 创建签名Signer对象
  let verifier = cryptoFramework.createVerify(signAlgName); // 创建验签Verifier对象
W
wangyongzhong2 已提交
1827 1828 1829 1830 1831
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("testRsaMultiUpdate");
    }, 10);
  }).then(() => {
胡啸天 已提交
1832
    return asyKeyGenerator.generateKeyPair(); // 生成RSA密钥
W
wangyongzhong2 已提交
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848
  }).then(keyPair => {
    globalKeyPair = keyPair; // 保存到密钥对全局变量
    return signer.init(globalKeyPair.priKey);
  }).then(async () => {
    // 当原文过大时,可将原文按理想长度进行拆分,循环调用update添加原文
    for (let i = 0; i < (globalPlainText.length / textSplitLen); i++) {
      let tempStr = globalPlainText.substr(i * textSplitLen, textSplitLen);
      let tempBlob = { data : stringToUint8Array(tempStr) };
      await signer.update(tempBlob);
    }
    return signer.sign(null);
  }).then(data =>{
    globalSignData = data.data;
    console.info(`globalSignOutput len is ${globalSignData.length}, data is: ${globalSignData.toString()}`);
    return verifier.init(globalKeyPair.pubKey);
  }).then(async() => {
胡啸天 已提交
1849
    // 当原文过大时,可将原文按理想长度进行拆分,循环调用update添加原文
W
wangyongzhong2 已提交
1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863
    for (let i = 0; i < (globalPlainText.length / textSplitLen); i++) {
      let tempData = globalPlainText.slice(i * textSplitLen, (i + 1) * textSplitLen);
      let tempBlob = { data : stringToUint8Array(tempData) };
      await verifier.update(tempBlob);
    }
    return verifier.verify(null, { data : globalSignData});
  }).then(res => {
    console.info(`verify res is ${res}`);
  }).catch(error => {
    console.error(`catch error, ${error.code}, ${error.message}`);
  })
}
```

X
xwb 已提交
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
### RSA签名验签PSS模式开发步骤

示例4:使用RSA签名验签PSS模式,以Callback形式完成签名验签流程。

1. 根据密钥参数生成RSA密钥。通过createAsyKeyGeneratorBySpec接口创建AsyKeyGeneratorBySpec对象,并生成RSA非对称密钥对。
2. 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥,可以获得、设置PSS模式相关参数。
3. 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用sign接口生成数据的签名。
4. 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥,可以获得、设置PSS模式相关参数,验签成功需要保证盐值长度一致。
5. 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用verify接口传入签名进行验签。

X
xwb 已提交
1874
```js
X
xwb 已提交
1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
import cryptoFramework from "@ohos.security.cryptoFramework"

// 可理解的字符串转成字节流
function stringToUint8Array(str) {
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

// 根据密钥参数属性构造RSA非对称密钥对密钥参数
胡啸天 已提交
1887 1888 1889 1890 1891 1892 1893 1894 1895
function genRsaKeyPairSpec(nIn: bigint, eIn: bigint, dIn: bigint) {
  let rsaCommSpec = { n: nIn, algName: "RSA", specType: cryptoFramework.AsyKeySpecType.COMMON_PARAMS_SPEC };
  let rsaKeyPairSpec = {
    params: rsaCommSpec,
    sk: dIn,
    pk: eIn,
    algName: "RSA",
    specType: cryptoFramework.AsyKeySpecType.KEY_PAIR_SPEC
  };
X
xwb 已提交
1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
  return rsaKeyPairSpec;
}

// 生成RSA2048密钥对参数
function genRsa2048KeyPairSpec() {
  let nIn = BigInt("0x9260d0750ae117eee55c3f3deaba74917521a262ee76007cdf8a56755ad73a1598a1408410a01434c3f5bc54a88b57fa19fc4328daea0750a4c44e88cff3b2382621b80f670464433e4336e6d003e8cd65bff211da144b88291c2259a00a72b711c116ef7686e8fee34e4d933c868187bdc26f7be071493c86f7a5941c3510806ad67b0f94d88f5cf5c02a092821d8626e8932b65c5bd8c92049c210932b7afa7ac59c0e886ae5c1edb00d8ce2c57633db26bd6639bff73cee82be9275c402b4cf2a4388da8cf8c64eefe1c5a0f5ab8057c39fa5c0589c3e253f0960332300f94bea44877b588e1edbde97cf2360727a09b775262d7ee552b3319b9266f05a25");
  let eIn = BigInt("0x010001");
  let dIn = BigInt("0x6a7df2ca63ead4dda191d614b6b385e0d9056a3d6d5cfe07db1daabee022db08212d97613d3328e0267c9dd23d787abde2afcb306aeb7dfce69246cc73f5c87fdf06030179a2114b767db1f083ff841c025d7dc00cd82435b9a90f695369e94df23d2ce458bc3b3283ad8bba2b8fa1ba62e2dce9accff3799aae7c840016f3ba8e0048c0b6cc4339af7161003a5beb864a0164b2c1c9237b64bc87556994351b27506c33d4bcdfce0f9c491a7d6b0628c7c852be4f0a9c3132b2ed3a2c8881e9aab07e20e17deb074691be677776a78b5c502e05d9bdde72126b3738695e2dd1a0a98a14247c65d8a7ee79432a092cb0721a12df798e44f7cfce0c498147a9b1");
  return genRsaKeyPairSpec(nIn, eIn, dIn);
}

function verifyMessageCallbackPSS() {
  let plan1 = "This is Sign test plan1";
  let plan2 = "This is Sign test plan1";
胡啸天 已提交
1910 1911
  let input1 = { data: stringToUint8Array(plan1) };
  let input2 = { data: stringToUint8Array(plan2) };
X
xwb 已提交
1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922
  let globalKeyPair;
  let signMessageBlob;
  // 获得RSA密钥对密钥参数对象
  let rsaKeyPairSpec = genRsa2048KeyPairSpec();
  // 构造RSA密钥对生成器
  let rsaGeneratorSpec = cryptoFramework.createAsyKeyGeneratorBySpec(rsaKeyPairSpec);
  // sign和verfiy均支持RSA密钥带长度/不带长度的写法
  let signer = cryptoFramework.createSign("RSA|PSS|SHA256|MGF1_SHA256");
  let verifyer = cryptoFramework.createVerify("RSA2048|PSS|SHA256|MGF1_SHA256");
  rsaGeneratorSpec.generateKeyPair(function (err, keyPair) {
    globalKeyPair = keyPair;
胡啸天 已提交
1923
    signer.init(globalKeyPair.priKey, err => {
X
xwb 已提交
1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936
      // 在签名初始化后,对PSS参数进行set和get操作
      let setN = 32;
      signer.setSignSpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, setN);
      let saltLen = signer.getSignSpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM);
      console.info("SaltLen == " + saltLen);
      let tf = signer.getSignSpec(cryptoFramework.SignSpecItem.PSS_TRAILER_FIELD_NUM);
      console.info("trailer field == " + tf);
      let md = signer.getSignSpec(cryptoFramework.SignSpecItem.PSS_MD_NAME_STR);
      console.info("md == " + md);
      let mgf = signer.getSignSpec(cryptoFramework.SignSpecItem.PSS_MGF_NAME_STR);
      console.info("mgf == " + mgf);
      let mgf1Md = signer.getSignSpec(cryptoFramework.SignSpecItem.PSS_MGF1_MD_STR);
      console.info("mgf1Md == " + mgf1Md);
胡啸天 已提交
1937
      signer.update(input1, err => {
X
xwb 已提交
1938 1939 1940
        signer.sign(input2, function (err, data) {
          // 在验签初始化前,对PSS参数进行set和get操作,功能与初始化后一致
          signMessageBlob = data;
胡啸天 已提交
1941
          AlertDialog.show({ message: "res" + signMessageBlob.data });
X
xwb 已提交
1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953
          let setN = 32;
          verifyer.setVerifySpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM, setN);
          let saltLen = verifyer.getVerifySpec(cryptoFramework.SignSpecItem.PSS_SALT_LEN_NUM);
          console.info("SaltLen == " + saltLen);
          let tf = verifyer.getVerifySpec(cryptoFramework.SignSpecItem.PSS_TRAILER_FIELD_NUM);
          console.info("trailer field == " + tf);
          let md = verifyer.getVerifySpec(cryptoFramework.SignSpecItem.PSS_MD_NAME_STR);
          console.info("md == " + md);
          let mgf = verifyer.getVerifySpec(cryptoFramework.SignSpecItem.PSS_MGF_NAME_STR);
          console.info("mgf == " + mgf);
          let mgf1Md = verifyer.getVerifySpec(cryptoFramework.SignSpecItem.PSS_MGF1_MD_STR);
          console.info("mgf1Md == " + mgf1Md);
胡啸天 已提交
1954 1955 1956 1957
          verifyer.init(globalKeyPair.pubKey, err => {
            verifyer.update(input1, err => {
              verifyer.verify(input2, signMessageBlob, function (err, data) {
                AlertDialog.show({ message: "res " + data });
X
xwb 已提交
1958 1959 1960 1961 1962 1963 1964 1965 1966 1967
              })
            });
          });
        });
      });
    });
  });
}
```

W
wutiantian_gitee 已提交
1968 1969
### SM2签名验签开发步骤

W
wutiantian_gitee 已提交
1970 1971 1972 1973
> **说明:**
>
> 从API version 10开始, 支持SM2签名验签。

W
wutiantian_gitee 已提交
1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
示例5:使用SM2操作

1. 生成SM2密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成SM2非对称密钥。
2. 生成Sign对象。通过createSign接口创建Sign对象,执行初始化操作并设置签名私钥。
3. 执行签名操作。通过Sign类提供的update接口,添加签名数据,并调用doFinal接口生成数据的签名。
4. 生成Verify对象。通过createVerify接口创建Verify对象,执行初始化操作并设置验签公钥。
5. 执行验签操作。通过Verify类提供的update接口,添加签名数据,并调用doFinal接口传入签名进行验签。

```js
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 plan1 = "This is Sign test plan1";
let plan2 = "This is Sign test plan2";
let input1 = { data: stringToUint8Array(plan1) };
let input2 = { data: stringToUint8Array(plan2) };

function signAndVerify() {
胡啸天 已提交
2001 2002 2003
  let signMessageBlob;
  let globalKeyPair;
  let sm2Generator = cryptoFramework.createAsyKeyGenerator("SM2_256");
W
wutiantian_gitee 已提交
2004
  let signer = cryptoFramework.createSign("SM2_256|SM3");
胡啸天 已提交
2005
  sm2Generator.generateKeyPair(function (err, keyPair) {
W
wutiantian_gitee 已提交
2006 2007
    globalKeyPair = keyPair;
    let priKey = globalKeyPair.priKey;
胡啸天 已提交
2008 2009
    signer.init(priKey, err => {
      signer.update(input1, err => {
W
wutiantian_gitee 已提交
2010
        signer.sign(input2, function (err, data) {
胡啸天 已提交
2011 2012
          signMessageBlob = data;
          console.info("sign output is " + signMessageBlob.data);
W
wutiantian_gitee 已提交
2013
          let verifyer = cryptoFramework.createVerify("SM2_256|SM3");
胡啸天 已提交
2014 2015 2016
          verifyer.init(globalKeyPair.pubKey, err => {
            verifyer.update(input1, err => {
              verifyer.verify(input2, signMessageBlob, function (err, data) {
W
wutiantian_gitee 已提交
2017
                console.info("verify result is " + data);
胡啸天 已提交
2018
                AlertDialog.show({ message: "verify success" })
W
wutiantian_gitee 已提交
2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
              });
            });
          })
        });
      });
    });
  });
}
```

X
xwb 已提交
2029 2030 2031 2032 2033 2034 2035 2036
## 使用密钥协商操作

### 场景说明

使用密钥协商操作中,典型的场景有:

通信双方可以在一个公开的信道上通过相互传送一些消息,共同建立一个安全的共享秘密密钥。

X
xwb 已提交
2037 2038
> **说明:**
>
X
xwb 已提交
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055
> 从API version 10开始,支持密钥协商时字符串参数不带密钥长度。

### 接口及参数说明

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)

|实例名|接口名|描述|
|---|---|---|
|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方式进行密钥协商|

### 开发步骤

1. 生成ECC密钥。通过createAsyKeyGenerator接口创建AsyKeyGenerator对象,并生成ECC非对称密钥。
2. 基于ECC密钥的私钥及公钥执行ECDH操作。

X
xwb 已提交
2056
```js
X
xwb 已提交
2057 2058 2059 2060 2061 2062 2063 2064
import cryptoFramework from "@ohos.security.cryptoFramework"

let globalKeyPair;

function ecdhPromise() {
  let eccGenerator = cryptoFramework.createAsyKeyGenerator("ECC256");
  let eccKeyAgreement = cryptoFramework.createKeyAgreement("ECC256"); // API version 10开始,支持输入"ECC"来进行密钥协商
  let keyGenPromise = eccGenerator.generateKeyPair();
胡啸天 已提交
2065
  keyGenPromise.then(keyPair => {
X
xwb 已提交
2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090
    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);
    });
  });
}
```

W
wangyongzhong2 已提交
2091 2092
## 使用摘要操作

X
xwb 已提交
2093
### 场景说明
W
wangyongzhong2 已提交
2094 2095 2096 2097 2098 2099 2100

用户指定摘要算法(如SHA256)生成Md实例,并输入单段或多段需要摘要的信息,进行摘要计算更新,并返回消息摘要计算结果,在指定算法后可获取当前算法名与摘要计算长度(字节)

使用摘要操作的主要场景为:

用户指定摘要算法(如SHA256)生成Md实例,并输入单段或多段需要摘要的信息,进行摘要计算更新,并返回消息摘要计算结果,在指定算法后可获取当前算法名与摘要计算长度(字节)

X
xwb 已提交
2101
### 接口及参数说明
W
wangyongzhong2 已提交
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)

| 实例名          | 接口名                                                       | 描述                                               |
| --------------- | ------------------------------------------------------------ | -------------------------------------------------- |
| 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;                                   | 获取当前设置的摘要算法名                           |

X
xwb 已提交
2115
### 摘要算法开发步骤
W
wangyongzhong2 已提交
2116

X
xwb 已提交
2117 2118 2119 2120
1. 设置算法,通过接口`createMd`生成摘要操作实例。
2. 接受用户数据,通过接口`update`,更新摘要,此步骤可重复,算法库不限制单次update的长度。
3. 通过接口`digest`,返回摘要计算结果。
4. 获取当前摘要算法名与摘要计算长度。
W
wangyongzhong2 已提交
2121

X
xwb 已提交
2122
```js
W
wangyongzhong2 已提交
2123 2124
import cryptoFramework from "@ohos.security.cryptoFramework"

胡啸天 已提交
2125
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
2126
function stringToUint8Array(str) {
胡啸天 已提交
2127 2128 2129
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
W
wangyongzhong2 已提交
2130
  }
胡啸天 已提交
2131
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
2132 2133
}

胡啸天 已提交
2134 2135 2136 2137 2138 2139
// 以Promise方式完成摘要
function doMdByPromise() {
  let mdAlgName = "SHA256"; // 摘要算法名
  let message = "mdTestMessgae"; // 待摘要数据
  let md;
  let mdOutput;
W
wangyongzhong2 已提交
2140
  try {
胡啸天 已提交
2141
    md = cryptoFramework.createMd(mdAlgName);
W
wangyongzhong2 已提交
2142 2143
  } catch (error) {
    console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
胡啸天 已提交
2144
    return;
W
wangyongzhong2 已提交
2145
  }
胡啸天 已提交
2146 2147 2148
  console.info("[Promise]: Md algName is: " + md.algName);
  // 数据量较少时,可以只做一次update,将数据全部传入,接口未对入参长度做限制
  let promiseMdUpdate = md.update({ data: stringToUint8Array(message) });
W
wangyongzhong2 已提交
2149
  promiseMdUpdate.then(() => {
胡啸天 已提交
2150 2151
    // 通过digest,返回摘要结果
    let PromiseMdDigest = md.digest();
W
wangyongzhong2 已提交
2152
    return PromiseMdDigest;
胡啸天 已提交
2153 2154 2155 2156 2157
  }).then(digestOutput => {
    mdOutput = digestOutput;
    console.info("[Promise]: MD result: " + mdOutput.data);
    let mdLen = md.getMdLength();
    console.info("[Promise]: MD len: " + mdLen);
W
wangyongzhong2 已提交
2158 2159 2160 2161 2162
  }).catch(error => {
    console.error("[Promise]: error: " + error.message);
  });
}

胡啸天 已提交
2163 2164 2165 2166 2167 2168
// 以Callback方式完成摘要
function doMdByCallback() {
  let mdAlgName = "SHA256"; // 摘要算法名
  let message = "mdTestMessgae"; // 待摘要数据
  let md;
  let mdOutput;
W
wangyongzhong2 已提交
2169
  try {
胡啸天 已提交
2170
    md = cryptoFramework.createMd(mdAlgName);
W
wangyongzhong2 已提交
2171 2172 2173
  } catch (error) {
    console.error("[Callback]: error code: " + error.code + ", message is: " + error.message);
  }
胡啸天 已提交
2174 2175 2176
  console.info("[Callback]: Md algName is: " + md.algName);
  // 数据量较少时,可以只做一次update,将数据全部传入,接口未对入参长度做限制
  md.update({ data: stringToUint8Array(message) }, (err,) => {
W
wangyongzhong2 已提交
2177 2178 2179
    if (err) {
      console.error("[Callback]: err: " + err.code);
    }
胡啸天 已提交
2180
    md.digest((err1, digestOutput) => {
W
wangyongzhong2 已提交
2181 2182
      if (err1) {
        console.error("[Callback]: err: " + err1.code);
胡啸天 已提交
2183 2184 2185 2186 2187
      } else {
        mdOutput = digestOutput;
        console.info("[Callback]: MD result: " + mdOutput.data);
        let mdLen = md.getMdLength();
        console.info("[Callback]: MD len: " + mdLen);
W
wangyongzhong2 已提交
2188 2189 2190 2191 2192
      }
    });
  });
}
```
2193

X
xwb 已提交
2194
### 分段摘要算法开发步骤
2195

X
xwb 已提交
2196 2197 2198 2199
1. 设置算法,通过接口`createMd`生成摘要操作实例。
2. 接受用户数据,多次通过接口`update`,更新摘要,实现分段。
3. 通过接口`digest`,返回摘要计算结果。
4. 获取当前摘要算法名与摘要计算长度。
X
xwb 已提交
2200

X
xwb 已提交
2201
```js
W
wangyongzhong2 已提交
2202 2203
import cryptoFramework from "@ohos.security.cryptoFramework"

胡啸天 已提交
2204
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
2205
function stringToUint8Array(str) {
胡啸天 已提交
2206 2207
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
W
wangyongzhong2 已提交
2208 2209
    arr.push(str.charCodeAt(i));
  }
胡啸天 已提交
2210
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
2211 2212
}

胡啸天 已提交
2213 2214 2215 2216 2217
// 使用Promise方式,完成分段摘要
async function doLoopMdPromise() {
  let mdAlgName = "SHA256"; // 摘要算法名
  let md;
  let mdOutput;
W
wangyongzhong2 已提交
2218
  try {
胡啸天 已提交
2219
    md = cryptoFramework.createMd(mdAlgName);
W
wangyongzhong2 已提交
2220 2221 2222 2223
  } catch (error) {
    console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
    return;
  }
胡啸天 已提交
2224 2225 2226 2227 2228 2229 2230 2231
  console.info("[Promise]: Md algName is: " + md.algName);
  let messageText = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // 假设信息总共43字节
  let messageArr = [];
  let updateLength = 20; // 假设每20字节分段update一次,实际并无要求

  for (let i = 0; i <= messageText.length; i++) {
    if ((i % updateLength == 0 || i == messageText.length) && messageArr.length != 0) {
      let message = new Uint8Array(messageArr);
胡啸天 已提交
2232
      let messageBlob = { data: message };
胡啸天 已提交
2233 2234 2235 2236 2237 2238 2239 2240
      // 使用await处理for循环里的update
      try {
        await md.update(messageBlob); // 分段update
      } catch (error) {
        console.error("await update error code: " + error.code + ", message is: " + error.message);
        return;
      }
      messageArr = [];
W
wangyongzhong2 已提交
2241
    }
胡啸天 已提交
2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252
    // 按分割长度,填充messageArr
    if (i < messageText.length) {
      messageArr.push(messageText.charCodeAt(i));
    }
  }
  let PromiseMdDigest = md.digest();
  PromiseMdDigest.then(digestOutput => {
    mdOutput = digestOutput;
    console.info("[Promise]: MD result: " + mdOutput.data);
    let mdLen = md.getMdLength();
    console.info("[Promise]: MD len: " + mdLen);
W
wangyongzhong2 已提交
2253 2254 2255 2256 2257 2258 2259 2260
  }).catch(error => {
    console.error("[Promise]: error: " + error.message);
  });
}
```

## 使用消息认证码操作

X
xwb 已提交
2261
### 场景说明
W
wangyongzhong2 已提交
2262 2263 2264

消息认证码操作主要应用于身份认证的场景:

X
xwb 已提交
2265
Mac(message authentication code)可以对消息进行完整性校验,通过使用双方共享的密钥,识别出信息伪装篡改等行为。
W
wangyongzhong2 已提交
2266 2267 2268

用户指定摘要算法(如SHA256)生成消息认证码Mac实例,输入对称密钥初始化Mac,并传入单段或多段需要摘要的信息,进行消息认证码计算,并获取消息认证码计算结果,在指定算法后可获取当前算法名与消息认证码计算长度(字节)。

X
xwb 已提交
2269
### 接口及参数说明
W
wangyongzhong2 已提交
2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284

详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)

| 实例名          | 接口名                                                       | 描述                                                |
| --------------- | ------------------------------------------------------------ | --------------------------------------------------- |
| 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;                                   | 获取当前设置的摘要算法名                            |

X
xwb 已提交
2285
### HMAC开发步骤
W
wangyongzhong2 已提交
2286

X
xwb 已提交
2287 2288 2289 2290 2291
1. 设置算法,通过接口`createMac`生成消息认证码操作实例。
2. 接受输入对称密钥,通过接口`init`,初始化Mac。
3. 接受数据,通过接口`update`,更新Mac,此步骤可重复。
4. 通过接口`doFinal`,返回Mac计算结果。
5. 获取当前摘要算法名与Mac计算长度。
W
wangyongzhong2 已提交
2292

X
xwb 已提交
2293
```js
W
wangyongzhong2 已提交
2294 2295
import cryptoFramework from "@ohos.security.cryptoFramework"

胡啸天 已提交
2296
// 可理解的字符串转成字节流
W
wangyongzhong2 已提交
2297
function stringToUint8Array(str) {
胡啸天 已提交
2298 2299 2300
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
W
wangyongzhong2 已提交
2301
  }
胡啸天 已提交
2302
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
2303 2304
}

胡啸天 已提交
2305 2306 2307 2308 2309 2310
// 以Promise方式完成HMAC
function doHmacByPromise() {
  let macAlgName = "SHA256"; // 摘要算法名
  let message = "hmacTestMessgae"; // 待hmac数据
  let macOutput;
  let mac;
W
wangyongzhong2 已提交
2311
  try {
胡啸天 已提交
2312
    mac = cryptoFramework.createMac(macAlgName);
W
wangyongzhong2 已提交
2313 2314 2315
  } catch (error) {
    console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
  }
胡啸天 已提交
2316 2317 2318
  console.info("[Promise]: Mac algName is: " + mac.algName);
  let KeyBlob = {
    // 128位密钥
胡啸天 已提交
2319
    data: stringToUint8Array("12345678abcdefgh")
W
wangyongzhong2 已提交
2320
  }
胡啸天 已提交
2321 2322 2323
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator("AES128");
  // 将二进制密钥转换为算法库密钥
  let promiseConvertKey = symKeyGenerator.convertKey(KeyBlob);
W
wangyongzhong2 已提交
2324
  promiseConvertKey.then(symKey => {
胡啸天 已提交
2325
    let promiseMacInit = mac.init(symKey);
W
wangyongzhong2 已提交
2326 2327
    return promiseMacInit;
  }).then(() => {
胡啸天 已提交
2328 2329
    // 数据量较少时,可以只做一次update,将数据全部传入,接口未对入参长度做限制
    let promiseMacUpdate = mac.update({ data: stringToUint8Array(message) });
W
wangyongzhong2 已提交
2330 2331
    return promiseMacUpdate;
  }).then(() => {
胡啸天 已提交
2332
    let PromiseMacDoFinal = mac.doFinal();
W
wangyongzhong2 已提交
2333
    return PromiseMacDoFinal;
胡啸天 已提交
2334 2335 2336 2337 2338
  }).then(output => {
    macOutput = output;
    console.info("[Promise]: HMAC result: " + macOutput.data);
    let macLen = mac.getMacLength();
    console.info("[Promise]: MAC len: " + macLen);
W
wangyongzhong2 已提交
2339 2340 2341 2342 2343
  }).catch(error => {
    console.error("[Promise]: error: " + error.message);
  });
}

胡啸天 已提交
2344 2345 2346 2347 2348 2349
// 以Callback方式完成HMAC
function doHmacByCallback() {
  let macAlgName = "SHA256"; // 摘要算法名
  let message = "hmacTestMessgae"; // 待hmac数据
  let macOutput;
  let mac;
W
wangyongzhong2 已提交
2350
  try {
胡啸天 已提交
2351
    mac = cryptoFramework.createMac(macAlgName);
W
wangyongzhong2 已提交
2352
  } catch (error) {
X
xwb 已提交
2353
    AlertDialog.show({message: "[Callback]: error code: " + error.code + ", message is: " + error.message});
W
wangyongzhong2 已提交
2354 2355
    console.error("[Callback]: error code: " + error.code + ", message is: " + error.message);
  }
胡啸天 已提交
2356 2357 2358
  console.info("[Promise]: Mac algName is: " + mac.algName);
  let KeyBlob = {
    // 128位密钥
胡啸天 已提交
2359
    data: stringToUint8Array("12345678abcdefgh")
W
wangyongzhong2 已提交
2360
  }
胡啸天 已提交
2361 2362
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator("AES128");
  // 将二进制密钥转换为算法库密钥
W
wangyongzhong2 已提交
2363 2364 2365 2366 2367 2368 2369 2370
  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);
      }
胡啸天 已提交
2371 2372
      // 数据量较少时,可以只做一次update,将数据全部传入,接口未对入参长度做限制
      mac.update({ data: stringToUint8Array(message) }, (err2, ) => {
W
wangyongzhong2 已提交
2373 2374 2375
        if (err2) {
          console.error("[Callback]: err: " + err2.code);
        }
胡啸天 已提交
2376
        mac.doFinal((err3, output) => {
W
wangyongzhong2 已提交
2377 2378
          if (err3) {
            console.error("[Callback]: err: " + err3.code);
胡啸天 已提交
2379 2380
          } else {
            macOutput = output;
X
xwb 已提交
2381
            console.error("[Callback]: HMAC result: " + macOutput.data);
胡啸天 已提交
2382
            let macLen = mac.getMacLength();
X
xwb 已提交
2383
            console.error("[Callback]: MAC len: " + macLen);
W
wangyongzhong2 已提交
2384 2385 2386 2387 2388 2389 2390
          }
        });
      });
    });
  });
}
```
2391

X
xwb 已提交
2392
### 分段HMAC开发步骤
X
xwb 已提交
2393

W
wangyongzhong2 已提交
2394
以HMAC更新MAC时多次调用update实现分段为例:
2395

X
xwb 已提交
2396 2397 2398 2399 2400 2401 2402
1. 设置算法,通过接口`createMac`生成消息认证码操作实例。
2. 接受输入对称密钥,通过接口`init`,初始化Mac。
3. 接受数据,多次通过接口`update`,以实现分段。
4. 通过接口`doFinal`,返回Mac计算结果。
5. 获取当前摘要算法名与Mac计算长度。

```js
W
wangyongzhong2 已提交
2403 2404 2405
import cryptoFramework from "@ohos.security.cryptoFramework"

function stringToUint8Array(str) {
胡啸天 已提交
2406 2407
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
W
wangyongzhong2 已提交
2408 2409
    arr.push(str.charCodeAt(i));
  }
胡啸天 已提交
2410
  return new Uint8Array(arr);
W
wangyongzhong2 已提交
2411 2412
}

胡啸天 已提交
2413 2414 2415 2416
function doLoopHmacPromise() {
  let macAlgName = "SHA256"; // 摘要算法名
  let macOutput;
  let mac;
W
wangyongzhong2 已提交
2417
  try {
胡啸天 已提交
2418
    mac = cryptoFramework.createMac(macAlgName);
W
wangyongzhong2 已提交
2419 2420 2421 2422
  } catch (error) {
    console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
    return;
  }
胡啸天 已提交
2423 2424 2425
  console.info("[Promise]: Mac algName is: " + mac.algName);
  let KeyBlob = {
    // 128位密钥
W
wangyongzhong2 已提交
2426 2427
    data : stringToUint8Array("12345678abcdefgh")
  }
胡啸天 已提交
2428 2429 2430 2431 2432
  let messageText = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // 假设信息总共43字节
  let updateLength = 20; // 假设每20字节分段update一次,实际并无要求
  let symKeyGenerator = cryptoFramework.createSymKeyGenerator("AES128");
  // 将二进制密钥转换为算法库密钥
  let promiseConvertKey = symKeyGenerator.convertKey(KeyBlob);
W
wangyongzhong2 已提交
2433
  promiseConvertKey.then(symKey => {
胡啸天 已提交
2434
    let promiseMacInit = mac.init(symKey);
W
wangyongzhong2 已提交
2435 2436
    return promiseMacInit;
  }).then(async () => {
胡啸天 已提交
2437 2438 2439 2440 2441
    let promiseMacUpdate;
    let messageArr = [];
    for (let i = 0; i <= messageText.length; i++) {
      if ((i % updateLength == 0 || i == messageText.length) && messageArr.length != 0) {
        let message = new Uint8Array(messageArr);
胡啸天 已提交
2442
        let messageBlob = { data: message };
胡啸天 已提交
2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455
        // 使用await处理for循环里的update
        try {
          promiseMacUpdate = await mac.update(messageBlob); // 分段update
        } catch (error) {
          console.error("await update error code: " + error.code + ", message is: " + error.message);
          return;
        }
        messageArr = [];
      }
      // 按分割长度,填充messageArr
      if (i < messageText.length) {
        messageArr.push(messageText.charCodeAt(i));
      }
W
wangyongzhong2 已提交
2456 2457 2458
    }
    return promiseMacUpdate;
  }).then(() => {
胡啸天 已提交
2459
    let PromiseMacDoFinal = mac.doFinal();
W
wangyongzhong2 已提交
2460
    return PromiseMacDoFinal;
胡啸天 已提交
2461 2462 2463 2464 2465
  }).then(output => {
    macOutput = output;
    console.log("[Promise]: HMAC result: " + macOutput.data);
    let macLen = mac.getMacLength();
    console.log("[Promise]: MAC len: " + macLen);
W
wangyongzhong2 已提交
2466 2467 2468 2469 2470 2471 2472 2473
  }).catch(error => {
    console.error("[Promise]: error: " + error.message);
  });
}
```

## 使用随机数操作

X
xwb 已提交
2474
### 场景说明
W
wangyongzhong2 已提交
2475 2476 2477

使用随机数操作的主要场景为:

X
xwb 已提交
2478
- 用户生成随机数Random实例,输入随机数生成的长度(字节),生成指定长度(范围为1~INT_MAX)的安全随机数。
W
wangyongzhong2 已提交
2479 2480
- 用户使用生成的随机数作为参数,进行种子设置。

X
xwb 已提交
2481
### 接口及参数说明
X
xwb 已提交
2482

W
wangyongzhong2 已提交
2483 2484
详细接口说明可参考[API参考](../reference/apis/js-apis-cryptoFramework.md)

X
xwb 已提交
2485 2486 2487 2488 2489 2490 2491
| 实例名          | 接口名                                                       | 描述                                       |
| --------------- | ------------------------------------------------------------ | ------------------------------------------ |
| cryptoFramework | function createRandom() : Random;                            | 生成随机数Random实例                       |
| Random          | generateRandom(len : number, callback: AsyncCallback\<DataBlob>) : void; | 接受输入长度,通过Callback,异步生成随机数 |
| Random          | generateRandom(len : number) : Promise\<DataBlob>;           | 接受输入长度,通过Promise,异步生成随机数  |
| Random          | generateRandomSync(len: number): DataBlob;                   | 接受输入长度,同步生成随机数               |
| Random          | setSeed(seed : DataBlob) : void;                             | 接受输入Blob,设置种子                     |
W
wangyongzhong2 已提交
2492

X
xwb 已提交
2493
### 开发步骤
W
wangyongzhong2 已提交
2494

X
xwb 已提交
2495 2496 2497
1. 通过接口`createRandom`生成随机数操作实例。
2. 接受输入长度,通过接口`generateRandom`,生成指定长度的随机数。
3. 接受DataBlob数据,通过接口`setSeed`,为随机数生成池设置种子。
W
wangyongzhong2 已提交
2498

X
xwb 已提交
2499
```js
W
wangyongzhong2 已提交
2500 2501
import cryptoFramework from "@ohos.security.cryptoFramework"

X
xwb 已提交
2502
// 通过Promise方式生成随机数
胡啸天 已提交
2503 2504 2505
function doRandByPromise() {
  let rand;
  let len = 4; // 生成长度4字节的随机数
W
wangyongzhong2 已提交
2506 2507 2508 2509 2510
  try {
    rand = cryptoFramework.createRandom();
  } catch (error) {
    console.error("[Promise]: error code: " + error.code + ", message is: " + error.message);
  }
胡啸天 已提交
2511
  let promiseGenerateRand = rand.generateRandom(len);
W
wangyongzhong2 已提交
2512
  promiseGenerateRand.then(randData => {
胡啸天 已提交
2513 2514 2515 2516 2517 2518
    console.info("[Promise]: rand result: " + randData.data);
    try {
      rand.setSeed(randData);
    } catch (error) {
      console.error("setSeed failed, errCode: " + error.code + ", errMsg: " + error.message);
    }
W
wangyongzhong2 已提交
2519 2520 2521 2522 2523
  }).catch(error => {
    console.error("[Promise]: error: " + error.message);
  });
}

X
xwb 已提交
2524
// 通过Callback方式生成随机数
胡啸天 已提交
2525 2526 2527
function doRandByCallback() {
  let rand;
  let len = 4; // 生成长度4字节的随机数
W
wangyongzhong2 已提交
2528 2529 2530 2531 2532 2533 2534 2535 2536
  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 {
胡啸天 已提交
2537
      console.info("[Callback]: generate random result: " + randData.data);
W
wangyongzhong2 已提交
2538
      try {
胡啸天 已提交
2539
        rand.setSeed(randData);
W
wangyongzhong2 已提交
2540
      } catch (error) {
胡啸天 已提交
2541
        console.error("setSeed failed, errCode: " + error.code + ", errMsg: " + error.message);
W
wangyongzhong2 已提交
2542 2543 2544 2545
      }
    }
  });
}
X
xwb 已提交
2546 2547

// 通过同步接口生成随机数
胡啸天 已提交
2548 2549 2550
function doRandBySync() {
  let rand;
  let len = 24; // 生成长度24字节的随机数
X
xwb 已提交
2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567
  try {
    rand = cryptoFramework.createRandom();
  } catch (error) {
    console.error("[Sync]: error code: " + error.code + ", message is: " + error.message);
  }

  try {
    let randData = rand.generateRandomSync(len);
    if (randData != null) {
      console.info("[Sync]: rand result: " + randData.data);
    } else {
      console.error("[Sync]: get rand result fail!");
    }
  } catch (error) {
    console.error("[Sync]: error: " + error.message);
  }
}
W
wangyongzhong2 已提交
2568
```