taro-vue2 如何使用国密加解密

发布于:2025-03-20 ⋅ 阅读:(23) ⋅ 点赞:(0)

taro-vue2 如何使用国密加解密

在这里插入图片描述
在这里插入图片描述

return unescape(encodeURIComponent(str)).split("").map(val => val.charCodeAt());
​
return decodeURIComponent(escape(String.fromCharCode(...strBuffer)));
const sm2 = require('miniprogram-sm-crypto').sm2
const SM4 = require("gm-crypt").sm4;

以下是需要引入的文件

修改crypt.js源码

在这里插入图片描述

'use strict'

const base64js = require('base64-js')

class Crypt {
  /**
   * Converts a JS string to an UTF-8 uint8array.
   *
   * @static
   * @param {String} str 16-bit unicode string.
   * @return {Uint8Array} UTF-8 Uint8Array.
   * @memberof Crypt
   */
  static stringToArrayBufferInUtf8 (str) {
    // if not browser env, then require node.js's util. otherwise just use window's
    const TextEncoder = (typeof window === 'undefined') ? require('util').TextEncoder : window.TextEncoder
    // always utf-8
    // let encoder = new TextEncoder()
    // return encoder.encode(str)
    return unescape(encodeURIComponent(str)).split("").map(val => val.charCodeAt());
  }

  /**
   * Converts an UTF-8 uint8array to a JS string.
   *
   * @static
   * @param {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @return {String} 16-bit unicode string.
   * @memberof Crypt
   */
  static utf8ArrayBufferToString (strBuffer) {
    // if not browser env, then require node.js's util. otherwise just use window's
    const TextDecoder = (typeof window === 'undefined') ? require('util').TextDecoder : window.TextDecoder
    // let decoder = new TextDecoder('utf-8')
    // return decoder.decode(strBuffer)
    return decodeURIComponent(escape(String.fromCharCode(...strBuffer)));
  }

  /**
   * crypt a utf8 byteArray to base64 string
   *
   * @static
   * @param {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @returns {String} base64 str
   * @memberof Crypt
   */
  static arrayBufferToBase64 (strBuffer) {
    return base64js.fromByteArray(strBuffer)
  }

  /**
   * crypt base64 stringa to utf8 byteArray
   *
   * @static
   * @param {String} base64 str
   * @returns {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @memberof Crypt
   */
  static base64ToArrayBuffer (base64) {
    return base64js.toByteArray(base64)
  }
}

module.exports = Crypt

request.js

import Taro from "@tarojs/taro";
import axios from "taro-axios";
import { commInHandle } from "./cryptography";
import handleReqData from "./handleReqData";
import { BASE_API_URL } from "./config";
import {
  // baseURL,
  contentType,
  requestTimeout,
  handleFailCode,
  successCode,
  noLoading,
  enc_algorithm, //0国际密 1国密
} from "./config";
import { removeStore } from "./plugins";

let loading;
// 操作正常Code数组
const codeVerificationArray = [...successCode];
const CODE_MESSAGE = {
  200: "服务器成功返回请求数据",
  201: "新建或修改数据成功",
  202: "一个请求已经进入后台排队(异步任务)",
  204: "删除数据成功",
  400: "发出信息有误",
  401: "用户没有权限(令牌失效、用户名、密码错误、登录过期)",
  402: "前端无痛刷新token",
  403: "用户得到授权,但是访问是被禁止的",
  404: "访问资源不存在",
  406: "请求格式不可得",
  410: "请求资源被永久删除,且不会被看到",
  500: "服务器发生错误",
  502: "网关错误",
  503: "服务不可用,服务器暂时过载或维护",
  504: "网关超时",
};
const handleResponseData = ({ data, config, status, statusText }) => {
  // console.log('🚀 ~ handleResponseData ~ data:', data)
  Taro.hideLoading();
  let code = data.result ? data.result : status;
  if (data.result && codeVerificationArray.indexOf(data.result) + 1) code = 200;
  switch (code) {
    case 200:
      // return data.data
      if (enc_algorithm == "1") {
        if (data.resBody) {
          if (handleReqData.verifySign_SM(data)) return data.resBody;
          return Taro.showToast({
            title: "",
            icon: "none",
            duration: 3000,
          });
        } else {
          return data.data;
        }
      } else {
        return data.data;
      }
  }

  // 异常处理
  const errMsg =
    data && data.msg
      ? data.msg
      : CODE_MESSAGE[code]
      ? CODE_MESSAGE[code]
      : statusText;
  Taro.showToast({
    title: errMsg || "请求异常",
    icon: "none",
    duration: 3000,
  });
  const err = new Error(errMsg);
  if (code == 999996) {
    Taro.setStorageSync("user", "");
    Taro.setStorageSync("verifyFlag", "");
    Taro.setStorageSync("loginName", "");
    Taro.setStorageSync("code", "");
    Taro.setStorageSync("openId", "");
    Taro.setStorageSync("cardNo", "");
    Taro.setStorageSync("certNo", "");
    Taro.setStorageSync("name", "");
    Taro.reLaunch({
      url: "/pages/login/login",
    });
  }
  return Promise.reject(err);
};
// const handleResponseData = ({ data, status, statusText }) => {
//   Taro.hideLoading();
//   if (data.key1 && data.data) {
//     data = commInHandle.dncryptedByDES(
//       commInHandle.decryptData(data.key1),
//       data.data
//     );
//   }
//   let code = data && data.result ? data.result : status;
//   if (code != "900099") {
//     if (data && data.result && codeVerificationArray.indexOf(data.result) + 1)
//       code = 200;
//     // console.log("成功返回参数", data);
//     if (code === 200) return data;

