当前位置: 首页 > news >正文

API接口签名和敏感信息加密使用国密SM方案

这或许是一个讨论文,因为我也不确定哪种方案更好。

签名方式使用国密sm,用到的有sm2、sm3、sm4。因为国家对这方面的安全检查越来越严格了,有要求使用国密。我们项目最近也被密码安全部门检查过,如果不想以后要整改,建议加密相关的需求最好使用国密算法。

sm2是类似于RSA的非对称加密算法,sm3是类似于md5的摘要算法,sm4是类似AES的对称加密算法。

接口中签名使用的参数

appId:appId

nonce:随机数

timestamp:时间戳

message:报文json字符串

这些参数的含义就不在此阐述了。

国密的用法

使用sm3对请求参数进行摘要签名,防止数据篡改。

使用sm4对敏感信息字段加密,比如登录接口有username,password。就可以对password字段加密。如果没有敏感信息则无需加密。

使用sm2对密钥或者摘要数据加密。

sm2的用法我是比较纠结的一点,因为非对称加密算法很复杂,需要占用一部分性能。

sm4的密钥的使用有两种方式,1是客户端每次请求生成一个新的sm4密钥,对敏感信息字段加密后,把密钥放入待签名字符串中一起进行签名,然后用sm2公钥加密此密钥,放入请求头中传给后端。2是固定使用一个sm4密钥,前后端各自保存,不需要放入带签名字符串中和请求头中。

下面说方案。

方案1:

客户端保存appId、sm2公钥。

客户端每次请求生成sm4密钥,仅对敏感信息字段加密,然后放入待签名字符串中一起进行sm3摘要,再使用sm2公钥对sm4密钥加密放入请求头。后端先使用sm2私钥对sm4密钥解密,然后拼接参数进行sm3摘要,最后和前端传来的sm3摘要做对比。

前端代码如下,使用js模拟

/**** @param data 请求参数* @param encryptKey sm4密钥*/
export const apiSign = (data: any, encryptKey: string) => {const appId: string = "appId";const publicKey: string = "sm2公钥";const nonce: string = randomStr(32, true);const timestamp: string = new Date().getTime().toString();const message: string = JSON.stringify(data);const signStr: string = `appId=${appId}&nonce=${nonce}&timestamp=${timestamp}&message=${message}&encryptKey=${encryptKey}`;const signature = SmUtils.sm3(signStr);const encryptKeyResult = SmUtils.sm2Encrypt(encryptKey, publicKey);const header: Map<string, string> = new Map();header.set("appId", appId);header.set("nonce", nonce);header.set("timestamp", timestamp);header.set("signature", signature);header.set("encryptKey", encryptKeyResult);return header;
};

响应的时候后端使用前端传来的sm4密钥加密敏感信息字段,然后签名方式同请求时一致,只是不需要再传回来sm4密文了,前端还是用当前请求生成的sm4密钥解密敏感信息字段。

方案2:

客户端保存appId、sm2公钥。

客户端每次请求生成sm4密钥,仅对敏感信息字段加密,然后放入待签名字符串中一起进行sm3摘要,再使用sm2公钥对摘要进行加密,再使用sm2公钥对sm4密钥加密放入请求头。后端先使用sm2私钥对sm4密钥解密,再使用sm2私钥对签名字段解密得出摘要,然后拼接参数进行sm3摘要,最后和前端传来的sm3摘要做对比。

此方案对比方案1多进行了一次sm2,势必会占用一些性能资源。

