OpenSSL TripleDES CBC模式解密乱码问题求助
TripleDES CBC/ECB模式加解密乱码问题(OpenSSL C++实现)
问题描述
在C++中基于OpenSSL实现TripleDES的CBC模式加解密,加密过程日志显示正常,但解密后输出乱码。切换至ECB模式测试,同样无法得到正确明文。
相关代码
加解密核心实现
#define DES_BLOCK_SIZE 8 unsigned char* start3DES (const unsigned char* text, int length, int mode) { // 1 - 加密 | 0 - 解密 DES_cblock Key1 = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }; DES_cblock Key2 = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }; DES_cblock Key3 = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 } ; DES_key_schedule SchKey1, SchKey2, SchKey3; DES_cblock input_data = {0xc3, 0x07, 0xd1, 0xe5, 0x66, 0x99, 0x18, 0x74 }; if ( -2 == (DES_set_key_checked(&Key1, &SchKey1) || DES_set_key_checked(&Key2, &SchKey2) || DES_set_key_checked(&Key3, &SchKey3))) { LOGE(" 弱密钥 \n"); return nullptr; } DES_cblock cipher; DES_cblock input_text; DES_cblock enc_cipher; // unsigned char[8] DES_cblock out_buf; unsigned char *cipher_out; cipher_out = new unsigned char[length]; DES_cblock cblock = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; // 初始化向量(IV) // 1 - 加密 | 0 - 解密 if(mode == DES_ENCRYPT) { memset(cblock,0,sizeof(DES_cblock)); DES_set_odd_parity(&cblock); int i = 0; int j = 0; int count_read = 0; int count_write = 0; int count_read_whole = 0; while (true) { for(; i < length ;) { for(int z = 0; z < DES_BLOCK_SIZE; ++z) { // 读取8字节到input_text if(text[i] == '\0') break; input_text[z] = text[i]; ++i; ++count_read; if(count_read == DES_BLOCK_SIZE) break; } break; } if (count_read < DES_BLOCK_SIZE) break; // 加密8字节数据:input_text -> cipher DES_ede3_cbc_encrypt(input_text, cipher, count_read, &SchKey1, &SchKey2, &SchKey3,&cblock, DES_ENCRYPT); count_read_whole += count_read; // 累计读取字节数 count_read = 0; for(; j < count_read_whole; ++j) { // 将加密后的8字节写入cipher_out cipher_out[j] = cipher[count_write]; ++count_write; if(count_write == DES_BLOCK_SIZE) break; } j++; count_write = 0; } return cipher_out; } if(mode == DES_DECRYPT) { memset(cblock,0,sizeof(DES_cblock)); DES_set_odd_parity(&cblock); unsigned char *plain_out; plain_out = new unsigned char[length]; int count_read = 0; int count_write = 0; int count_read_whole = 0; int i = 0; int j = 0; while (true) { for(; i < length ;) { for(int z = 0; z < DES_BLOCK_SIZE; ++z) { // 读取8字节到enc_cipher if(text[i] == '\0') break; enc_cipher[z] = cipher_out[i]; ++i; ++count_read; if(count_read == DES_BLOCK_SIZE) break; } break; } if (count_read < DES_BLOCK_SIZE) break; // 解密8字节数据:enc_cipher -> out_buf DES_ede3_cbc_encrypt(enc_cipher, out_buf, count_read, &SchKey1, &SchKey2, &SchKey3,&cblock, DES_DECRYPT); count_read_whole += count_read; // 累计读取字节数 count_read = 0; for(; j < count_read_whole; ++j) { plain_out[j] = out_buf[count_write]; ++count_write; if(count_write == DES_BLOCK_SIZE) break; } j++; count_write = 0; } return plain_out; } else { LOGE("请选择模式:1 - 加密 | 0 - 解密"); return nullptr; } }
C++ JNI调用代码
extern "C" JNIEXPORT jbyteArray JNICALL Java_com_example_myapplication_MainActivity_encrypt3des(JNIEnv *env, jobject thiz, jbyteArray plain_text) { jboolean isCopy; int plainText_len = env->GetArrayLength(plain_text); jbyte* temp = env->GetByteArrayElements(plain_text,&isCopy); const unsigned char* plainText; plainText = (unsigned char *) temp; unsigned char* cipher_text = start3DES(plainText, plainText_len, DES_ENCRYPT); for(int i = 0; i < plainText_len; ++i) LOGI("Cipher[%d]: %02X", i, cipher_text[i]); jbyteArray ByteArray = env->NewByteArray(plainText_len); env->SetByteArrayRegion(ByteArray, 0, plainText_len, reinterpret_cast<const jbyte *>(start3DES(plainText, plainText_len, 1))); return ByteArray; } extern "C" JNIEXPORT jbyteArray JNICALL Java_com_example_myapplication_MainActivity_decrypt3des(JNIEnv *env, jobject thiz, jbyteArray enc_text) { jboolean isCopy; int encText_len = env->GetArrayLength(enc_text); jbyte* temp = env->GetByteArrayElements(enc_text, &isCopy); const unsigned char* encText; encText = (unsigned char *) temp; unsigned char* plain = start3DES(encText, encText_len, DES_DECRYPT); for(int i = 0; i < encText_len; ++i) LOGI("Plain[%d]: %02X", i, plain[i]); jbyteArray DecryptedByteArray = env->NewByteArray(encText_len); env->SetByteArrayRegion(DecryptedByteArray, 0, encText_len, reinterpret_cast<const jbyte *>(start3DES(encText, encText_len, 0))); return DecryptedByteArray; }
Java端调用代码
byte[] plainText1 = "c307d1e566991874 ".getBytes(); byte[] t_des_encrypted = encrypt3des(plainText1); decrypt3des(t_des_encrypted);
问题根源与修复方案
1. 解密分支非法访问未定义变量
解密逻辑中直接使用cipher_out[i]读取密文,但cipher_out是加密分支的局部变量,解密时该变量不存在,属于非法内存访问,这是乱码的核心原因。
// 原错误代码 enc_cipher[z] = cipher_out[i]; // 修复后 enc_cipher[z] = text[i];
2. OpenSSL EDE3 CBC接口使用错误
DES_ede3_cbc_encrypt第三个参数是总字节数,而非单块8字节。手动分块时传入单块长度会导致IV无法正确更新,后续块加解密失效。无需手动分块,直接让OpenSSL处理完整数据:
// 加密分支替换原循环代码 memset(cipher_out, 0, length); DES_ede3_cbc_encrypt(text, cipher_out, length, &SchKey1, &SchKey2, &SchKey3, &cblock, DES_ENCRYPT); return cipher_out;
// 解密分支替换原循环代码 memset(plain_out, 0, length); DES_ede3_cbc_encrypt(text, plain_out, length, &SchKey1, &SchKey2, &SchKey3, &cblock, DES_DECRYPT); return plain_out;
3. 未处理数据填充问题
TripleDES要求输入长度为8字节整数倍,需实现PKCS#7填充(加密补位、解密去位):
// 加密前填充示例 int padded_len = ((length + DES_BLOCK_SIZE - 1) / DES_BLOCK_SIZE) * DES_BLOCK_SIZE; unsigned char* padded_text = new unsigned char[padded_len]; memcpy(padded_text, text, length); unsigned char pad_val = padded_len - length; memset(padded_text + length, pad_val, pad_val); // 解密后去填充示例 unsigned char pad_val = plain_out[padded_len - 1]; int actual_len = padded_len - pad_val; unsigned char* actual_text = new unsigned char[actual_len]; memcpy(actual_text, plain_out, actual_len); delete[] plain_out; return actual_text;
4. JNI调用重复执行加解密导致异常
加密函数中两次调用start3DES,不仅造成内存泄漏,还可能因填充差异导致返回结果与日志不一致,需改为单次调用:
// 修复后代码 unsigned char* cipher_text = start3DES(plainText, plainText_len, DES_ENCRYPT); for(int i = 0; i < plainText_len; ++i) LOGI("Cipher[%d]: %02X", i, cipher_text[i]); jbyteArray ByteArray = env->NewByteArray(plainText_len); env->SetByteArrayRegion(ByteArray, 0, plainText_len, reinterpret_cast<const jbyte *>(cipher_text)); // 注意:需在JNI层管理内存,避免泄漏
5. IV初始化逻辑矛盾
先定义IV为{0x01,0x02,...},随后又用memset重置为全0,建议保持IV一致性,避免随意修改。
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

