SM4加密算法是中国国家密码管理局定义的一种对称分组加密算法,它广泛应用于无线局域网安全协议等领域。
前言
为什么要对前后端传输的表单数据进行加密处理?主要原因有以下几点:
-
保护隐私和机密性:未经加密的数据在传输过程中容易被黑客、未经授权的第三方监听和截取,导致敏感信息如个人身份、银行账号、商业秘密等泄露,给个人和组织带来严重的安全风险。
-
防止篡改:未加密的数据可以轻易被中间人修改或插入恶意内容。通过加密,数据的真实性和完整性得以保障,任何对数据的非法更改在解密时都能被发现。
-
防止数据泄露风险:在传统的表单提交过程中,数据是以明文的形式发送到服务器的。这意味着在数据传输的过程中,如果存在中间人攻击或者网络被监听,那么敏感数据很容易被窃取。
-
无法保证数据完整性:传统的表单提交是基于HTTP协议的,它使用的是明文传输,不提供数据完整性校验的机制。这就意味着在数据传输过程中,数据可能会被修改、篡改或者替换。
-
增强数据传输的安全性:加密技术在数据安全中的应用非常广泛,主要包括数据传输加密,使用SSL/TLS等协议对数据进行加密传输,可以确保数据在公网上传输时的安全性。
一、前端加密
在前端加密时,加密的内容可能包含了特殊字符或非ASCII
字符。需要先将其转为 UTF-8
或其他标准编码格式的字节数组,再进行 SM4
加密。
<template>
<div>
<a-form :form="form" style="margin-top: 20px">
<a-form-item key="code" v-bind="form" label="编码">
<a-input placeholder="请输入编码" v-decorator="['description']" />
</a-form-item>
</a-form>
</div>
</template>
<script>
import { getSM2Key, getSMS4Key } from '@/utils/util'
import SM4 from '@/utils/sm4'
export default {
data() {
return {
form: this.$form.createForm(this),
}
},
methods: {
submitTableForm() {
const {
form: { validateFields },
} = this
validateFields((errors, values) => {
if (!errors) {
const smKey = getSMS4Key.call(this)
const sm2Key = getSM2Key.call(this, 1)
formData.description = SG_sm2Encrypt(smKey, sm2Key)
let codeBytes = new TextEncoder().encode(formData.code)
formData.description = SM4.encodeByHexSMS4(codeBytes, smKey)
// 调用接口
api(formData)
}
})
},
},
}
</script>
国密工具类方法如下
/**
* 获取国密算法秘钥
* @return String
*/
export function getSMS4Key () {
return SM4.genRandomKey()
}
/**
* 获取参数加密传递时所需的sm2公钥,每次登录都重新生成新的公钥
*/
export function getSM2Key (key = 0) {
let sm2Key
switch (key) {
case 1:
//前台传参时所用密钥,每次登录重新生成
//添加token标记,避免多次
let llKey = `${S2}_${sessionStorage.getItem(ACCESS_TOKEN)}`
sm2Key = Vue.ls.get(llKey) || this.$store.state.user.s2
break
case 2:
//后台传参解密时所需密钥
// eslint-disable-next-line no-case-declarations
const s1 = Vue.ls.get(S1) || store.state.user.s1
sm2Key = Constant.SPRI + s1
break
default:
//用户登录时使用此密钥
sm2Key = Constant.SPUB
break
}
return sm2Key
}
二、后端解密
通过SM2
算法和私钥解密该 decryptKey,获取新的SM4
密钥。使用SM4
解密算法解密前端传来的字段。中文字符在加密之前被转换成字节流,如果加密或解密时使用了不一致的字符编码(如 UTF-8
和 ISO-8859-1
之间的转换),就会出现乱码。将解密后的数据(ASCII 编码的 JSON 格式)转换为字符。修复可能的字符编码问题,确保解密后的内容正确显示。
@PostMapping("/edit")
@LogAnnotation(menuName = MENU_NAME, content = CONTENT, operateType = OperateTypeEnum.UPDATE)
@ApiOperation(value = "修改数据", httpMethod = "POST")
@ApiImplicitParam(name = "obj", value = "实体对象", required = true)
public Result update(@RequestBody @Validated MaterialVO obj) {
String token = this.getRequest().getHeader(RestConstant.X_ACCESS_TOKEN);
String priKey = systemCacheUtil.getTokenValue(token, SystemConfigConstant.PRIKEY);
String sms4Key = systemCacheUtil.getCfgValue(SystemConfigConstant.SECURITY_PROPERTIES, SystemConfigConstant.SMS4);
if (StrUtil.isNotEmpty(obj.getDecryptKey())){
sms4Key = Sm2Util.decodeByPriKey(priKey, obj.getDecryptKey());
}
String decryptedDescription = Sm4Util.decodeByHex(obj.getDescription(), sms4Key);
// 解密后的内容是 JSON 格式,包含 ASCII 值
// 解析解密后的 JSON 字符串
JSONObject jsonObject = new JSONObject(decryptedDescription);
StringBuilder finalDescriptionBuilder = new StringBuilder();
// 遍历 JSON 对象的键
for (int i = 0; i < jsonObject.length(); i++) {
// 获取 ASCII 值
int asciiValue = jsonObject.getInt(String.valueOf(i));
finalDescriptionBuilder.append((char) asciiValue);
}
String finalDescription = finalDescriptionBuilder.toString();
// 确保解码后的字符串按 UTF-8 解码
byte[] decryptedBytes = finalDescription.getBytes(StandardCharsets.ISO_8859_1);
finalDescription = new String(decryptedBytes, StandardCharsets.UTF_8);
obj.setDescription(finalDescription);
obj = materialService.update(obj);
return ResultGenerator.genOkResult(obj);
}
总结
SM4
是一款高效、安全、符合中国法律和行业标准的对称加密算法,适合国内应用,特别是合规性要求较高的场景。但是国际支持较弱,可能在跨国业务和标准化方面存在兼容性问题。