前台传到后台base64加密后的json格式,后台加密乱码问题

在尝试解密从前台传到后台的Base64加密JSON字符串时遇到乱码问题。直接在后台解密无误,但从前台接收的字符串解密后出现问号乱码。通过更换解密工具类成功解决问题,适用于JDK1.5以上版本,但仍不清楚两种方法的具体差异。

今天在做base64位加密解密方法时发现一个有趣的问题
我将加密好的内容放在后台代码中直接解密,一点瑕疵都没有,但是,当我从前台接收加密好的字符串来后台解密的时候,就一直乱码,一堆问号的那种乱码。
我在网上找了有一会了,后来换了一个工具就好用啦,下面是具体的过程。
没有实现的过程:

public String generateCode(@RequestBody String params) throws IOException {
   
   
        log.error("接收到的加密字符串:" + params);
        String jiamiStr = params;
        
你提出了一个非常关键的问题: > **“但是改变后的 base64 加密出来的数据解密后会有乱码”** 这说明虽然加密成功了,但**解密失败或不完整**,导致输出不是合法的 JSON 或字符串,而是类似: ``` sN... 或 {"fre} ``` 这类“部分乱码”或“二进制夹杂可读文本”的现象,通常不是因为算法本身有问题,而是 **加解密过程中某些参数不一致或处理不当**。 --- ## ✅ 问题定位:为什么解密会出现乱码? 我们来系统性分析可能导致“解密乱码”的常见原因,并结合你的场景(AES-256-CBC + Base64)逐一排查。 --- ### ❌ 原因一:IV 不匹配(最常见!) #### 🔍 现象 - 加密时用了某个随机 IV - 解密时用了另一个 IV(比如固定 IV、空 IV、错误的 Base64 解码) - 导致第一个块解密错误,后续块连锁出错 #### 📌 结果 - 第一块明文完全错误 → 出现乱码 - 后续块可能部分正确(CBC 模式特性) #### ✅ 示例 ```c // 加密时 unsigned char iv_encrypt[16] = { /* 随机值 */ }; // 解密时却用 unsigned char iv_decrypt[16] = {0}; // 全零 IV → 错! ``` 👉 必须保证:**解密时使用的 IV 必须和加密时完全相同!** > 💡 解决方案:将 IV 和密文一起传输,例如: > ```json > { > "iv": "base64_encoded_iv", > "cipher": "base64_encoded_ciphertext" > } > ``` --- ### ❌ 原因二:Base64 编码/解码错误 #### 🔍 现象 - `base64_decode(cipher_b64, &decoded_cipher)` 返回长度错误 - 或者解码后的二进制数据被截断、填充错误 - 导致传给 `EVP_DecryptUpdate` 的是坏数据 #### 📌 可能问题点: - Base64 字符串包含换行、空格、URL 特殊字符(如 `_` 替代 `/`) - 使用了 URL-safe Base64 但没做转换 - 解码函数未正确处理 padding(`=`) #### ✅ 示例错误 ```c char *bad_b64 = "AbCd_Ef..."; // 包含 '_' 而非 '/' // 标准 Base64 不接受 '_',必须转成 '/' ``` > ✅ 正确做法:确保使用标准 Base64 编码;如果是 URL-safe(JWT 风格),需先替换 `-` → `+`, `_` → `/` --- ### ❌ 原因三:密文长度传递错误 在调用 `EVP_DecryptUpdate(ctx, out, &outlen, cipher, cipher_len)` 时: - 如果 `cipher_len` 错了(比如少了一个字节) - 或者 `decoded_cipher` 缓冲区被意外截断 - 就会导致解密器读取非法内存或跳过数据 📌 特别注意: - `base64_decode()` 应返回真实二进制长度 - 不要以 `\0` 判断结束(Base64 解码结果可能是二进制流,含 `\0`) --- ### ❌ 原因四:PKCS#7 填充验证失败(导致 Final 失败) AES-CBC 要求明文长度为块大小(16 字节)的整数倍,所以会自动添加 PKCS#7 填充。 但如果: - 密文被篡改 - 长度不对 - IV 错误导致最后一块解密失败 → `EVP_DecryptFinal_ex()` 会失败,并且可能不会写入 `final_len` 但如果你仍然把 `plaintext` 当字符串使用(即使前面有部分解密内容),就会看到“前半段正常 + 后半段乱码”。 --- ### ❌ 原因五:未正确终止字符串或复制结果 假设你这样处理解密结果: ```c plaintext[plaintext_len] = '\0'; // 添加结束符 ``` 但如果 `plaintext_len` 是负数、溢出、或中间有 `\0`,你就不能安全地把它当 C 字符串打印。 或者你在释放资源前就返回了指针,造成悬垂指针。 --- ## ✅ 正确的解密流程代码(防乱码) ```c char *aes_decrypt(const char *cipher_b64, const char *key_b64, const char *iv_b64) { if (!cipher_b64 || !key_b64 || !iv_b64) { fprintf(stderr, "NULL input parameter\n"); return NULL; } // Step 1: Base64 decode key, iv, cipher char *decoded_key = NULL, *decoded_iv = NULL, *decoded_cipher = NULL; int key_len = base64_decode(key_b64, &decoded_key); int iv_len = base64_decode(iv_b64, &decoded_iv); int cipher_len = base64_decode(cipher_b64, &decoded_cipher); if (key_len <= 0 || iv_len != 16 || cipher_len <= 0) { fprintf(stderr, "Invalid base64 or length\n"); goto cleanup; } // Step 2: Alloc buffer unsigned char *plaintext = malloc(cipher_len); // 最大可能长度 if (!plaintext) { fprintf(stderr, "Malloc failed\n"); goto cleanup; } EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { fprintf(stderr, "CTX new failed\n"); free(plaintext); goto cleanup; } if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char*)decoded_key, (const unsigned char*)decoded_iv)) { fprintf(stderr, "Decrypt init failed\n"); free(plaintext); EVP_CIPHER_CTX_free(ctx); goto cleanup; } int plaintext_len = 0; if (1 != EVP_DecryptUpdate(ctx, plaintext, &plaintext_len, (const unsigned char*)decoded_cipher, cipher_len)) { fprintf(stderr, "Decrypt update failed\n"); free(plaintext); EVP_CIPHER_CTX_free(ctx); goto cleanup; } int final_len = 0; if (1 != EVP_DecryptFinal_ex(ctx, plaintext + plaintext_len, &final_len)) { fprintf(stderr, "Decrypt final failed - BAD KEY/IV/CIPHER?\n"); free(plaintext); EVP_CIPHER_CTX_free(ctx); goto cleanup; // ←← 这里失败会导致你继续用脏数据! } plaintext_len += final_len; // Null-terminate for string usage plaintext[plaintext_len] = '\0'; // Copy to result char *result = (char *)malloc(plaintext_len + 1); if (result) { memcpy(result, plaintext, plaintext_len + 1); } free(plaintext); EVP_CIPHER_CTX_free(ctx); cleanup: free(decoded_key); free(decoded_iv); free(decoded_cipher); return result; } ``` > ⚠️ 关键点:只有当 `EVP_DecryptFinal_ex` 成功时才返回有效字符串! --- ## ✅ 如何调试“乱码问题? ### 排查清单: | 检查项 | 方法 | |------|------| | 1. IV 是否一致? | 打印加密解密时的 IV(hex format)对比 | | 2. Base64 是否标准? | 查看是否含 `-`, `_` → 应为 `+`, `/` | | 3. 密文长度是否一致? | `strlen(cipher_b64)` 和解码后长度是否合理? | | 4. `EVP_DecryptFinal_ex` 是否成功? | 如果失败,说明整体解密无效 | | 5. 解密后字符串是否以 `\0` 结尾? | 手动补上再打印 | --- ## ✅ 总结:乱码的根本原因 | 原因 | 是否会导致乱码 | 建议 | |------|----------------|------| | ✅ IV 不匹配 | 是(首块错) | 保存并复用加密时的 IV | | ✅ Base64 解码错误 | 是 | 使用标准 Base64 解码库 | | ✅ 密文被截断 | 是 | 检查 `cipher_len` | | ✅ `EVP_DecryptFinal_ex` 失败仍使用输出 | 是(最大陷阱!) | 失败则直接返回 NULL | | ✅ 明文未正确 null 终止 | 是 | 手动设置 `buf[len] = '\0'` | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值