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