Arduino中AES-CBC加密输出与Android/在线工具不一致问题排查
AES-CBC加密输出差异排查请求
问题描述
我需要演示128-bit AES加密功能,Android端及在线工具可生成预期的加密输出,但Arduino端的AES-CBC加密输出与二者不符,不过Arduino端解密后仍能得到正确明文,请求排查输出差异原因。
Arduino端代码
#include <string.h> #include <stdio.h> #include <hwcrypto/aes.h> #include <stdlib.h> #include <base64.h> static inline int32_t _getCycleCount(void) { int32_t ccount; __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); return ccount; } char plaintext[16384]; char encrypted[16384]; int encodetest() { unsigned char iv[16] = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A}; unsigned char key[32] ={0x66 ,0x54 ,0x6A ,0x57 ,0x6E ,0x5A ,0x72 ,0x34 ,0x75 ,0x37 ,0x78 ,0x21 ,0x41 ,0x25 ,0x44 ,0x2A ,0x47 ,0x2D ,0x4A ,0x61 ,0x4E ,0x64 ,0x52 ,0x67 ,0x55 ,0x6B ,0x58 ,0x70 ,0x32 ,0x73 ,0x35 ,0x76}; strcpy( plaintext, "Hello" ); Serial.printf( "\n================================Encode================================\nIV HEX:" ); for(int d = 0; d < sizeof(iv); d++) { Serial.printf( "%02x", iv[d] ); } Serial.printf( "\nKey HEX:" ); for(int f = 0; f < sizeof(key); f++) { Serial.printf( "%02x", key[f] ); } Serial.printf("\nWill Encode \n%s",plaintext); esp_aes_context ctx; esp_aes_init( &ctx ); esp_aes_setkey( &ctx, key, 256 ); int32_t start = _getCycleCount(); esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), iv, (uint8_t*)plaintext, (uint8_t*)encrypted ); int32_t end = _getCycleCount(); float enctime = (end-start)/240.0; Serial.printf( "Encryption time: %.2fus (%f MB/s)\n", enctime, (sizeof(plaintext)*1.0)/enctime ); memset( plaintext, 0, sizeof( plaintext ) ); int i; Serial.printf( "Hex" ); for( i = 0; i < 128; i++ ) { if (i % 8 == 0) {Serial.printf( " " );}; Serial.printf( "%02x", encrypted[i] ); } Serial.printf( "\n================================Decode================================ \n" ); unsigned char iva[16] = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A}; for(int c = 0; c < sizeof(iva); c++) { Serial.printf( "%02x ", iva[c] ); } memset( iv, 0, sizeof( iv ) ); esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), iva, (uint8_t*)encrypted, (uint8_t*)plaintext ); Serial.printf( "\nHuman Readable\n" ); for( i = 0; i < sizeof( plaintext ); i++ ) { Serial.printf( "%c", plaintext[i]); } Serial.printf( "\n" ); esp_aes_free( &ctx ); } void setup() { Serial.begin(115200); Serial.println("Execute Begin"); int a = encodetest(); }
Android端代码
public class MainActivity extends AppCompatActivity { EditText inputText, inputPassword; TextView outputText; Button encBtn, decBtn; String outputString; String AES = "AES"; private static final String SECRET_KEY = "fTjWnZr4u7x!A%D*G-JaNdRgUkXp2s5v"; private static final String SALT = "ssshhhhhhhhhhh!!!!"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); inputText = findViewById(R.id.inputText); inputPassword = findViewById(R.id.password); outputText =findViewById(R.id.outputText); encBtn = findViewById(R.id.encBtn); decBtn = findViewById(R.id.decBtn); encBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { outputString = encrypt(inputText.getText().toString()); outputText.setText(outputString); } catch (Exception e) { e.printStackTrace(); } } }); } public static String encrypt(String strToEncrypt) { try { byte[] iv = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A}; IvParameterSpec ivspec = new IvParameterSpec(iv); byte[] key = SECRET_KEY.getBytes("UTF-8"); SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec); byte[] encVal = cipher.doFinal(strToEncrypt.getBytes()); String encryptedValue = Base64.encodeToString(encVal, Base64.DEFAULT); return encryptedValue; } catch (Exception e) { System.out.println("Error while encrypting: " + e.toString()); } return null; } }
测试数据
- 明文:
Hello - Arduino加密输出:
7c6e13cc7e943313 ee111a1c90387c04 ac1fa7a7c491d53e be6eea1feaf01815 d7733389f23dd8f4 ed6dae3e78f5388e 1eda7bbbaf3bad5f 1b5b0d01281805d4 2c38139fcda0a05b 7eded259247e105b 16b228aacefc89ee 9dd2db2392537168 63adf145feb2b33a 287c5a5623212a0d c8760ed78ffc6508 a1e95d6235895ef5 - Android加密输出:与在线工具生成的标准输出一致
差异原因分析
加密输入长度完全错误
Arduino端调用esp_aes_crypt_cbc时传入的长度是sizeof(plaintext),即16384字节,这意味着你在加密整个数组——而数组中除了前5个字节是Hello,其余都是未初始化的随机垃圾数据。最终加密输出是16384字节的内容,你打印的前128字节自然和Android端只加密Hello(经PKCS5填充到16字节)的结果完全不同。AES密钥长度与需求不符
你需求是演示128-bit AES,但两端实际都在使用256-bit密钥:- Android端的
SECRET_KEY字符串UTF-8编码后是32字节,对应AES-256; - Arduino端直接定义了32字节的key数组,并调用
esp_aes_setkey时指定256位长度。
- Android端的
填充方式未统一
Android端自动使用PKCS5Padding对不足块长的明文进行填充,而Arduino端的esp_aes_crypt_cbc是底层加密接口,不会自动处理填充,你直接传入原始明文数组,相当于对包含垃圾数据的16384字节进行无填充加密。
修复建议
- 修正加密长度:获取
Hello的实际字节长度(strlen(plaintext)),然后手动进行PKCS5填充到16字节的整数倍,再传入加密函数;或者使用支持自动填充的AES封装库。 - 统一密钥长度:如果要演示128-bit AES,需将密钥改为16字节(128位),两端保持一致。
- 统一IV处理:CBC模式加密后IV会被修改,解密时必须使用原始IV(Arduino端已经做到,但需注意后续代码不要误修改原始IV)。
内容的提问来源于stack exchange,提问作者Do Ji
相关产品推荐
相关产品推荐