//     // console.log(code);

//     // 异常处理
//     const errMsg =
//       data && data.msg
//         ? data.msg
//         : CODE_MESSAGE[code]
//         ? CODE_MESSAGE[code]
//         : statusText;
//     Taro.showToast({
//       title: errMsg || "请求异常",
//       icon: "none",
//       duration: 3000
//     });
//     const err = new Error(errMsg);
//     if (code == 999996) {
//       Taro.setStorageSync("user", "");
//       Taro.setStorageSync("verifyFlag", "");
//       Taro.setStorageSync("loginName", "");
//       Taro.setStorageSync("code", "");
//       Taro.setStorageSync("openId", "");
//       Taro.setStorageSync("cardNo", "");
//       Taro.setStorageSync("certNo", "");
//       Taro.setStorageSync("name", "");
//       Taro.reLaunch({
//         url: "/pages/login/login"
//       });
//     }
//     return Promise.reject(err);
//   } else {
//     return data;
//   }
// };

/**
 * @description axios初始化
 */
const instance = axios.create({
  baseURL: BASE_API_URL,
  timeout: requestTimeout,
  headers: {
    "Content-Type": contentType,
  },
});
console.log(BASE_API_URL);

/**
 * @description axios请求拦截器
 */
instance.interceptors.request.use(
  async (config) => {
    let isLoading = noLoading.includes(config.url);
    if (!isLoading) {
      Taro.showLoading({ title: "加载中" });
    }
    if (enc_algorithm == "1") {
      console.log("sm2");
      console.log(config);
      const data = await handleReqData._encryptAndSign_SM(config, true);
      config.data = data;
    } else {
      const data = commInHandle.sign(commInHandle.encrypt(config.data));
      config.data = data;
    }

    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

/**
 * @description axios响应拦截器
 */
instance.interceptors.response.use(
  (response) => handleResponseData(handleReqData.decryptSM4(response)),
  (error) => {
    const { response } = error;
    if (response === undefined) {
      Taro.showToast({
        title: "网络连接失败",
        icon: "none",
        duration: 3000,
      });
      return {};
    } else return handleResponseData(handleReqData.decryptSM4(response));
  }
);

export default instance;

handleReqData.js

import { isArray, isJson, dateFormat } from "./plugins";
// import JSEncrypt from "jsencrypt";
// import jsrsasign from "jsrsasign";
import CryptoJS from "crypto-js";
// import sha1 from "sha1";
// import md5 from "md5";
import sm4Util from "./sm4Util";
import sm2Util from "./sm2Util";
export default {
  data: {
    publicKey:
      "xxxxxxxxxxxxxxxxxxxxx",
    privateKey:
      "xxxxxxxxxxxxxxxxxxxxxxxxx",
    encryptKeys: [
    
    ], // 需要加密的key
    salt: "xxxxxxxxxxx",
    noSignKeys: [
     
    ],
  },

  /**
   * @description SM加密签名
   * @param {*} jsonData
   * @return {*}
   */
  async _encryptAndSign_SM(configData, isEncrypt) {
    let jsonData = configData.data;
    let data = filterReqBody(jsonData);
    //加密
    const rand = randomWord(true, 16, 16).toUpperCase();
    // const key = sm2Util.encrypt(rand)
    const key = sm2Util.encrypt("xxxxxxxxxxxxx");

    // const reqHeader = {
    //   channel: sessionStorage.getItem('channel'), //渠道
    //   appVerNo: '1.0.0', //版本号
    //   timeStamp: new Date().getTime(), //时间戳
    //   ranNum: randomWord(true, 8, 8), //随机值,用于防重放验证,可随机生成
    //   decKey: key, //用于3DES加密的key,并且要传给接口
    // }

    // 公共参数
    const commParameters = {
      channel: "05",
      term_sys: 2,
      model: "-1",
      term_sys_ver: "-1",
      root: "1",
      key1: "1",
      term_id: "0",
      app_ver_no: "1.0.0",
      // 时间戳:yyyy-MM-dd HH:mm:ss, 需要加上时间矫正值
      time: dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"),
      decKey: key, //用于3DES加密的key,并且要传给接口
    };
    // if (process.env.VUE_APP_FTP_USE_LOCAL_URL == 'true') {
    //     commParameters.domainUrl = window.location.origin
    // }

    console.log("data:", data);
    if (isEncrypt) {
      this.data.encryptKeys.map((item) => {
        if (data[item])
          // data[item] = sm4Util.encryptHex(data[item].toString(), rand)
          data[item] = sm4Util.encryptHex(data[item].toString());
        for (let param in commParameters) {
          if (item === param) {
            commParameters[param] = sm4Util.encryptHex(
              commParameters[param].toString()
              // rand
            );
          }
        }
      });
    }
    // console.log(data);
    // const reqBody = data
    //签名
    data = { ...commParameters, ...data };
    // 去除空值
    data = filterReqBody(data);

    let signStr = "";
    //2021-08-16,pengm,增加安全加密签名开关,配合性能测试使用
    if (isEncrypt) {
      data = filterNoSignDataType(data);
      let keyArr = Object.keys(data).sort();
      // keyArr = keyArr.filter((item) => {
      //   if (!this.data.noSignKeys.includes(item)) {
      //     return item
      //   }
      // })
      // key集合
      data["key"] = keyArr.join(",");
      keyArr.map((item) => {
        signStr += data[item].toString();
      });
      data["sign"] = sm2Util.getSign(signStr);
      // incSign = signStr
    }
    return data;
  },
  /**
   * @description 解密2.0版本
   * @param {*} jsonData
   * @return {*}
   */
  _decrypt_v2(jsonData) {
    const { decryptKeys, privateKey } = this.data;
    decryptKeys.forEach((item) => {
      if (jsonData[item])
        jsonData[item] = decryptByDES(privateKey, jsonData[item].toString());
    });
    return jsonData;
  },

  /**
   * @description SM验签
   * @param {*} jsonData
   * @return {*}
   */
  verifySign_SM(jsonData) {
    const sign = jsonData.incSign;
    delete jsonData.resHeader.decKey;
    let data = { ...jsonData.resHeader, ...jsonData.resBody };
    data = filterReqBody(data);
    data = filterNoSignDataType(data);
    let keys = Object.keys(data).sort();
    let valueStr = "";
    keys = keys.filter((item) => {
      if (!this.data.noSignKeys.includes(item)) {
        return item;
      }
    });
    keys.map((item) => {
      valueStr += data[item].toString();
    });
    // console.log('验签', valueStr)
    let isVerify = sm2Util.verifySign(valueStr, sign);
    // console.log(isVerify)
    return isVerify;
  },
  /**
   * @description SM4解密
   * @param {*} key 密钥
   * @param {*} message 入参值
   * @return {*}
   */
  decryptSM4(jsonData) {
    if (jsonData.data.result == 0) {
      jsonData.data.data = JSON.parse(sm4Util.decryptHex(jsonData.data.data));
      console.log(jsonData.config.url + "解密:", jsonData.data.data);
    }
    return jsonData;
  },
  encryptByDES(message) {
    const crypto_key = CryptoJS.enc.Utf8.parse(
      "xxxxxxxxxxxxxx"
    );
    const crypto_iv = CryptoJS.enc.Utf8.parse("xxxxxx");
    const encode_str = CryptoJS.TripleDES.encrypt(message, crypto_key, {
      iv: crypto_iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7,
    });
    return encode_str.toString();
  },
  decryptByDES(message) {
    const crypto_key = CryptoJS.enc.Utf8.parse(
      "xxxxxxxxxxxxxxx"
    );
    const crypto_iv = CryptoJS.enc.Utf8.parse("xxxxxx");
    console.log("🚀 ~ decryptByDES ~ crypto_iv:", crypto_iv);
    const decrypt_str = CryptoJS.TripleDES.decrypt(message, crypto_key, {
      iv: crypto_iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7,
    });
    console.log(
      "🚀 ~ decryptByDES ~ decrypt_str:",
      message,
      ">>>>>>",
      decrypt_str
    );
    return decrypt_str.toString(CryptoJS.enc.Utf8);
  },
};

/**
 * description 过滤空值
 * param {*} jsonData
 * return {*}
 */
function filterReqBody(jsonData) {
  for (let param in jsonData) {
    if (!jsonData[param] && typeof jsonData[param] !== "number") {
      delete jsonData[param];
    }
  }
  return jsonData;
}

/**
 * description 过滤不参与签名的数据类型(array,json)
 * param {*} jsonData
 * return {*}
 */
function filterNoSignDataType(jsonData) {
  for (let param in jsonData) {
    if (isArray(jsonData[param]) || isJson(jsonData[param])) {
      delete jsonData[param];
    }
  }
  return jsonData;
}

/**
 * @description 产生任意长度随机字母数字组合
 * @param {*} randomFlag 是否任意长度
 * @param {*} min 任意长度最小位[固定位数]
 * @param {*} max 任意长度最大位
 * @return {*}
 */
function randomWord(randomFlag, min, max) {
  let str = "",
    range = min,
    arr = [
      "0",
      "1",
      "2",
      "3",
      "4",
      "5",
      "6",
      "7",
      "8",
      "9",
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
      "g",
      "h",
      "i",
      "j",
      "k",
      "l",
      "m",
      "n",
      "o",
      "p",
      "q",
      "r",
      "s",
      "t",
      "u",
      "v",
      "w",
      "x",
      "y",
      "z",
      "A",
      "B",
      "C",
      "D",
      "E",
      "F",
      "G",
      "H",
      "I",
      "J",
      "K",
      "L",
      "M",
      "N",
      "O",
      "P",
      "Q",
      "R",
      "S",
      "T",
      "U",
      "V",
      "W",
      "X",
      "Y",
      "Z",
    ];
  if (randomFlag) {
    range = Math.round(Math.random() * (max - min)) + min;
  }
  for (let i = 0; i < range; i++) {
    let pos = Math.round(Math.random() * (arr.length - 1));
    str += arr[pos];
  }
  return str;
}

plugins.js

/*
 * @Descripttion: 常用js方法 封装
 * @version:
 * @Author: sang junke
 * @Date: 2021-03-25 11:28:41
 * @LastEditors: zhouyx
 * @LastEditTime: 2022-05-27 15:45:53
 */

/**
 * @name: setLocalStore
 * @test: test font
 * @msg: localStorage 存值
 * @param {*} name key
 * @param {*} content value
 * @return {*}
 */
import Taro from "@tarojs/taro";
export const setLocalStore = (name, content) => {
  if (!name) return;
  if (typeof content !== "string") {
    content = JSON.stringify(content);
  }
  window.localStorage.setItem(name, content);
};
/**
 * @name: getLocalStore
 * @test: test font
 * @msg: localStorage 取值
 * @param {*} name key
 * @return {*}
 */
export const getLocalStore = (name) => {
  if (!name) return;
  return window.localStorage.getItem(name);
};
/**
 * @name: removeStore
 * @test: test font
 * @msg: 删除 localStorage
 * @param {*} name key
 * @return {*}
 */
export const removeLocalStore = (value) => {
  if (!value) return;
  if (typeof value == "string") {
    window.localStorage.removeItem(value);
  } else if (isArray(value)) {
    for (var item of value) {
      window.localStorage.removeItem(item);
    }
  }
};

/**
 * @name: setSessionStore
 * @test: test font
 * @msg:sessionStorage 存储
 * @param {*} name key
 * @param {*} content value
 * @return {*}
 */
export const setSessionStore = (name, content) => {
  if (!name) return;
  if (typeof content !== "string") {
    content = JSON.stringify(content);
  }
  window.sessionStorage.setItem(name, content);
};
/**
 * @name: getSessionStore
 * @test: test font
 * @msg: sessionStorage 取值
 * @param {*} name key
 * @return {*}
 */
export const getSessionStore = (name) => {
  if (!name) return;
  return window.sessionStorage.getItem(name);
};
/**
 * @name:removeSessionStore
 * @test: test font
 * @msg:sessionStorage 删除 单个||多个
 * @param {*} value
 * @return {*}
 */
export const removeSessionStore = (value) => {
  if (!value) return;
  if (typeof value == "string") {
    window.sessionStorage.removeItem(value);
  } else if (isArray(value)) {
    for (var item of value) {
      window.sessionStorage.removeItem(item);
    }
  }
};
/**
 * @name: getUrlString
 * @test: test font
 * @msg: 获取url 参数
 * @param {*} str
 * @return {*} value
 */
export const getUrlString = (str) => {
  let LocString = String(window.document.location.href);
  let rs = new RegExp("(^|)" + str + "=([^&]*)(&|$)", "gi").exec(LocString),
    tmp;
  if ((tmp = rs)) return decodeURI(tmp[2]);
  return null;
};
/**
 * @name: desCertNo
 * @test: test font
 * @msg: 18位身份证号脱敏处理 显示前三后四
 * @param {*} str
 * @return {*}
 */
export const desCertNo = (str) => {
  if (!str) return;
  let id = str.substr(0, 3) + "***********" + str.substr(+str.length - 4, 4);
  return id;
};
/**
 * @name: desmobile
 * @test: test font
 * @msg: 手机号脱敏处理 前三后四
 * @param {*} str
 * @return {*}
 */
export const desmobile = (str) => {
  let id = str.substr(0, 3) + "****" + str.substr(+str.length - 4, 4);
  return id;
};
/**
 * @name: getMoney
 * @test: test font
 * @msg: 处理金额 保留后两位小数 分转元
 * @param {*} str
 * @return {*}
 */
export const getMoney = (str) => {
  if (!str) return;
  let money = (Number(str) / 100).toFixed(2);
  return money;
};
// 卡号脱敏
export const cardnos = (str) => {
  if (!str) return;
  let id = "**************" + str.substr(+str.length - 4, 4);
  return id;
};
export const desName = (str) => {
  if (!str) return;
  // console.log(str.substr(1, 1));
  let id = "*" + str.substr(1);
  return id;
};

/**
 * @name: formatTime
 * @test: test font
 * @msg: 格式化日期函数
 * @param {*} timeType yyyy-MM-dd HH:mm:ss || yyyy/MM/dd HH:mm:ss
 * @param {*} time Date() 标准时间
 * @return {*}
 */
export const formatTime = (
  time = new Date(),
  timeType = "yyyy-MM-dd HH:mm:ss"
) => {
  var t = new Date(time);
  var format = timeType;
  var tf = function (i) {
    return (i < 10 ? "0" : "") + i;
  };
  return format.replace(/yyyy|MM|dd|HH|mm|ss/g, function (a) {
    let str = "";
    switch (a) {
      case "yyyy":
        str = tf(t.getFullYear());
        break;
      case "MM":
        str = tf(t.getMonth() + 1);
        break;
      case "mm":
        str = tf(t.getMinutes());
        break;
      case "dd":
        str = tf(t.getDate());
        break;
      case "HH":
        str = tf(t.getHours());
        break;
      case "ss":
        str = tf(t.getSeconds());
        break;
    }
    return str;
  });
};
/**
 * @name: mobileValidate
 * @test: test font
 * @msg: 手机号格式校验
 * @param {*} mobile
 * @return {*}
 */
export const mobileValidate = (mobile) => {
  const newReg =
    /^(((13[0-9]{1})|(14[0,1,4,5,6,7,8,9]{1})|(15[0,1,2,3,5,6,7,8,9]{1})|(16[2,5,6,7]{1})|(17[0-8]{1})|(18[0-9]{1})|(19[0,1,2,3,5,6,7,8,9]{1}))+\d{8})$/;
  if (!mobile) return false;
  if (newReg.test(mobile)) return true;
  return false;
};
/**
 * @name: emailValidate
 * @test: test font
 * @msg: 邮箱格式校验
 * @param {*} emails
 * @return {*}
 */
export const emailValidate = (emails) => {
  const newReg = /^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$/;
  if (!emails) return false;
  if (newReg.test(emails)) return true;
  return false;
};
/**
 * @name: nameValidate
 * @test: test font
 * @msg: 姓名基本检测 特殊符号过滤
 * @param {*} name
 * @return {*}
 */
export const nameValidate = (name) => {
  const newReg = new RegExp(
    "[`~!@#%$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()《》——|{}【】‘;:”“'。,、?]"
  );
  if (!name) return false;
  if (newReg.test(name)) return false;
  return true;
};
/**
 * @name: certNoValidate
 * @test: test font
 * @msg: 身份证号格式校验
 * @param {*} certNo
 * @return {*}
 */
export const certNoValidate = (certNo) => {
  if (!certNo) return false;
  // 加权因子
  const weight_factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  // 校验码
  const check_code = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"];
  let code = certNo + "";
  let seventeen = code.substring(0, 17);
  // 判断最后一位校验码是否正确
  let arr = seventeen.split("");
  let len = arr.length;
  let num = 0;
  for (let i = 0; i < len; i++) {
    num = num + arr[i] * weight_factor[i];
  }
  // 获取余数
  let resisue = num % 11;
  let last_no = check_code[resisue];
  // 格式的正则
  // 正则思路
  /*
      第一位不可能是0
      第二位到第六位可以是0-9
      第七位到第十位是年份,所以七八位为19或者20
      十一位和十二位是月份,这两位是01-12之间的数值
      十三位和十四位是日期,是从01-31之间的数值
      十五,十六,十七都是数字0-9
      十八位可能是数字0-9,也可能是X
      */
  const newReg =
    /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/;
  // 判断格式是否正确
  let format = newReg.test(certNo);
  // 返回验证结果,校验码和格式同时正确才算是合法的身份证号码
  if (last_no && format) return true;
  return false;
};

/**
 * @name: isArray
 * @test: test font
 * @msg: 检测是否为数组
 * @param {*} arr array
 * @return {*} isArray
 */
export const isArray = (arr) => {
  if (!Array.isArray) {
    Array.isArray = function (arg) {
      return Object.prototype.toString.call(arg) === "[object Array]";
    };
  }
  return Array.isArray(arr);
};
export const trim = (str) => {
  return str.replace(/(^\s*)|(\s*$)/g, "");
};
/**
 * 渠道设备
 */
export const browser = {
  versions: (function () {
    const u = navigator.userAgent;
    return {
      ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
      android: u.indexOf("Android") > -1 || u.indexOf("Linux") > -1, //android终端或者uc浏览器
    };
  })(),
};
/**
 * Created by Wandergis on 2015/7/8.
 * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
 */

//定义一些常量
var x_PI = (3.14159265358979324 * 3000.0) / 180.0;
var PI = 3.1415926535897932384626;
var a = 6378245.0;
var ee = 0.00669342162296594323;

/**
 * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
 * 即 百度 转 谷歌、高德
 * @param bd_lon
 * @param bd_lat
 * @returns {*[]}
 */
export function bd09togcj02(bd_lon, bd_lat) {
  var x_pi = (3.14159265358979324 * 3000.0) / 180.0;
  var x = bd_lon - 0.0065;
  var y = bd_lat - 0.006;
  var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
  var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
  var gg_lng = z * Math.cos(theta);
  var gg_lat = z * Math.sin(theta);
  return [gg_lng, gg_lat];
}

/**
 * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
 * 即谷歌、高德 转 百度
 * @param lng
 * @param lat
 * @returns {*[]}
 */
export function gcj02tobd09(lng, lat) {
  var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
  var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
  var bd_lng = z * Math.cos(theta) + 0.0065;
  var bd_lat = z * Math.sin(theta) + 0.006;
  return [bd_lng, bd_lat];
}

/**
 * WGS84转GCj02
 * @param lng
 * @param lat
 * @returns {*[]}
 */
export function wgs84togcj02(lng, lat) {
  if (out_of_china(lng, lat)) {
    return [lng, lat];
  } else {
    var dlat = transformlat(lng - 105.0, lat - 35.0);
    var dlng = transformlng(lng - 105.0, lat - 35.0);
    var radlat = (lat / 180.0) * PI;
    var magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    var sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
    dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
    var mglat = lat + dlat;
    var mglng = lng + dlng;
    return [mglng, mglat];
  }
}

/**
 * GCJ02 转换为 WGS84
 * @param lng
 * @param lat
 * @returns {*[]}
 */
export function gcj02towgs84(lng, lat) {
  if (out_of_china(lng, lat)) {
    return [lng, lat];
  } else {
    var dlat = transformlat(lng - 105.0, lat - 35.0);
    var dlng = transformlng(lng - 105.0, lat - 35.0);
    var radlat = (lat / 180.0) * PI;
    var magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;
    var sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
    dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
    var mglat = lat + dlat;
    var mglng = lng + dlng;
    return [lng * 2 - mglng, lat * 2 - mglat];
  }
}

export function transformlat(lng, lat) {
  var ret =
    -100.0 +
    2.0 * lng +
    3.0 * lat +
    0.2 * lat * lat +
    0.1 * lng * lat +
    0.2 * Math.sqrt(Math.abs(lng));
  ret +=
    ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
      2.0) /
    3.0;
  ret +=
    ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) /
    3.0;
  ret +=
    ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) *
      2.0) /
    3.0;
  return ret;
}

