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. Use [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. Use [cryptoFramework.createCipher](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatecipher) with the string parameter **'AES128|GCM|PKCS7'** to create a **Cipher** instance. The key type is **AES128**, block cipher mode is **GCM**, and the padding mode is **PKCS7**. 15 163. Use [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. Use [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.authTag** as the authentication information for decryption. 33 34 In GCM mode, extract the last 16 bytes from the encrypted data as the authentication information for initializing the **Cipher** instance in decryption. In the example, **authTag** is of 16 bytes. 35 36 37**Decryption** 38 39 401. Use [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. 41 422. 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. 43 443. Use [Cipher.doFinal](../../reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#dofinal-1) to obtain the decrypted data. 45 46 47- Example (using asynchronous APIs): 48 49 ```ts 50 import { cryptoFramework } from '@kit.CryptoArchitectureKit'; 51 import { buffer } from '@kit.ArkTS'; 52 function genGcmParamsSpec() { 53 let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes 54 let dataIv = new Uint8Array(arr); 55 let ivBlob: cryptoFramework.DataBlob = { data: dataIv }; 56 arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes 57 let dataAad = new Uint8Array(arr); 58 let aadBlob: cryptoFramework.DataBlob = { data: dataAad }; 59 arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes 60 let dataTag = new Uint8Array(arr); 61 let tagBlob: cryptoFramework.DataBlob = { 62 data: dataTag 63 }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption. 64 let gcmParamsSpec: cryptoFramework.GcmParamsSpec = { 65 iv: ivBlob, 66 aad: aadBlob, 67 authTag: tagBlob, 68 algName: "GcmParamsSpec" 69 }; 70 return gcmParamsSpec; 71 } 72 let gcmParams = genGcmParamsSpec(); 73 // Encrypt the message by segment. 74 async function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) { 75 let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7'); 76 await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams); 77 let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required. 78 let cipherText = new Uint8Array(); 79 for (let i = 0; i < plainText.data.length; i += updateLength) { 80 let updateMessage = plainText.data.subarray(i, i + updateLength); 81 let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage }; 82 // Call update() multiple times to pass in data by segment. 83 let updateOutput = await cipher.update(updateMessageBlob); 84 // 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 85 // 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. 86 let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length); 87 mergeText.set(cipherText); 88 mergeText.set(updateOutput.data, cipherText.length); 89 cipherText = mergeText; 90 } 91 gcmParams.authTag = await cipher.doFinal(null); 92 let cipherBlob: cryptoFramework.DataBlob = { data: cipherText }; 93 return cipherBlob; 94 } 95 // Decrypt the message by segment. 96 async function decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) { 97 let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7'); 98 await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams); 99 let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required. 100 let decryptText = new Uint8Array(); 101 for (let i = 0; i < cipherText.data.length; i += updateLength) { 102 let updateMessage = cipherText.data.subarray(i, i + updateLength); 103 let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage }; 104 // Call update() multiple times to pass in data by segment. 105 let updateOutput = await decoder.update(updateMessageBlob); 106 // Combine the update() results to obtain the plaintext. 107 let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length); 108 mergeText.set(decryptText); 109 mergeText.set(updateOutput.data, decryptText.length); 110 decryptText = mergeText; 111 } 112 let decryptData = await decoder.doFinal(null); 113 if (decryptData == null) { 114 console.info('GCM decrypt success, decryptData is null'); 115 } 116 let decryptBlob: cryptoFramework.DataBlob = { data: decryptText }; 117 return decryptBlob; 118 } 119 async function genSymKeyByData(symKeyData: Uint8Array) { 120 let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData }; 121 let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128'); 122 let symKey = await aesGenerator.convertKey(symKeyBlob); 123 console.info('convertKey success'); 124 return symKey; 125 } 126 async function aes() { 127 let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]); 128 let symKey = await genSymKeyByData(keyData); 129 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. 130 let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) }; 131 let encryptText = await encryptMessageUpdateBySegment(symKey, plainText); 132 let decryptText = await decryptMessagePromise(symKey, encryptText); 133 if (plainText.data.toString() === decryptText.data.toString()) { 134 console.info('decrypt ok'); 135 console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8')); 136 } else { 137 console.error('decrypt failed'); 138 } 139 } 140 ``` 141 142- Example (using synchronous APIs): 143 144 ```ts 145 import { cryptoFramework } from '@kit.CryptoArchitectureKit'; 146 import { buffer } from '@kit.ArkTS'; 147 148 function genGcmParamsSpec() { 149 let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes 150 let dataIv = new Uint8Array(arr); 151 let ivBlob: cryptoFramework.DataBlob = { data: dataIv }; 152 arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes 153 let dataAad = new Uint8Array(arr); 154 let aadBlob: cryptoFramework.DataBlob = { data: dataAad }; 155 arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes 156 let dataTag = new Uint8Array(arr); 157 let tagBlob: cryptoFramework.DataBlob = { 158 data: dataTag 159 }; // The GCM authTag is obtained by doFinal() in encryption and passed in params of init() in decryption. 160 let gcmParamsSpec: cryptoFramework.GcmParamsSpec = { 161 iv: ivBlob, 162 aad: aadBlob, 163 authTag: tagBlob, 164 algName: "GcmParamsSpec" 165 }; 166 return gcmParamsSpec; 167 } 168 let gcmParams = genGcmParamsSpec(); 169 // Encrypt the message by segment. 170 function encryptMessageUpdateBySegment(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) { 171 let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7'); 172 cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams); 173 let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required. 174 let cipherText = new Uint8Array(); 175 for (let i = 0; i < plainText.data.length; i += updateLength) { 176 let updateMessage = plainText.data.subarray(i, i + updateLength); 177 let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage }; 178 // Call update() multiple times to pass in data by segment. 179 let updateOutput = cipher.updateSync(updateMessageBlob); 180 // 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 181 // 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. 182 let mergeText = new Uint8Array(cipherText.length + updateOutput.data.length); 183 mergeText.set(cipherText); 184 mergeText.set(updateOutput.data, cipherText.length); 185 cipherText = mergeText; 186 } 187 gcmParams.authTag = cipher.doFinalSync(null); 188 let cipherBlob: cryptoFramework.DataBlob = { data: cipherText }; 189 return cipherBlob; 190 } 191 // Decrypt the message by segment. 192 function decryptMessage(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) { 193 let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7'); 194 decoder.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams); 195 let updateLength = 20; // Set the data length to be passed in each time to 20 bytes. You can set this parameter as required. 196 let decryptText = new Uint8Array(); 197 for (let i = 0; i < cipherText.data.length; i += updateLength) { 198 let updateMessage = cipherText.data.subarray(i, i + updateLength); 199 let updateMessageBlob: cryptoFramework.DataBlob = { data: updateMessage }; 200 // Call update() multiple times to pass in data by segment. 201 let updateOutput = decoder.updateSync(updateMessageBlob); 202 // Combine the update() results to obtain the plaintext. 203 let mergeText = new Uint8Array(decryptText.length + updateOutput.data.length); 204 mergeText.set(decryptText); 205 mergeText.set(updateOutput.data, decryptText.length); 206 decryptText = mergeText; 207 } 208 let decryptData = decoder.doFinalSync(null); 209 if (decryptData == null) { 210 console.info('GCM decrypt success, decryptData is null'); 211 } 212 let decryptBlob: cryptoFramework.DataBlob = { data: decryptText }; 213 return decryptBlob; 214 } 215 async function genSymKeyByData(symKeyData: Uint8Array) { 216 let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData }; 217 let aesGenerator = cryptoFramework.createSymKeyGenerator('AES128'); 218 let symKey = await aesGenerator.convertKey(symKeyBlob); 219 console.info('convertKey success'); 220 return symKey; 221 } 222 async function main() { 223 let keyData = new Uint8Array([83, 217, 231, 76, 28, 113, 23, 219, 250, 71, 209, 210, 205, 97, 32, 159]); 224 let symKey = await genSymKeyByData(keyData); 225 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. 226 let plainText: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(message, 'utf-8').buffer) }; 227 let encryptText = encryptMessageUpdateBySegment(symKey, plainText); 228 let decryptText = decryptMessage(symKey, encryptText); 229 if (plainText.data.toString() === decryptText.data.toString()) { 230 console.info('decrypt ok'); 231 console.info('decrypt plainText: ' + buffer.from(decryptText.data).toString('utf-8')); 232 } else { 233 console.error('decrypt failed'); 234 } 235 } 236 237 ``` 238