前后端SM2加密交互问题解析与解决方案

SM2 前后端加密交互问题解析与解决方案

问题背景

在 SM2 国密算法加解密交互中,前后端通常使用不同的编码方式:

  • 前端 (如 sm-crypto)生成的密文默认是 Hex 编码 (不带 04 前缀)。
  • 后端 (如 Java 的 BouncyCastleHutool)生成的密文通常是 Base64 编码 (可能含 04 前缀,目前使用Hutoo-all测试不含04 前缀)。

这导致 前端加密后端解密后端加密前端解密 时出现格式不匹配问题。


核心问题分析

场景 问题 原因
前端加密 → 后端解密 解密失败 前端 Hex 密文不带 04,但后端需要 04
后端加密 → 前端解密 解密失败 后端 Base64 转 Hex 后含 04,但前端不需要

解决方案

1. 前端加密 → 后端解密

步骤:

  1. 前端生成 Hex 密文(如 "a1b2c3...")。

  2. 前端需补 04 后传给后端:

    ini 复制代码
    const cipherText = sm2.doEncrypt(plainText, publicKey); // 生成 Hex 密文
    const cipherTextWith04 = "04" + cipherText; // 补 04
  3. 后端解码时直接处理:

    ini 复制代码
    String cipherText = "04a1b2c3..."; // 前端传来的 Hex
    byte[] cipherBytes = Hex.decode(cipherText); // Hex 转字节
    String plainText = sm2Decrypt(cipherBytes, privateKey); // 解密

2. 后端加密 → 前端解密

步骤:

  1. 后端生成 Base64 密文(如 "BElNTU9S...")。

  2. 后端转 Hex 并去掉 04

    arduino 复制代码
    public static String encryptForFrontend(String plainText, String publicKey) {
        String base64Cipher = sm2Encrypt(plainText, publicKey); // Base64 密文
        byte[] bytes = Base64.getDecoder().decode(base64Cipher);
        String hexCipher = Hex.encodeHexString(bytes); // Base64 → Hex
        if (hexCipher.startsWith("04")) {
            hexCipher = hexCipher.substring(2); // 去掉 04
        }
        return hexCipher;
    }
  3. 前端直接解密:

    ini 复制代码
    const plainText = sm2.doDecrypt(cipherText, privateKey); // 自动处理无 04 的 Hex

完整工具类(Java 版)

arduino 复制代码
import org.apache.commons.codec.binary.Hex;
import java.util.Base64;

public class Sm2CryptoUtils {

    /**
     * 后端加密 → 前端可解密的 Hex(无 04)
     */
    public static String encryptForFrontend(String plainText, String publicKey) {
        String base64Cipher = sm2Encrypt(plainText, publicKey);
        byte[] bytes = Base64.getDecoder().decode(base64Cipher);
        String hexCipher = Hex.encodeHexString(bytes);
        return remove04Prefix(hexCipher);
    }

    /**
     * 解密前端传来的 Hex(补 04)
     */
    public static String decryptFromFrontend(String hexCipher, String privateKey) {
        hexCipher = add04PrefixIfNeeded(hexCipher);
        byte[] cipherBytes = Hex.decodeHex(hexCipher);
        return sm2Decrypt(cipherBytes, privateKey);
    }

    private static String remove04Prefix(String hex) {
        return hex.startsWith("04") ? hex.substring(2) : hex;
    }

    private static String add04PrefixIfNeeded(String hex) {
        return hex.startsWith("04") ? hex : "04" + hex;
    }
}

前端适配代码(JavaScript 版)

php 复制代码
import { sm2 } from 'sm-crypto';

/**
 * 加密 → 传给后端(补 04)
 */
function encryptForBackend(plainText, publicKey) {
  const cipherText = sm2.doEncrypt(plainText, publicKey); // Hex 密文
  return "04" + cipherText; // 补 04
}

/**
 * 解密后端传来的 Hex(去 04)
 */
function decryptFromBackend(cipherText, privateKey) {
  if (cipherText.startsWith("04")) {
    cipherText = cipherText.substring(2); // 去 04
  }
  return sm2.doDecrypt(cipherText, privateKey);
}

关键点总结

  1. 编码差异

    • 前端默认 Hex,后端默认 Base64。
    • SM2 密文的 04 是椭圆曲线未压缩标识,部分库需要它,部分库不需要。
  2. 转换规则

    方向 操作
    前端 → 后端 Hex 补 04
    后端 → 前端 Base64 → Hex → 去 04
  3. 为什么 04 重要?

    • 某些库(如 Java 的 BouncyCastle)要求密文含 04,而 sm-crypto 默认不带。

测试用例

1. 前端加密 → 后端解密

arduino 复制代码
// 前端
const cipherText = encryptForBackend("Hello", "04公钥...");
// 发送 cipherText(带 04)到后端
ini 复制代码
// 后端
String plainText = Sm2CryptoUtils.decryptFromFrontend(cipherText, "私钥");

2. 后端加密 → 前端解密

arduino 复制代码
// 后端
String cipherText = Sm2CryptoUtils.encryptForFrontend("Hello", "公钥");
// 返回 cipherText(无 04)到前端
ini 复制代码
// 前端
const plainText = decryptFromBackend(cipherText, "私钥");

**常见问题 **

Q1:为什么前端生成的 Hex 不带 04

A1:sm-crypto 默认使用压缩格式,而 04 是未压缩标识。可通过配置强制包含:

ini 复制代码
const cipherText = sm2.doEncrypt(plainText, publicKey, { mode: "uncompressed" }); // 含 04

Q2:后端如何判断 Hex 是否需要补 04

A2:SM2 密文长度应为 64 字节(128 字符)或 65 字节(130 字符,含 04)。如果长度是 128,补 04;如果是 130,直接使用。


通过上述方案,可彻底解决 SM2 前后端加解密的编码兼容性问题!

相关推荐
hey_ner1 分钟前
页面PDF文件格式预览(不使用pdf.js)
前端·javascript
luckyPian2 分钟前
前端+AI:HTML5语义标签(一)
前端·ai·面试·html·html5·ai编程
普通码农3 分钟前
Vue3 + dom-to-image 实现高质量截图复制与下载功能
前端
王六岁9 分钟前
🐍 前端开发 0 基础学 Python 入门指南:数字与字符串篇
前端·python·全栈
寻kiki10 分钟前
python test transpose hstack vstack...
后端
shengjk115 分钟前
搞不懂去中心化、主从架构和 HA?1 分钟理清关系,再也不怕被问架构设计
后端
PFinal社区_南丞24 分钟前
开源开发者必备-toilet终端ASCII艺术字工具
后端
我不是混子26 分钟前
Springboot整合Druid
后端
tiantian_cool27 分钟前
HarmonyOS 开发环境配置指南 - macOS 版
前端
写不来代码的草莓熊1 小时前
vue前端面试题——记录一次面试当中遇到的题(10)
前端·vue.js·面试