export function transformlng(lng, lat) {
  var ret =
    300.0 +
    lng +
    2.0 * lat +
    0.1 * lng * lng +
    0.1 * lng * lat +
    0.1 * Math.sqrt(Math.abs(lng));
  ret +=
    ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
      2.0) /
    3.0;
  ret +=
    ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) /
    3.0;
  ret +=
    ((150.0 * Math.sin((lng / 12.0) * PI) +
      300.0 * Math.sin((lng / 30.0) * PI)) *
      2.0) /
    3.0;
  return ret;
}

/**
 * 判断是否在国内,不在国内则不做偏移
 * @param lng
 * @param lat
 * @returns {boolean}
 */
export function out_of_china(lng, lat) {
  return (
    lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271 || false
  );
}

// 格式化日期
export const SetFormatTime = (timeType, time = new Date()) => {
  var t = new Date(time);
  var format = timeType; //timeType =>yyyy-MM-dd HH:mm:ss
  var tf = function (i) {
    return (i < 10 ? "0" : "") + i;
  };
  return format.replace(/yyyy|MM|dd|HH|mm|ss/g, function (a) {
    switch (a) {
      case "yyyy":
        return tf(t.getFullYear());
      case "MM":
        return tf(t.getMonth() + 1);
      case "mm":
        return tf(t.getMinutes());
      case "dd":
        return tf(t.getDate());
      case "HH":
        return tf(t.getHours());
      case "ss":
        return tf(t.getSeconds());
    }
  });
};
// 生成用户相关的唯一标识
export const generateUUID = async () => {
  const user = Taro.getStorageSync("user");
  const systemInfo = await Taro.getSystemInfoSync();
  const deviceInfoStr =
    (user.userId || "") +
    "_" +
    systemInfo.model +
    "_" +
    systemInfo.version +
    "_" +
    systemInfo.windowWidth +
    "_" +
    systemInfo.windowHeight;
  console.log("generateUUID == ", deviceInfoStr);
  const md5Hash = CryptoJS.MD5(deviceInfoStr).toString();
  return md5Hash;
};