/**** @param data 请求参数* @param encryptKey sm4密钥*/
export const apiSign = (data: any, encryptKey: string) => {const appId: string = "appId";const publicKey: string = "sm2公钥";const nonce: string = randomStr(32, true);const timestamp: string = new Date().getTime().toString();const message: string = JSON.stringify(data);const signStr: string = `appId=${appId}&nonce=${nonce}&timestamp=${timestamp}&message=${message}&encryptKey=${encryptKey}`;const sm3Str = SmUtils.sm3(signStr);const signature = SmUtils.sm2Encrypt(sm3Str, publicKey);const encryptKeyResult = SmUtils.sm2Encrypt(encryptKey, publicKey);const header: Map<string, string> = new Map();header.set("appId", appId);header.set("nonce", nonce);header.set("timestamp", timestamp);header.set("signature", signature);header.set("encryptKey", encryptKeyResult);return header;
};

响应的时候后端使用前端传来的sm4密钥加密敏感信息字段,然后签名方式同请求时一致,只是不需要再传回来sm4密文了,前端还是用当前请求生成的sm4密钥解密敏感信息字段。

方案3:

客户端保存appId、sm2公钥 、sm4密钥。

使用固定sm4密钥,仅对敏感信息字段加密,前后端各自保存,不需要一起进行sm3摘要,也不需要放入请求头。拼接带签名字符串进行sm3摘要,再使用sm2公钥对摘要进行加密。后端使用sm2私钥对签名字段解密得出摘要,拼接参数进行sm3摘要,最后和前端传来的sm3摘要做对比。

此方案的缺点是使用固定sm4密钥,安全性不如每次生成。

/**** @param data 请求参数*/
export const apiSign = (data: any) => {const appId: string = "appId";const publicKey: string = "sm2公钥";const nonce: string = randomStr(32, true);const timestamp: string = new Date().getTime().toString();const message: string = JSON.stringify(data);const signStr: string = `appId=${appId}&nonce=${nonce}&timestamp=${timestamp}&message=${message}`;const sm3Str = SmUtils.sm3(signStr);const signature = SmUtils.sm2Encrypt(sm3Str, publicKey);const header: Map<string, string> = new Map();header.set("appId", appId);header.set("nonce", nonce);header.set("timestamp", timestamp);header.set("signature", signature);return header;
};

响应的时候后端使用固定sm4密钥加密敏感信息字段,然后签名方式同请求时一致。

这三种方案我倾向于方案2,但方案2使用了2次sm2,对性能有一定的影响。也希望和各位大佬讨论一下,是否有更合适的方案?

http://www.dtcms.com/a/288666.html

相关文章:

  • Web开发 04
  • 【新手向】PyTorch常用Tensor shape变换方法
  • 零基础学习性能测试第二章-linux/jvm/mysql等数据收集环境搭建
  • Golang基础语法-数据类型
  • Go语言--语法基础6--基本数据类型--map类型
  • Java学习第六十部分——JVM
  • An End-to-End Attention-Based Approach for Learning on Graphs NC 2025
  • 04 51单片机之数码管显示
  • Shell脚本-uniq工具
  • 两个路由器通过不同的网段互联
  • 从TPACK到TPACK - AI:人工智能时代教师知识框架的重构与验证
  • EPLAN 电气制图(十): 继电器控制回路绘制(下)放料、放灰
  • 基于单片机的IC卡门禁系统设计
  • 最大子数组和问题-详解Kadane算法
  • 每日一题7.20
  • OSS文件上传(一):简单上传
  • feignClient 调用详细流程
  • Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
  • 判断一个数是否为质数方法
  • VSCode使用Jupyter完整指南配置机器学习环境
  • c#:TCP服务端管理类
  • 正点原子stm32F407学习笔记10——输入捕获实验
  • 2025 年科技革命时刻表:四大关键节点将如何重塑未来?
  • 内网后渗透攻击过程(实验环境)--3、横向攻击
  • SQL 调优第一步:EXPLAIN 关键字全解析
  • 【已解决】GitHub SSH 连接失败解决方案:Permission Denied (publickey) 错误修复指南
  • [Linux]进程 / PID
  • 30天打牢数模基础-决策树讲解
  • Linux入门篇学习——NFS 服务器的搭建和使用和开发板固件烧录
  • Spring Boot 第一天知识汇总