First of all, I use the OpenSSL library to implement the encryption. When using, I need to import these libraries and header files, which have been uploaded to GitHub. First pull them down from here.
1, Introducing AES encryption:
AES is a symmetric encryption algorithm. You can specify your own key to participate in the encryption of plaintext. The same key is used for encryption and decryption. There are five encryption modes,
- Electronic Codebook Book (ECB): this mode is to divide the whole plaintext into several identical small segments, and then encrypt each small segment.
2. Cipher Block Chaining (CBC): this mode first divides the plaintext into several small segments, and then performs XOR operation with the initial block or the ciphertext segment of the previous segment, and then encrypts it with the key.
3. Calculator mode (Counter (CTR)): calculator mode is not common. In CTR mode, there is a self increasing operator. This operator uses the output encrypted by the key and the result of plaintext XOR to obtain the ciphertext, which is equivalent to one encryption at a time. This encryption method is simple, fast, safe and reliable, and can be encrypted in parallel. However, when the calculator can not maintain a long time, the key can only be used once.
4. Cipher feedback mode (CFB):
5. Output feedback mode (OFB)
As the 4th and 5th are very complex, they are shown in schematic diagram
4. Cipher feedback mode (CFB):
5. Output feedback mode (OFB)
1.1 a picture is used to illustrate the process of AES symmetric encryption as a whole:
1.2 filling of block cipher
This is a PKCS#5 filling method. If the plaintext is just 16 bytes and does not need to be filled, 16 bytes of 16 will be added. You can see the code at a glance later. Here I use cbc encryption mode, and here I use c + +. Java has been based on api, and c + + code can also be called in jni mode. Cross platform is an advantage, and the superior performance of c + + code is also an advantage. See the code implementation below:
//The test entry is written in the main function int main(int argc, char** argv) { //Record the encryption start time to evaluate the encryption time struct timeval tv; gettimeofday(&tv, NULL); int64_t start = tv.tv_sec * 1000 + tv.tv_usec / 1000; //Manually write a string of plaintext for encryption. Readers can replace this plaintext with plaintext bytes that need to be encrypted std::string data = "abcdefghijklmnopqrstovwxyz"; int16_t dateLen = data.length(); /**Generally, AES uses 128bit as the length of encryption block, that is, 16 bytes, so the key length generally uses 16 bytes for encryption*/ int AES_block_num = dateLen / AES_BLOCK_SIZE + 1; int AES_encrypt_data_len = AES_block_num * AES_BLOCK_SIZE; unsigned char *encryptBuffer = (unsigned char *)malloc(AES_encrypt_data_len); unsigned char *ciphertext= (unsigned char *)malloc(AES_encrypt_data_len); if( encryptBuffer == NULL || out == NULL) { ALOGE("Error,malloc fail:encryptBuffer or out"); return; } memset(encryptBuffer,0,AES_encrypt_data_len); memset(ciphertext,0,AES_encrypt_data_len); /**The filling principle is: 1,If the source byte length is not an integer multiple of 16, it is necessary to fill in the integer multiple of 16 bytes, and fill in (16 len) 16 len (value) where it is not enough, For example, the original byte is 0x10 11 01 02 05 04. For a total of 6 bytes, 16-6 bytes need to be supplemented, and each byte is filled with 16-6 = 10 (Hex is 0x0a) After filling, it becomes: 0x10 11 01 02 05 04 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0A 0A 2,If the length of the source byte is exactly an integer multiple of 16, 16 Decimal bytes of 16 need to be added*/ int fillDataAndNum = 0; if( dateLen % AES_BLOCK_SIZE >0 ) { fillDataAndNum = AES_encrypt_data_len - dateLen; } else { fillDataAndNum = AES_BLOCK_SIZE; } memset(encryptBuffer,fillDataAndNum,AES_encrypt_data_len); memcpy(encryptBuffer,date, dateLen); AES_encryptOrEncrypt(encryptBuffer,AES_encrypt_data_len,TRUE,ciphertext); if(ciphertext) { //Here you can print out the value in ciphertext, and the encrypted ciphertext is in ciphertext } else { ALOGE("[AES_encrypt]:faild!\n"); break; } struct timeval tv1; gettimeofday(&tv1, NULL); int64_t end = tv1.tv_sec * 1000 + tv1.tv_usec / 1000; ALOGE("11111ddddd:AES_encryptDataTime: %d",end - start); ALOGE("11111aaa:encryptData len = %d",AES_encrypt_data_len ); //decrypt int AES_block_num1 = AES_encrypt_data_len / AES_BLOCK_SIZE + 1; int AES_decrypt_data_len = AES_block_num1 * AES_BLOCK_SIZE; unsigned char *decryptBuffer = (unsigned char *)malloc(AES_decrypt_data_len ); unsigned char *out1 = (unsigned char *)malloc(AES_decrypt_data_len ); if( decryptBuffer == NULL || out1 == NULL) { ALOGE("Error,malloc fail:encryptBuffer or out"); return; } memset(decryptBuffer ,AES_decrypt_data_len ); memset(out1,0,AES_decrypt_data_len ); /**The filling principle is: 1,If the source byte length is not an integer multiple of 16, it is necessary to fill in the integer multiple of 16 bytes, and fill in (16 len) 16 len (value) where it is not enough, For example, the original byte is 0x10 11 01 02 05 04. For a total of 6 bytes, 16-6 bytes need to be supplemented, and each byte is filled with 16-6 = 10 (Hex is 0x0a) After filling, it becomes: 0x10 11 01 02 05 04 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0A 0A 2,If the length of the source byte is exactly an integer multiple of 16, 16 Decimal bytes of 16 need to be added*/ int fillDataAndNum1 = 0; if(AES_decrypt_data_len % AES_BLOCK_SIZE >0 ) { fillDataAndNum1 = AES_decrypt_data_len - AES_encrypt_data_len ; } else { fillDataAndNum1 = AES_BLOCK_SIZE; } memset(decryptBuffer,fillDataAndNum1,AES_decrypt_data_len ); memcpy(decryptBuffer,msg_info.msg, AES_encrypt_data_len ); AES_encryptOrEncrypt(decryptBuffer,AES_decrypt_data_len,FALSE,out1); ALOGE("11111bbb:decryptData"); if(out1) { //Here you can print out the value in out1, and the decrypted plaintext is in out1 } else { ALOGE("[AES_encrypt]:faild!\n"); break; } return 0; } void GBDataHandle::AES_encryptOrEncrypt(uint8_t *data,uint16_t len,bool isEncrypt,unsigned char* out) { AES_KEY aes; char ivValue[] = "0000000000000000"; unsigned char key[17] = {0}; memset(key, '0', 16);//Assign the default encryption key, which is only used for testing if(isEncrypt) { //Set encryption key, 16 bytes int keyLen = strlen((char*)(key)) * 8; ALOGD(" key = %s, keyLen = %d", key, keyLen); if (AES_set_encrypt_key((unsigned char*)key, keyLen, &aes) < 0) { ALOGD("Unable to set encryption key in AES"); return; } } else { //Set decryption key, 16 bytes int keyLen = strlen((char*)(key)) * 8; ALOGD(" key = %s, keyLen = %d", key, keyLen); if (AES_set_decrypt_key((unsigned char*)key, keyLen, &aes) < 0) { ALOGD("Unable to set encryption key in AES"); return; } } //std::string data_bak = (char*)pcInput; AES_cbc_encrypt((const unsigned char*)pcInput, (unsigned char*)out, nLen, &aes, (unsigned char*)ivValue, isEncrypt ? AES_ENCRYPT : AES_DECRYPT); }
In this way, encryption and decryption can be completed, because you can pay attention to encapsulation and optimization when using demo. The overall idea is right