// Date格式化
export const dateFormat = (
  date = new Date(),
  format = "yyyy-MM-dd HH:mm:ss"
) => {
  // 转成
  if (!date) return "";
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const seconds = date.getSeconds();
  return format
    .replace("yyyy", year)
    .replace("MM", month < 10 ? "0" + month : month)
    .replace("dd", day < 10 ? "0" + day : day)
    .replace("HH", hour < 10 ? "0" + hour : hour)
    .replace("mm", minute < 10 ? "0" + minute : minute)
    .replace("ss", seconds < 10 ? "0" + seconds : seconds);
};
/**
 * @description: 判断是否Json
 * @param {*} value
 * @return {*}
 */
export const isJson = (value) => {
  if (value && typeof value === "object") return true;
  return false;
};

/**
 * 16进制字符转为Base64字符串
 * @param {*} str
 * @returns
 */
export function hexToBase64(str) {
  // 将16进制字符串两个字符一组转换为ASCII码字符,然后转为Base64字符串
  // return btoa(
  //   String.fromCharCode.apply(
  //     null,
  //     str.match(/.{2}/g).map((hexPair) => parseInt(hexPair, 16))
  //   )
  // );
  // // 将16进制字符串转换为Buffer对象
  const buffer = Buffer.from(str, 'hex');
  // 将Buffer对象转换为Base64字符串
  const base64String = buffer.toString('base64');
  return base64String
  // const uint8Array = new Uint8Array(
  //   str.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
  // );
  // return Taro.arrayBufferToBase64(uint8Array);
}

