前端实现方式1:此方法采用16位key可实现请求,也需使用方法2的md5Hex.js文件,最后需要将结果base64转换hex。
可参考的axios: 实际使用时还将再对少数细节做一下优化,比如requestPath参数,或一些方法封装。
测试函数执行耗时获取微秒级时间工具:
https://www.npmjs.com/package/microseconds
npm地址:https://www.npmjs.com/package/gm-crypt
const SM4 = require('gm-crypt').sm4
import { md5 } from '@/util/md5Hex'
取秒时间戳:秒级
function timest() {
var tmp = Date.parse(new Date()).toString();
tmp = tmp.substr(0,10);
// console.log(tmp, '123')
return tmp;
}
base64转换hex的方法:
function base64toHEX(base64) {
var raw = atob(base64);
var HEX = '';
for (var i = 0; i < raw.length; i++ ) {
var _hex = raw.charCodeAt(i).toString(16)
HEX += (_hex.length==2?_hex:'0'+_hex);
}
return HEX.toUpperCase();
}
获取uuid:
function generateUUID() { var d = new Date().getTime();
if(window.performance && typeof window.performance.now ===
“function”){
d += performance.now(); //use high-precision timer if available } var uuid = ‘xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx’.replace(/[xy]/g,
function© {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c==‘x’ ? r : (r&0x3|0x8)).toString(16); }); return uuid; }
请求头处理:
let deviceId = ''
let requestTime = timest()
let requestPath = 'eduAuthTestPath' // 该路径暂时固定为测试接口路径,后续当替换为获取的需验证的具体接口
let requestId = deviceId + '-' + requestTime + '-' + generateUUID()
let requestContent = encrypt(config.data, config.url)
let content = requestId + requestTime + requestContent + requestPath
console.log(content, 'smContent')
let keyOne = md5(content, 32).toUpperCase()
console.log(keyOne, '333')
let smKey = md5('www.scedu.tech' + keyOne, 16).toUpperCase()
// let smKey = md5('www.scedu.tech' + content, 32).toUpperCase()
console.log(smKey, 'smKey')
let sm4Config = {
// encrypt/decypt main key; cannot be omitted
key: smKey,
// optional; can be 'cbc' or 'ecb'
mode: 'ecb', // default
// optional; when use cbc mode, it's necessary
iv: null, // default is null
// optional: this is the cipher data's type; Can be 'base64' or 'text'
cipherType: 'base64' // default is base64
}
let sm4 = new SM4(sm4Config)
// let encryptHex = SM4.encrypt(keyOne, smKey).toUpperCase()
let encryptHex = base64toHEX(sm4.encrypt(keyOne)).toUpperCase()
console.log(encryptHex, 'smEncryptHex')
// let decryptHex = SM4.decrypt(encryptHex, smKey).toUpperCase()
// console.log(decryptHex, 'decryptHex')
let smObj = {
requestTime: requestTime,
requestId: requestId,
auth: encryptHex,
requestPath: requestPath,
}
config.headers["eduRequestAuth"] = JSON.stringify(smObj)
config.data = {
// data: encrypt(config.data, config.url)
data: requestContent
};
对接口response响应进行验证:
// 如果具有签名edurequestauth
if (res.headers['edurequestauth']) {
// 请求头
const reqPath = getStore({ name: 'eduRequestAuth_contrast'})
console.log(reqPath, 'reqPath')
// 响应头
let rspPath = JSON.parse(cloneDeep(res.headers['edurequestauth']))
console.log(rspPath, 'rspPath');
// console.log(md5(reqPath.requestId, 16))
// console.log(cloneDeep(res.headers['edurequestauth']), JSON.stringify(reqPath))
// console.log(timest(), 'ssss');
let timeInterval = timest() - rspPath.responseTime
console.log(timeInterval, 'timeInterval');
let clientReqContent = ''
if (status === 1000) {
clientReqContent = rspPath.requestId + rspPath.responseTime + res.data.result + rspPath.responsePath
}
if (md5(reqPath.requestId, 16) !== rspPath.responsePath) {
// 验证requestId
return {
data : {
status: 7001,
msg: 'requestId不一致,可能存在欺诈行为。'
}
}
} else if (timeInterval >= 20) {
// 验证请求返回间隔时间
return {
data : {
status: 7002,
msg: '时间错误 请调节本地时间和网络一致。'
}
}
} else if (clientReqContent && !decodeVerify(clientReqContent, rspPath.auth)) {
// 验证结果是否和响应签名一致
return {
data : {
status: 7003,
msg: '验签失败 服务器返回内容不可信。'
}
}
}
}
解密验签方法:
// 解密验证
function decodeVerify(clientReqContent, auth) {
let keyOne = md5(clientReqContent, 32).toUpperCase()
console.log(keyOne, '333')
let smKey = md5('www.scedu.tech' + keyOne, 16).toUpperCase()
console.log(smKey, 'smKey')
let sm4Config = {
// encrypt/decypt main key; cannot be omitted
key: smKey,
// optional; can be 'cbc' or 'ecb'
mode: 'ecb', // default
// optional; when use cbc mode, it's necessary
iv: null, // default is null
// optional: this is the cipher data's type; Can be 'base64' or 'text'
cipherType: 'base64' // default is base64
}
let sm4 = new SM4(sm4Config)
let encryptHex = base64toHEX(sm4.encrypt(keyOne)).toUpperCase()
console.log(encryptHex, 'authEncrypt');
// console.log(encryptHex === auth, 'xxx');
return encryptHex === auth;
}
前端实现方式 2:现阶段目前只发起了加密请求,但后端没有使用32位的密钥,故还未进行后续验证。
前端实现可直接参考npm文档或者git
但目前都采用32位md5加密才能准确还原。
npm 地址:
https://www.npmjs.com/package/gm-crypto
npm的圈红的这两个base64编码根据我们使用要求可以换成hex编码
java实现:
后端依赖:
org.bouncycastle
bcprov-jdk15on
1.62
注意: 以下代码位本地js引用,未使用npm包方式。如果使用npm包,则按npm文档的方式来使用。
本地sm4文件:
md5转换文件:
现阶段前端对请求头的加密,axios发起请求之前添加。deviceId 前端没有所以现在为空。
import SM4 from ‘@/util/smFour’
import { md5 } from ‘@/util/md5Hex’
let deviceId = ‘’
let requestTime = timest()
let requestPath = ‘eduAuthTestPath’ // 该路径暂时固定为测试接口路径,后续当替换为获取的需验证的具体接口
let requestId = deviceId + ‘-’ + requestTime + ‘-’ + generateUUID()
let requestContent = encrypt(config.data, config.url)
let content = requestId + requestTime + requestContent + requestPath
console.log(content, ‘smContent’)
let keyOne = md5(content, 32).toUpperCase()
console.log(keyOne, ‘333’)
// let smKey = md5(‘www.scedu.tech’ + keyOne, 16).toUpperCase()
let smKey = md5(‘www.scedu.tech’ + content, 32).toUpperCase()
console.log(smKey, ‘smKey’)
let encryptHex = SM4.encrypt(keyOne, smKey).toUpperCase()
console.log(encryptHex, ‘smEncryptHex’)
let decryptHex = SM4.decrypt(encryptHex, smKey).toUpperCase()
console.log(decryptHex, ‘decryptHex’)
let smObj = {
requestTime: requestTime,
requestId: requestId,
auth: encryptHex,
requestPath: requestPath,
}
config.headers[“eduRequestAuth”] = JSON.stringify(smObj)
config.data = {
data: requestContent
};