• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Encryption and Decryption by Segment with an AES Symmetric Key (GCM Mode) (ArkTS)
2
3
4For details about the algorithm specifications, see [AES](crypto-sym-encrypt-decrypt-spec.md#aes).
5
6
7**Encryption**
8
9
101. Call [cryptoFramework.createSymKeyGenerator](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatesymkeygenerator) and [SymKeyGenerator.generateSymKey](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#generatesymkey-1) to generate a 128-bit AES symmetric key (**SymKey**).
11
12   In addition to the example in this topic, [AES](crypto-sym-key-generation-conversion-spec.md#aes) and [Randomly Generating a Symmetric Key](crypto-generate-sym-key-randomly.md) may help you better understand how to generate an AES symmetric key. Note that the input parameters in the reference documents may be different from those in the example below.
13
142. Call [cryptoFramework.createCipher](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatecipher) with the string parameter **'AES128|GCM|PKCS7'** to create a **Cipher** instance for encryption. The key type is **AES128**, block cipher mode is **GCM**, and the padding mode is **PKCS7**.
15
163. Call [Cipher.init](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#init-1) to initialize the **Cipher** instance. In the **Cipher.init** API, set **opMode** to **CryptoMode.ENCRYPT_MODE** (encryption), **key** to **SymKey** (the key for encryption), and **params** to **GcmParamsSpec** corresponding to the GCM mode.
17
184. Set the size of the data to be passed in each time to 20 bytes, and call [Cipher.update](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#update-1) multiple times to pass in the data (plaintext) to be encrypted.
19
20   - Currently, the amount of data to be passed in by a single **update()** is not limited. You can determine how to pass in data based on the data volume.
21   - You are advised to check the result of each **update()**. If the result is not **null**, obtain the data and combine the data segments into complete ciphertext. The **update()** result may vary with the key specifications.
22
23      If a block cipher mode (ECB or CBC) is used, data is encrypted and output based on the block size. That is, if the data of an **update()** operation matches the block size, the ciphertext is output. Otherwise, **null** is output, and the plaintext will be combined with the input data of the next **update()** to form a block. When **doFinal()** is called, the unencrypted data is padded to the block size based on the specified padding mode, and then encrypted. The **update()** API works in the same way in decryption.
24
25      If a stream cipher mode (CTR or OFB) is used, the ciphertext length is usually the same as the plaintext length.
26
275. Call [Cipher.doFinal](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#dofinal-1) to obtain the encrypted data.
28
29   - If data has been passed in by **update()**, pass in **null** in the **data** parameter of **Cipher.doFinal**.
30   - The output of **doFinal** may be **null**. To avoid exceptions, always check whether the result is **null** before accessing specific data.
31
326. Obtain [GcmParamsSpec](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#gcmparamsspec).authTag as the authentication information for decryption.
33
34   In GCM mode, **authTag** must be of 16 bytes. It is used as the authentication information during decryption. In the example, **authTag** is of 16 bytes.
35
36
37**Decryption**
38
391. Call [cryptoFramework.createCipher](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatecipher) with the string parameter **'AES128|GCM|PKCS7'** to create a **Cipher** instance for decryption. The key type is **AES128**, block cipher mode is **GCM**, and the padding mode is **PKCS7**.
40
412. Call [Cipher.init](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#init-1) to initialize the **Cipher** instance. In the **Cipher.init** API, set **opMode** to **CryptoMode.DECRYPT_MODE** (decryption), **key** to **SymKey** (the key for decryption), and **params** to **GcmParamsSpec** corresponding to the GCM mode.
42
433. Set the size of the data to be passed in each time to 20 bytes, and call [Cipher.update](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#update-1) multiple times to pass in the data (ciphertext) to be decrypted.
44
454. Call [Cipher.doFinal](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#dofinal-1) to obtain the decrypted data.
46
47
48- Example (using asynchronous APIs):
49
50  ```ts
51  import { cryptoFramework } from '@kit.CryptoArchitectureKit';
52  import { buffer } from '@kit.ArkTS';
53
54  function generateRandom(len: number) {
55    let rand = cryptoFramework.createRandom();
56    let generateRandSync = rand.generateRandomSync(len);
57    return generateRandSync;
58  }
59
60  function genGcmParamsSpec() {
61    let ivBlob = generateRandom(12);
62    let arr = [1, 2, 3, 4, 5, 6, 7, 8]; // 8 bytes
63    let dataAad = new Uint8Array(arr);
64    let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
65    arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
66    let dataTag = new Uint8Array(arr);
67    let tagBlob: cryptoFramework.DataBlob = {
68      data: dataTag
69    }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption.
70    let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
71      iv: ivBlob,
72      aad: aadBlob,
73      authTag: tagBlob,
74      algName: "GcmParamsSpec"
75    };
76    return gcmParamsSpec;
77  }
78  let gcmParams = genGcmParamsSpec();
79  // Encrypt the message by segment.
80  async function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
81    let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
82    await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
83    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
84    let cipherText = new Uint8Array();
85    for (let i = 0; i < plainText.data.length; i += updateLength) {
86      let updateMessage = plainText.data.subarray(i, i + updateLength);
87      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
88      // Call update() multiple times to pass in data by segment.
89      let updateOutput = await cipher.update(updateMessageBlob);
90      // Combine the result of each update() to obtain the ciphertext. In certain cases, the doFinal() result also needs to be combined, which depends on the cipher block mode
91      // and padding mode you use. In this example, the GCM mode is used, and the doFinal() result contains authTag but not ciphertext. Therefore, there is no need to combine the doFinal() result.
92      let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length);
93      mergeText.set(cipherText);
94      mergeText.set(updateOutput.data, cipherText.length);
95      cipherText = mergeText;
96    }
97    gcmParams.authTag = await cipher.doFinal(null);
98    let cipherBlob: cryptoFramework.DataBlob = { data: cipherText };
99    return cipherBlob;
100  }
101  // Decrypt the message by segment.
102  async function decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
103    let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
104    await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);
105    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
106    let decryptText = new Uint8Array();
107    for (let i = 0; i < cipherText.data.length; i += updateLength) {
108      let updateMessage = cipherText.data.subarray(i, i + updateLength);
109      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
110      // Call update() multiple times to pass in data by segment.
111      let updateOutput = await decoder.update(updateMessageBlob);
112      // Combine the update() results to obtain the plaintext.
113      let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length);
114      mergeText.set(decryptText);
115      mergeText.set(updateOutput.data, decryptText.length);
116      decryptText = mergeText;
117    }
118    let decryptData = await decoder.doFinal(null);
119    if (decryptData === null) {
120      console.info('GCM decrypt success, decryptData is null');
121    }
122    let decryptBlob: cryptoFramework.DataBlob = { data: decryptText };
123    return decryptBlob;
124  }
125  async function genSymKeyByData(symKeyData: Uint8Array) {
126    let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
127    let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128');
128    let symKey = await aesGenerator.convertKey(symKeyBlob);
129    console.info('convertKey success');
130    return symKey;
131  }
132  async function aes() {
133    let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]);
134    let symKey = await genSymKeyByData(keyData);
135    let message = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // The message is of 43 bytes. After decoded in UTF-8 format, the message is also of 43 bytes.
136    let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
137    let encryptText = await encryptMessageUpdateBySegment(symKey, plainText);
138    let decryptText = await decryptMessagePromise(symKey, encryptText);
139    if (plainText.data.toString() === decryptText.data.toString()) {
140      console.info('decrypt ok');
141      console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8'));
142    } else {
143      console.error('decrypt failed');
144    }
145  }
146  ```
147
148- Example (using synchronous APIs):
149
150  ```ts
151  import { cryptoFramework } from '@kit.CryptoArchitectureKit';
152  import { buffer } from '@kit.ArkTS';
153
154  function generateRandom(len: number) {
155    let rand = cryptoFramework.createRandom();
156    let generateRandSync = rand.generateRandomSync(len);
157    return generateRandSync;
158  }
159
160  function genGcmParamsSpec() {
161    let ivBlob = generateRandom(12);
162    let arr = [1, 2, 3, 4, 5, 6, 7, 8]; // 8 bytes
163    let dataAad = new Uint8Array(arr);
164    let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
165    arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
166    let dataTag = new Uint8Array(arr);
167    let tagBlob: cryptoFramework.DataBlob = {
168      data: dataTag
169    }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption.
170    let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
171      iv: ivBlob,
172      aad: aadBlob,
173      authTag: tagBlob,
174      algName: "GcmParamsSpec"
175    };
176    return gcmParamsSpec;
177  }
178  let gcmParams = genGcmParamsSpec();
179  // Encrypt the message by segment.
180  function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
181    let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
182    cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
183    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
184    let cipherText = new Uint8Array();
185    for (let i = 0; i < plainText.data.length; i += updateLength) {
186      let updateMessage = plainText.data.subarray(i, i + updateLength);
187      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
188      // Call update() multiple times to pass in data by segment.
189      let updateOutput = cipher.updateSync(updateMessageBlob);
190      // Combine the result of each update() to obtain the ciphertext. In certain cases, the doFinal() result also needs to be combined, which depends on the cipher block mode
191      // and padding mode you use. In this example, the GCM mode is used, and the doFinal() result contains authTag but not ciphertext. Therefore, there is no need to combine the doFinal() result.
192      let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length);
193      mergeText.set(cipherText);
194      mergeText.set(updateOutput.data, cipherText.length);
195      cipherText = mergeText;
196    }
197    gcmParams.authTag = cipher.doFinalSync(null);
198    let cipherBlob: cryptoFramework.DataBlob = { data: cipherText };
199    return cipherBlob;
200  }
201  // Decrypt the message by segment.
202  function decryptMessage(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
203    let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
204    decoder.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams);
205    let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required.
206    let decryptText = new Uint8Array();
207    for (let i = 0; i < cipherText.data.length; i += updateLength) {
208      let updateMessage = cipherText.data.subarray(i, i + updateLength);
209      let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage };
210      // Call update() multiple times to pass in data by segment.
211      let updateOutput = decoder.updateSync(updateMessageBlob);
212      // Combine the update() results to obtain the plaintext.
213      let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length);
214      mergeText.set(decryptText);
215      mergeText.set(updateOutput.data, decryptText.length);
216      decryptText = mergeText;
217    }
218    let decryptData = decoder.doFinalSync(null);
219    if (decryptData === null) {
220      console.info('GCM decrypt success, decryptData is null');
221    }
222    let decryptBlob: cryptoFramework.DataBlob = { data: decryptText };
223    return decryptBlob;
224  }
225  function genSymKeyByData(symKeyData: Uint8Array) {
226    let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
227    let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128');
228    let symKey = aesGenerator.convertKeySync(symKeyBlob);
229    console.info('convertKeySync success');
230    return symKey;
231  }
232  function main() {
233    let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]);
234    let symKey = genSymKeyByData(keyData);
235    let message = "aaaaa.....bbbbb.....ccccc.....ddddd.....eee"; // The message is of 43 bytes. After decoded in UTF-8 format, the message is also of 43 bytes.
236    let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) };
237    let encryptText = encryptMessageUpdateBySegment(symKey, plainText);
238    let decryptText = decryptMessage(symKey, encryptText);
239    if (plainText.data.toString() === decryptText.data.toString()) {
240      console.info('decrypt ok');
241      console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8'));
242    } else {
243      console.error('decrypt failed');
244    }
245  }
246
247  ```
248