/**
 * Base64字符串16进制字符
 * @param {*} str
 * @returns
 */
export function base64ToHex(str) {
  return Buffer.from(str, "base64").toString("hex");
}

sm2Util.js

const sm2 = require('miniprogram-sm-crypto').sm2
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1
// 后端会生成密钥对(16进制字符串)
// publicKey:公钥 后端提供
// privateKey:私钥 后端提供
const privateKey = xxxxxxxxxxxx

const publicKey =xxxxxxxxxxxxxxx

const gatewayPrivateKey =xxxxxxxxxxxx
export default {
  // 加密
  // value:需要加密的内容
  encrypt(value) {
    // 给后端传值时需要在加密的密文前面加04 ,这样后端才能解密正确不报错
    return sm2.doEncrypt(value, publicKey, cipherMode)
  },

  // 解密
  // value:需要解密的密文
  decrypt(value) {
    // 后端传输过来的密文开头的两个字符通常也为04,因此解密时需要删除
    return sm2.doDecrypt(value, privateKey, cipherMode)
  },

  //签名
  getSign(text) {
    return sm2.doSignature(text, privateKey, {
      hash: true,
      der: true,
    })
    // return hexToBase64(
    //   sm2.doSignature(text, privateKey, {
    //     hash: true,
    //     der: true,
    //   })
    // )
  },
  //验签
  verifySign(text, sign) {
    return sm2.doVerifySignature(text, sign, publicKey, {
      hash: true,
      der: true,
    })
    // return sm2.doVerifySignature(text, base64ToHex(sign), publicKey, {
    //   hash: true,
    //   der: true,
    // })
  },
  //网关签名
  gatewaySign(text) {
    return sm2.doSignature(text, gatewayPrivateKey, {
      hash: true,
      der: true,
    })
    // return hexToBase64(
    //   sm2.doSignature(text, privateKey, {
    //     hash: true,
    //     der: true,
    //   })
    // )
  },
}

sm4Util.js

import { hexToBase64, base64ToHex } from "./plugins";
// const SM4 = require("gm-crypt").sm4;
// const SM4 = require("miniprogram-sm-crypto").sm4;
const SM4 = require("./gm-crypt").sm4
console.log("15 line -> SM4:", SM4);

const pwdKey = "xxxxxxxxx"; //密钥 前后端一致,后端提供
console.log(pwdKey);
//密钥 前后端一致,后端提供
let sm4Config = {
  key: pwdKey,
  mode: "ecb", // 加密的方式有两种,ecb和cbc两种,看后端如何定义的,cbc需要iv参数,ecb不用
  // mode: CryptoJS.mode.ECB, // 加密的方式有两种,ecb和cbc两种,看后端如何定义的,cbc需要iv参数,ecb不用
  iv: "xxxxxxxxxxxxxxxxx", // 初始向量,cbc模式的第二个参数,也需要跟后端配置的一致
  cipherType: "base64",
  // padding: "PKCS7",
  // padding: CryptoJS.pad.Pkcs7,
};
const sm4Util = new SM4(sm4Config);
export default {
  /*
   * 加密工具函数 返回base64字符串
   * @param {String} text 待加密文本
   */
  encryptBase64(text, key) {
    if (key) {
      sm4Config.key = key;
    }
    const sm4Util = new SM4(sm4Config);
    return sm4Util.encrypt(text, pwdKey);
  },

  /*
   * 解密工具函数
   * @param {String} text 待解密密文 base64字符串
   */
  decryptBase64(text, key) {
    if (key) {
      sm4Config.key = key;
    }
    const sm4Util = new SM4(sm4Config);
    return sm4Util.decrypt(text, pwdKey);
  },

  /*
   * 加密工具函数 返回16进制字符串
   * @param {String} text 待加密文本
   */
  encryptHex(text, key) {
    if (key) {
      sm4Config.key = key;
    }
    const sm4Util = new SM4(sm4Config);
    return base64ToHex(sm4Util.encrypt(text, pwdKey));
  },

  /*
   * 解密工具函数
   * @param {String} text 待解密密文 16进制字符串
   */
  decryptHex(hexStr, key) {
    if (key) {
      sm4Config.key = key;
    }
    const sm4Util = new SM4(sm4Config);
    return sm4Util.decrypt(hexToBase64(hexStr), pwdKey);
  },
};