对称加密、非对称加密、小程序sm2、sm4的加密解密

2,697 阅读4分钟

国密介绍

国密算法是国家密码局制定标准的一系列算法,主要有SM1,SM2,SM3,SM4,密钥长度和分组长度均为128位。其中:

  • SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
  • SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
  • SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
  • SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

对称加密和非对称加密

对称加密好比一把钥匙一把锁,开关这把锁都用同一把钥匙,非对称加密有2把钥匙,一把公钥一把私钥。对称加密简单运算速度快,但安全性不如非对称加密。所以,在实际应用中,非对称加密和对称加密总是结合在一起使用。 下面再举一个更形象一点的例子:小美是学校里众所周知的女神,有很多追求者给她写信,如果是对称加密,1000个追求者就有1000把钥匙,小美需要管理1000把钥匙,才能查看追求者的来信,这种方式显然不太现实。这时候就要用到非对称加密,小美公开自己的公钥供追求者去复制,追求者通过公钥加密,小美通过私钥解密,这时候只要保管好自己的私钥就好了。假如小美相中了某个追求者小明,这时小美还可以通过私钥把所回复的内容上锁,小明通过公钥看到小美回复的内容,同样其他持有公钥的追求者也可以看到,这其实可以看作一种证明,女神给小明回信了,这就是数字签名的原理。因为其他追求者都有那把公钥,所以她所回复的内容往往是简单固定的文字或者是回复内容的数学摘要,数字签名的作用不是加密通信,而是证明来源,所以数字签名不需要涉及具体内容。假如女神想要回复一些私密内容该怎么办呢?最简单的方式就是每个追求者给女神附上对称加密的密钥,这样就能确保你们之间的通信只有你们自己知道了。

准备

  • 安装 miniprogram-sm-crypto(从小程序基础库版本 2.2.1及开发者工具 1.02.1808300 开始,小程序支持使用 npm 安装第三方包)

npm install --save miniprogram-sm-crypto

  • 生成miniprogram_npm包 首先要在开发者工具勾选“使用npm模块 ”; 其次要先npm init 初始化一直按回车键; 最后开发者工具选择构建npm; 如果没有生成miniprogram_npm,很有可能是 project.config.json 中"packNpmManually"设为true了,需要把它改成false才会生成miniprogram_npm包

编码

编码之前先说一下整体思路,小程序中单独定义一个js文件写好加密解密函数暴露出去,然后在请求拦截器中引入,请求的时候调用加密函数,获取响应数据的时候用解密函数。

// 对请求响应报文进行加解密的工具库
const sm2 = require('miniprogram-sm-crypto').sm2
const sm4 = require('miniprogram-sm-crypto').sm4

// 此处可下载第三方库直接引用,或者自己定义
function uuid(len,radix){
    ......
}

// 生成一个随机的32位字符串
let key = uuid(32,16)
// 前后台约定一个公钥
let publicKey = '......'
// 密文排列方式 这边选择1:标准排序方式
let cipherMode = 1 // 1-C1C3C2  0-C1C2C3
// 生成sm2加密密钥
let encryptKey = sm2.doEncrypt(key,publicKey,cipherMode)

// 加密请求报文(具体代码需根据前后端约定)
function encrypt(request) {
    let reqData = JSON.stringify(request)
    let reqObj = {}
    reqObj.encryptData = sm4.encrypt(reqData,key)
    reqObj.encryptKey = encryptKey
    return JSON.stringify(reqObj)
}

// 解密响应报文(具体代码需根据前后端约定)
function decrypt(response) {
    let decryptData = sm4.decrypt(response.resData,key)
    return JSON.parse(decryptData)
}

// 暴露出去
module.exports = {
  encrypt:encrypt, // 加密
  decrypt:decrypt // 解密
}