import { hmacSha256 } from "./encrypt/hmacSha256" import * as CryptoJS from "./encrypt/aes.js" /** * 加密解密工具类 * @author 邹勇 */ export class EncryptUtil { /** * 随机生成多位字符串 * @param len 字符串长度 * @returns */ public randomString(len = 32) { let $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz123456789'; let maxPos = $chars.length; let pwd = ''; for (let i = 0; i < len; i++) { pwd += $chars.charAt(Math.floor(Math.random() * maxPos)) } return pwd; } //说明: // 1.如果加密解密涉及到前端和后端,则这里的key要保持和后端的key一致 // 2.AES的算法模式有好几种(ECB,CBC,CFB,OFB),所以也要和后端保持一致 // 3.AES的补码方式有两种(PKS5,PKS7),所以也要和后端保持一致 // 4.AES的密钥长度有三种(128,192,256,默认是128),所以也要和后端保持一致 // 5.AES的加密结果编码方式有两种(base64和十六进制),具体怎么选择由自己定,但是加密和解密的编码方式要统一 //十六位十六进制数作为秘钥 // private public key = fun_aes.CryptoJS.enc.Utf8.parse("3454345434543454"); //十六位十六进制数作为秘钥偏移量 // private public iv = fun_aes.CryptoJS.enc.Utf8.parse('6666666666666666'); /** * 说明: * 1.如果加密解密涉及到前端和后端,则这里的key要保持和后端的key一致 * 2.AES的算法模式有好几种(ECB,CBC,CFB,OFB),所以也要和后端保持一致 * 3.AES的补码方式有两种(PKS5,PKS7),所以也要和后端保持一致 * 4.AES的密钥长度有三种(128,192,256,默认是128),所以也要和后端保持一致 * 5.AES的加密结果编码方式有两种(base64和十六进制),具体怎么选择由自己定,但是加密和解密的编码方式要统一 * 十六位十六进制数作为秘钥 * private public key = fun_aes.CryptoJS.enc.Utf8.parse("3454345434543454"); * 十六位十六进制数作为秘钥偏移量 * private public iv = fun_aes.CryptoJS.enc.Utf8.parse('6666666666666666'); * * aes加密 * @param word 需要加密的字符串 * @param tkey !!!加密key,32位字符串,一般为sessionkey * @param appid 游戏的appid * @param state true 存包长 false /r/n * @returns */ public encrypt(word, tkey, appid, state = true) { let tiv = tkey.substr(0, 16); //明文拼接 let newStr = ''; if (state) { newStr = tiv + word + appid; } else { newStr = tiv + word + '\r\n' + appid; } //加密后 let key = CryptoJS.enc.Utf8.parse(tkey); let iv = CryptoJS.enc.Utf8.parse(tiv); newStr = CryptoJS.enc.Utf8.parse(newStr); let encrypted = CryptoJS.AES.encrypt(newStr, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); encrypted = encrypted.toString(); let mac = hmacSha256.Instance.hex_hmac_sha256(tkey, encrypted); return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(tiv + encrypted + mac)); } /** * aes解密 * @param word 需要加密的字符串 * @param tkey 加密key,一般为sessionkey * @param appid 游戏的appid * @param state true 存包长 false /r/n * @returns */ public decrypt(word, tkey, appid, state = true) { let en = CryptoJS.enc.Base64.parse(word).toString(CryptoJS.enc.Utf8); let newStrOne = en.substr(16); let temp = newStrOne.substr(0, newStrOne.length - 64); let de = temp; let key = CryptoJS.enc.Utf8.parse(tkey); let iv = CryptoJS.enc.Utf8.parse(tkey.substr(0, 16)); let decrypt = CryptoJS.AES.decrypt(de, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); let decryptedStr = CryptoJS.enc.Utf8.stringify(decrypt); decryptedStr = decryptedStr.toString(); let newStrThree = ''; if (state) { newStrThree = decryptedStr.slice(16).replace(new RegExp("(.*)" + appid), '$1'); } else { newStrThree = decryptedStr.slice(16).split('\r\n')[0]; } return newStrThree; } private options = { default_key_size: 2048, default_public_exponent: new Uint8Array([0x01, 0x00, 0x01]) } /** * rsa加密 * @param word 需要加密的字符串 * @param isLong 字符串加长版 * @returns */ public rsaEncrypt(word, isLong: boolean = false) { let jsb = new window['JSEncrypt'](this.options); //公钥 let publicString = '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwz5V8wt525dMS+A9wOd\n' + 'NAjvKgxdyoXYuZSlAJEXLhvJlaXwFCKEsUg/qBBeo4nrRnwaFAjG7ItqbCWR9Ozv\n' + '6oY5tMYQnFFK82XkMGbKm1gG0z9vYKhcNYgHZHi+QKAw4tLLWXWO/QBTGelEXptD\n' + 'azlNmdM+8wHOknTcMuIyjc9Yo5qCoNsCJpA465vnREiBezBbKB3rSw+LmrRcZcFk\n' + 's8hmGtjpabcQ8sCxco7mRzVh6/DomNWfNm7hUP6shrrJirwjQtaS51grlScj8KhN\n' + 'h+kBoiI4Ad0Xz0GHasK5hAMf3sXDsN61r0An8KTfa/nsYSJDwogPIKkeKAsmw9Df\n' + 'EQIDAQAB\n' + '-----END PUBLIC KEY-----' jsb.setPublicKey(publicString); // 加密内容 let encrypted = isLong ? jsb.encryptLong(word) : jsb.encrypt(word); return encrypted; } /** * for php pack( N , length ) * @param len * @returns */ public pack(len: number) { let buf: ArrayBuffer = new ArrayBuffer(4); let dataView = new DataView(buf); dataView.setUint32(0, len); let str2 = this.ab2str(buf); return str2; } /** * * @param array * @returns */ public utf8ArrayToStr(array) { let out, i, len, c; let char2, char3; out = ""; len = array.length; i = 0; while (i < len) { c = array[i++]; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: // 0xxxxxxx out += String.fromCharCode(c); break; case 12: case 13: // 110x xxxx 10xx xxxx char2 = array[i++]; out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); break; case 14: // 1110 xxxx 10xx xxxx 10xx xxxx char2 = array[i++]; char3 = array[i++]; out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); break; } } return out; } /** * UTF-16转UTF-8 * @param s * @returns */ public utf16ToUtf8(s) { if (!s) { return; } let i, code, ret = [], len = s.length; for (i = 0; i < len; i++) { code = s.charCodeAt(i); if (code > 0x0 && code <= 0x7f) { //单字节 //UTF-16 0000 - 007F //UTF-8 0xxxxxxx ret.push(s.charAt(i)); } else if (code >= 0x80 && code <= 0x7ff) { //双字节 //UTF-16 0080 - 07FF //UTF-8 110xxxxx 10xxxxxx ret.push( //110xxxxx String.fromCharCode(0xc0 | ((code >> 6) & 0x1f)), //10xxxxxx String.fromCharCode(0x80 | (code & 0x3f)) ); } else if (code >= 0x800 && code <= 0xffff) { //三字节 //UTF-16 0800 - FFFF //UTF-8 1110xxxx 10xxxxxx 10xxxxxx ret.push( //1110xxxx String.fromCharCode(0xe0 | ((code >> 12) & 0xf)), //10xxxxxx String.fromCharCode(0x80 | ((code >> 6) & 0x3f)), //10xxxxxx String.fromCharCode(0x80 | (code & 0x3f)) ); } } return ret.join(''); } /** * UTF-8转UTF-16 * @param s * @returns */ public utf8ToUtf16(s) { if (!s) { return; } let i, codes, bytes, ret = [], len = s.length; for (i = 0; i < len; i++) { codes = []; codes.push(s.charCodeAt(i)); if (((codes[0] >> 7) & 0xff) == 0x0) { //单字节 0xxxxxxx ret.push(s.charAt(i)); } else if (((codes[0] >> 5) & 0xff) == 0x6) { //双字节 110xxxxx 10xxxxxx codes.push(s.charCodeAt(++i)); bytes = []; bytes.push(codes[0] & 0x1f); bytes.push(codes[1] & 0x3f); ret.push(String.fromCharCode((bytes[0] << 6) | bytes[1])); } else if (((codes[0] >> 4) & 0xff) == 0xe) { //三字节 1110xxxx 10xxxxxx 10xxxxxx codes.push(s.charCodeAt(++i)); codes.push(s.charCodeAt(++i)); bytes = []; bytes.push((codes[0] << 4) | ((codes[1] >> 2) & 0xf)); bytes.push(((codes[1] & 0x3) << 6) | (codes[2] & 0x3f)); ret.push(String.fromCharCode((bytes[0] << 8) | bytes[1])); } } return ret.join(''); } /** * ArrayBuffer转为字符串,参数为ArrayBuffer对象 * @param buf * @returns */ public ab2str(buf: ArrayBuffer) { return String.fromCharCode.apply(null, new Uint8Array(buf)); } /** * * @param fileData * @returns */ public uint8ArrayToString(fileData) { let dataString = ""; for (let i = 0; i < fileData.length; i++) { dataString += String.fromCharCode(fileData[i]); } return dataString } /** * * @param str * @returns */ public stringToUint8Array(str) { let arr = []; for (let i = 0, j = str.length; i < j; ++i) { arr.push(str.charCodeAt(i)); } let tmpUint8Array = new Uint8Array(arr); return tmpUint8Array } /** * 字符串转为ArrayBuffer对象,参数为字符串 * @param str * @returns */ public str2ab(str) { let buf = new ArrayBuffer(str.length); // 每个字符占用2个字节 let bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } }