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