You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 13:52:31