@tmsfe/tms-core 0.0.167 → 0.0.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import md5 from'./md5';const ecc=require('./nacl.min.js');const base64Util=require('./nacl-util.min.js');const logger=wx.getLogManager({});const SERVER_HOST_MAP={production:'https://tim.map.qq.com',test:'https://tim.sparta.html5.qq.com',development:'https://tim.sparta.html5.qq.com/dev',predist:'https://tim.sparta.html5.qq.com/pre',mock:'http://localhost:8003',};const baseUtil={_isObject:obj=>Object.prototype.toString.call(obj)==='[object Object]',_formatLog(args){const time=new Date().toISOString().replace('T',' ').substring(0,19).replace(/-/g,'-').replace(/:/g,':');args.unshift(time);return args},logInfo:(...args)=>{args.unshift('request_encrypt_log');const items=baseUtil._formatLog(args);console.log(...items);logger.log(...items)},encUrl:input=>{let base64=base64Util.encode(input);return base64.replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'')},decUrl:input=>{let base64=input.replace(/-/g,'+').replace(/_/g,'/');while(base64.length%4)base64+='=';return base64Util.decode(base64)},formatHeader:header=>{if(!header||!baseUtil._isObject(header))return{};const formatHeader={};Object.keys(header).forEach(key=>{formatHeader[key.toLowerCase()]=header[key]});return formatHeader},formatGetData:params=>{if(!params||!baseUtil._isObject(params))return'';return Object.keys(params).map(key=>`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`).join('&')},getSinanHost:()=>wx.$_encryptEnvInfo?SERVER_HOST_MAP[wx.$_encryptEnvInfo.envName]:'',getClientEncryptOpen:()=>wx.$_encryptEnvInfo?.requestEncryptOpen,BaseRespFac:class BaseRespFac{constructor(res,success=true,msg=''){this.success=success;this.msg=msg;this.res=res}}};let composeParamsFunc=null;const eccUtil={_refreshPromise:null,_refreshPubKeyInfo:async forceRefresh=>{if(!composeParamsFunc)return false;if(!wx.$_publicKey||forceRefresh){eccUtil._refreshPromise=new Promise(async resolve=>{const url=`${baseUtil.getSinanHost()}/basic/crypto/lastkey2`;const data=await composeParamsFunc({});wx.request({url,method:'POST',data,enableHttp2:true,success:()=>resolve(true),fail:()=>resolve(false)})})}return eccUtil._refreshPromise},_updateGlobalPublicKeyInfo:(clear,resHeader)=>{if(clear){wx.$_publicKey=null;return}const header=baseUtil.formatHeader(resHeader);const{'x-gateway-code':code,'x-crypto-pub-id':pubId,'x-crypto-pub-key':pubKey,'x-crypto-pub-exp':pubExp,'x-crypto-path':pathRule}=header;const success=!code&&pubId;wx.$_publicKey=success?{pubId,pubKey,pubExp,pathRule}:null;return new baseUtil.BaseRespFac(success)},_privateKeyInfo:null,getPrivateKeyInfo:forceUpdate=>{if(!wx.$_publicKey)return null;const serverPubInfo=wx.$_publicKey;if(forceUpdate||!eccUtil._privateKeyInfo||eccUtil._privateKeyInfo.serverPubId!==serverPubInfo.pubId){const keyPair=ecc.box.keyPair();eccUtil._privateKeyInfo={serverPubId:serverPubInfo.pubId,sharedByte:ecc.box.before(baseUtil.decUrl(serverPubInfo.pubKey),keyPair.secretKey),clientPublicKey:baseUtil.encUrl(keyPair.publicKey)}}return eccUtil._privateKeyInfo},resolveGwCode:async codeStr=>{if(!codeStr)return{retry:false,success:true};const code=parseInt(codeStr,10);switch(code){case 0:return{retry:false,success:true};case 11305:case 11306:{eccUtil._refreshPubKeyInfo(true).then(genPubSuccess=>{genPubSuccess&&eccUtil.getPrivateKeyInfo()});return{retry:true,success:false}}case 11308:{eccUtil.getPrivateKeyInfo(true);return{retry:true,success:false}}case 11300:case 11301:case 11302:case 11303:case 11304:case 11307:return{retry:true,success:false};case 11309:return{retry:false,success:false};default:return{retry:false,success:true}}},_getSignSharedByte:()=>baseUtil.decUrl('mEufQpM1n5J8-OZZoJE7ucYMC2suTjfsHUq_6z5cyh8'),getClientCryptoSign:(data={},header={},sharedByte)=>{const obj=baseUtil.formatHeader(Object.assign({},data,header));const str=Object.keys(obj).filter(item=>obj[item]).sort().reduce((pre,cur)=>{pre.push(`${cur}=${obj[cur]}`);return pre},[]).join('&');baseUtil.logInfo('---客户端签名---:before',str);const md5Str=md5(str);const nonce=ecc.randomBytes(ecc.box.nonceLength);const encrypted=ecc.box.after(md5Str,nonce,sharedByte);const combined=new Uint8Array(nonce.length+encrypted.length);combined.set(nonce);combined.set(encrypted,nonce.length);return baseUtil.encUrl(combined)},verifyServerCryptoSign:(traceId,resHeader={})=>{try{const formatHeader=baseUtil.formatHeader(resHeader);const signStr=formatHeader['x-crypto-sign'];if(!signStr)return false;const obj={'x-encrypt-key':formatHeader['x-encrypt-key'],'x-encrypt-response':formatHeader['x-encrypt-response'],'x-response-header-name':formatHeader['x-response-header-name'],'x-encrypted-headers':formatHeader['x-encrypted-headers'],'x-crypto-enable':formatHeader['x-crypto-enable'],'x-gateway-code':formatHeader['x-gateway-code'],'x-crypto-pub-id':formatHeader['x-crypto-pub-id'],'x-crypto-pub-key':formatHeader['x-crypto-pub-key'],'x-crypto-pub-exp':formatHeader['x-crypto-pub-exp'],'x-crypto-path':formatHeader['x-crypto-path'],'x-trace-id':traceId};const msg=baseUtil.decUrl(signStr);const decrypted=ecc.sign.open(msg,eccUtil._getSignSharedByte());const str=Object.keys(obj).filter(item=>obj[item]).sort().reduce((pre,cur)=>{pre.push(`${cur}=${obj[cur]}`);return pre},[]).join('&');baseUtil.logInfo('---验证服务端的客户端签名---:before',str,traceId);const preHashArr=md5(str);return preHashArr.length===decrypted.length&&preHashArr.every((v,i)=>v===decrypted[i])}catch(e){console.error('verifyServerCryptoSign error',e);return false}},execEncrypt:(input,ignoreNull=false)=>{try{const eccKeyInfo=eccUtil.getPrivateKeyInfo();if(!eccKeyInfo)return new baseUtil.BaseRespFac(null,false,'加密失败:无加密秘钥信息');const cryptoKeyInfo=Object.assign({},eccKeyInfo);if(ignoreNull&&(!input||input==='{}'))return new baseUtil.BaseRespFac({cryptoKeyInfo,encryptedContent:''});const{sharedByte}=cryptoKeyInfo;const nonce=ecc.randomBytes(ecc.box.nonceLength);const encrypted=ecc.box.after(base64Util.decodeUTF8(input),nonce,sharedByte);const combined=new Uint8Array(nonce.length+encrypted.length);combined.set(nonce);combined.set(encrypted,nonce.length);const encryptedStr=baseUtil.encUrl(combined);return new baseUtil.BaseRespFac({cryptoKeyInfo,encryptedContent:encryptedStr})}catch(e){return new baseUtil.BaseRespFac(null,false,`execEncrypt失败:${JSON.stringify(e)}`)}},execDecrypt:(msg,cryptoKeyInfo)=>{try{const{sharedByte}=cryptoKeyInfo;const nonce=msg.slice(0,ecc.box.nonceLength);const encrypted=msg.slice(ecc.box.nonceLength);const decrypted=ecc.box.open.after(encrypted,nonce,sharedByte);const decryptedStr=base64Util.encodeUTF8(decrypted);return new baseUtil.BaseRespFac(decryptedStr)}catch(err){return new baseUtil.BaseRespFac('',false,`execDecrypt失败:${JSON.stringify(err)}`)}},checkCryptoOpen:()=>!!eccUtil._privateKeyInfo,closeCrypto:()=>{eccUtil._privateKeyInfo=null;eccUtil._updateGlobalPublicKeyInfo(true)},openCrypto:async()=>{const genPubSuccess=await eccUtil._refreshPubKeyInfo(false);if(!genPubSuccess)return false;eccUtil.getPrivateKeyInfo();return true}};const cryptRuleUtil={isServerOpen:()=>!!wx.$_publicKey,pathInEnablePrefix:path=>{if(!wx.$_publicKey)return false;const{pathRule}=wx.$_publicKey;if(pathRule==='*')return true;const prefixArr=pathRule.split(',').map(item=>item.trim());for(let i=0,len=prefixArr.length;i<len;i++){if(path.indexOf(prefixArr[i])>-1)return true}return false},isPerformanceReport:(path,params)=>{if(path.indexOf('basic/event/upload')>-1){if(params.batch?.length===1&¶ms.batch[0]?.[31]==='tms-performance-log')return true;return false}return false},_encryptPathRule:{'tim.map.qq.com':['^/user/login','^/api/getClientConfigs','^/basic/crypto/lastkey2'],'tim.sparta.html5.qq.com':['^/user/login','^/cnabroad','^~/ReChargeCard/','^/gasolinerecharge/v2/','^/gasolinerecharge/rechargecard/','^/tde','^/basic/crypto/lastkey2']},isHostValid:url=>{const urlPattern=/^(https?:\/\/)?([^/?#]+)([/?#].*)?$/;const matches=url.match(urlPattern);if(!matches){console.error('Invalid URL:',url);return false}const domain=matches[2];const path=matches[3]||'/';if(cryptRuleUtil._encryptPathRule[domain]){const pathRules=cryptRuleUtil._encryptPathRule[domain];for(const rule of pathRules){const regex=new RegExp(rule);if(regex.test(path))return false}return true}return false}};const init=composeFunc=>{composeParamsFunc=(...args)=>composeFunc(...args);return new baseUtil.BaseRespFac(null)};const isCryptoRuleMath=(path,reqData)=>{if(!wx.$_encryptEnvInfo.requestEncryptOpen)return new baseUtil.BaseRespFac(false,false,'本地加密未开启');if(!cryptRuleUtil.isServerOpen())return new baseUtil.BaseRespFac(false,false,'服务端加密未开启');if(!cryptRuleUtil.pathInEnablePrefix(path))return new baseUtil.BaseRespFac(false,false,'未命中服务端加密规则');if(cryptRuleUtil.isPerformanceReport(path,reqData))return new baseUtil.BaseRespFac(false,false,'性能埋点');if(!cryptRuleUtil.isHostValid(path))return new baseUtil.BaseRespFac(false,false,'非sinan网关加密接口');return new baseUtil.BaseRespFac(true)};const reqEncrypt=(method,data,header,encryptedResponseHeaderName)=>{const reqHeader=baseUtil.formatHeader(header);if(reqHeader.contentType)return new baseUtil.BaseRespFac(null,false,'户自定义了请求contentType');let finalData={};if(method.toUpperCase()==='GET'){const searchParams=baseUtil.formatGetData(data);if(!searchParams)return new baseUtil.BaseRespFac(null,false,`GET请求参数不满足加密的规则:${JSON.stringify(data)}`);const{success,msg,res}=eccUtil.execEncrypt(searchParams);if(!success)return new baseUtil.BaseRespFac(null,false,`GET请求参数加密失败:${msg}`);finalData={tmsec:res.encryptedContent}}else{const{success,msg,res}=eccUtil.execEncrypt(JSON.stringify(data));if(!success)return new baseUtil.BaseRespFac(null,false,`${method}请求参数加密失败:${msg}`);finalData=res.encryptedContent}const{success,msg,res}=eccUtil.execEncrypt(JSON.stringify(header),true);if(!success)return new baseUtil.BaseRespFac(null,false,`请求Header加密失败:${msg}`);const cryptoHeader={'X-Crypto-Mode':'2','X-Encrypted-Headers':res.encryptedContent,'X-Encrypt-Pub':res.cryptoKeyInfo.serverPubId,'X-Encrypt-Key':res.cryptoKeyInfo.clientPublicKey,'X-Encrypt-Response':'3','X-Response-Header-Name':encryptedResponseHeaderName};const cryptoSign=eccUtil.getClientCryptoSign(baseUtil._isObject(finalData)?finalData:{body:finalData},cryptoHeader,res.cryptoKeyInfo.sharedByte);return new baseUtil.BaseRespFac({cryptoKeyInfo:res.cryptoKeyInfo,data:finalData,header:{...cryptoHeader,'Content-Type':'text/plain','X-Crypto-Sign':cryptoSign}})};const resDecrypt=async(requestTraceId,header,data,cryptoKeyInfo)=>{try{const formatHeader=baseUtil.formatHeader(header);const{'x-encrypt-response':encryptResponseMode,'x-response-header-name':encryptedResponseHeaderName,'x-encrypted-headers':encryptedHeaders,'x-gateway-code':gatewayCode,'content-type':contentType}=formatHeader;if(!encryptResponseMode||encryptResponseMode==='0'){const dataStr=base64Util.encodeUTF8(new Uint8Array(data));return new baseUtil.BaseRespFac({header,data:JSON.parse(dataStr),retry:false})}const{retry,success:gwSuccess}=await eccUtil.resolveGwCode(gatewayCode);if(!gwSuccess){const verified=eccUtil.verifyServerCryptoSign(requestTraceId,header);if(!verified)return new baseUtil.BaseRespFac({header:null,data:null,retry:false},false,'响应被篡改');return new baseUtil.BaseRespFac({header:null,data:null,retry},false,`网关返回错误码:${gatewayCode}`)}let decryptedHeaders={};if(encryptedResponseHeaderName){const{success,msg,res}=eccUtil.execDecrypt(baseUtil.decUrl(encryptedHeaders),cryptoKeyInfo);if(!success)return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Header失败:${msg}`);decryptedHeaders=JSON.parse(res)}const needDecode=contentType.indexOf('text/plain')>-1;const cipher=needDecode?baseUtil.decUrl(data):new Uint8Array(data);const{success,msg,res}=eccUtil.execDecrypt(cipher,cryptoKeyInfo);if(!success)return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Body失败:${msg}`);const decryptedBody=JSON.parse(res);return new baseUtil.BaseRespFac({retry:false,header:{...header,...decryptedHeaders},data:decryptedBody})}catch(e){return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Body失败:${JSON
|
|
1
|
+
import md5 from'./md5';const ecc=require('./nacl.min.js');const base64Util=require('./nacl-util.min.js');const logger=wx.getLogManager({});interface BaseResp<T>{success:boolean,msg:string,res:T,}interface CryptoKeyInfo{sharedByte:Uint8Array,clientPublicKey:string,serverPubId:string,}const SERVER_HOST_MAP={production:'https://tim.map.qq.com',test:'https://tim.sparta.html5.qq.com',development:'https://tim.sparta.html5.qq.com/dev',predist:'https://tim.sparta.html5.qq.com/pre',mock:'http://localhost:8003',};const baseUtil={_isObject:(obj:any):boolean=>Object.prototype.toString.call(obj)==='[object Object]',_formatLog(args:any[]):any[]{const time=new Date().toISOString().replace('T',' ').substring(0,19).replace(/-/g,'-').replace(/:/g,':');args.unshift(time);return args},logInfo:(...args)=>{args.unshift('request_encrypt_log');const items=baseUtil._formatLog(args);console.log(...items);logger.log(...items)},encUrl:(input:Uint8Array):string=>{let base64=base64Util.encode(input);base64=base64 .replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');return base64},decUrl:(input:string):Uint8Array=>{let base64=input.replace(/-/g,'+').replace(/_/g,'/');while(base64.length%4){base64+='='}return base64Util.decode(base64)},formatHeader:(header):any=>{if(!header||!baseUtil._isObject(header)){return{}}const formatHeader={};Object.keys(header).forEach((key)=>{formatHeader[key.toLocaleLowerCase()]=header[key]});return formatHeader},formatGetData:(params:any):string=>{if(!params||!baseUtil._isObject(params)){return''}return Object.keys(params).map(key=>`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`).join('&')},getSinanHost:():string=>{if(wx.$_encryptEnvInfo){return SERVER_HOST_MAP[wx.$_encryptEnvInfo.envName]}return''},getClientEncryptOpen:():boolean=>wx.$_encryptEnvInfo?.requestEncryptOpen,BaseRespFac:class BaseRespFac<T>implements BaseResp<T>{success:boolean;msg:string;res:T;constructor(res:T,success?:boolean,msg?:string){this.success=success===undefined?true:success;this.msg=msg===undefined?'':msg;this.res=res}},};let composeParamsFunc=null;const eccUtil={_refreshPromise:null,_refreshPubKeyInfo:async(forceRefresh:boolean):Promise<boolean>=>{if(!composeParamsFunc){return false}if(!wx.$_publicKey||forceRefresh){eccUtil._refreshPromise=new Promise(async(resolve)=>{const url=`${baseUtil.getSinanHost()}/basic/crypto/lastkey2`;const data=await composeParamsFunc({});wx.request({url,method:'POST',data,enableHttp2:true,success:()=>{resolve(true)},fail:()=>{resolve(false)},})})}return eccUtil._refreshPromise},_updateGlobalPublicKeyInfo:(clear:boolean,resHeader?:{[key:string]:any}):BaseResp<boolean>=>{if(clear){wx.$_publicKey=null;return}const header=baseUtil.formatHeader(resHeader);const{'x-gateway-code':code,'x-crypto-pub-id':pubId,'x-crypto-pub-key':pubKey,'x-crypto-pub-exp':pubExp,'x-crypto-path':pathRule,}=header;const success=!code&&pubId;wx.$_publicKey=success?{pubId,pubKey,pubExp,pathRule}:null;return new baseUtil.BaseRespFac(success)},_privateKeyInfo:null,getPrivateKeyInfo:(forceUpdate=false):CryptoKeyInfo=>{if(!wx.$_publicKey)return null;const serverPubInfo=wx.$_publicKey;if(forceUpdate||!eccUtil._privateKeyInfo||eccUtil._privateKeyInfo.serverPubId!==serverPubInfo.pubId){const keyPair=ecc.box.keyPair();eccUtil._privateKeyInfo={serverPubId:serverPubInfo.pubId,sharedByte:ecc.box.before(baseUtil.decUrl(serverPubInfo.pubKey),keyPair.secretKey),clientPublicKey:baseUtil.encUrl(keyPair.publicKey),}}return eccUtil._privateKeyInfo},resolveGwCode:async(codeStr:string):Promise<{retry:boolean,success:boolean}>=>{if(!codeStr)return{retry:false,success:true};const code=parseInt(codeStr,10);switch(code){case 0:return{retry:false,success:true};case 11305:case 11306:{eccUtil._refreshPubKeyInfo(true).then((genPubSuccess)=>{genPubSuccess&&eccUtil.getPrivateKeyInfo()});return{retry:true,success:false}}case 11308:{eccUtil.getPrivateKeyInfo(true);return{retry:true,success:false}}case 11300:case 11301:case 11302:case 11303:case 11304:case 11307:return{retry:true,success:false};case 11309:return{retry:false,success:false};default:return{retry:false,success:true}}},_getSignSharedByte:()=>baseUtil.decUrl('mEufQpM1n5J8-OZZoJE7ucYMC2suTjfsHUq_6z5cyh8'),getClientCryptoSign:(data={},header={},sharedByte):string=>{const obj=baseUtil.formatHeader(Object.assign({},data,header));const str=Object.keys(obj).filter(item=>obj[item]).sort().reduce((pre,cur)=>{pre.push(`${cur}=${obj[cur]}`);return pre},[]).join('&');baseUtil.logInfo('---客户端签名---:before',str);const md5Str=md5(str);const nonce=ecc.randomBytes(ecc.box.nonceLength);const encrypted=ecc.box.after(md5Str,nonce,sharedByte);const combined=new Uint8Array(nonce.length+encrypted.length);combined.set(nonce);combined.set(encrypted,nonce.length);return baseUtil.encUrl(combined)},verifyServerCryptoSign:(traceId:string,resHeader={}):boolean=>{try{const formatHeader=baseUtil.formatHeader(resHeader);const signStr=formatHeader['x-crypto-sign'];if(!signStr){return false}const obj={'x-encrypt-key':formatHeader['x-encrypt-key'],'x-encrypt-response':formatHeader['x-encrypt-response'],'x-response-header-name':formatHeader['x-response-header-name'],'x-encrypted-headers':formatHeader['x-encrypted-headers'],'x-crypto-enable':formatHeader['x-crypto-enable'],'x-gateway-code':formatHeader['x-gateway-code'],'x-crypto-pub-id':formatHeader['x-crypto-pub-id'],'x-crypto-pub-key':formatHeader['x-crypto-pub-key'],'x-crypto-pub-exp':formatHeader['x-crypto-pub-exp'],'x-crypto-path':formatHeader['x-crypto-path'],'x-trace-id':traceId,};const msg=baseUtil.decUrl(signStr);const decrypted=ecc.sign.open(msg,eccUtil._getSignSharedByte());const str=Object.keys(obj).filter(item=>obj[item]).sort().reduce((pre,cur)=>{pre.push(`${cur}=${obj[cur]}`);return pre},[]).join('&');baseUtil.logInfo('---验证服务端的客户端签名---:before',str,traceId);const preHashArr=md5(str);const verified=preHashArr.length===decrypted.length&&preHashArr.every((v,i)=>v===decrypted[i]);return verified}catch(e){console.error('verifyServerCryptoSign error',e);return false}},execEncrypt:(input:string,ignoreNull=false):BaseResp<{cryptoKeyInfo:CryptoKeyInfo,encryptedContent:any}|null>=>{try{const eccKeyInfo=eccUtil.getPrivateKeyInfo();if(!eccKeyInfo){return new baseUtil.BaseRespFac(null,false,'加密失败:无加密秘钥信息')}const cryptoKeyInfo=Object.assign({},eccKeyInfo);if(ignoreNull&&(!input||input==='{}')){return new baseUtil.BaseRespFac({cryptoKeyInfo,encryptedContent:''})}const{sharedByte}=cryptoKeyInfo;const nonce=ecc.randomBytes(ecc.box.nonceLength);const encrypted=ecc.box.after(base64Util.decodeUTF8(input),nonce,sharedByte);const combined=new Uint8Array(nonce.length+encrypted.length);combined.set(nonce);combined.set(encrypted,nonce.length);const encryptedStr=baseUtil.encUrl(combined);return new baseUtil.BaseRespFac({cryptoKeyInfo,encryptedContent:encryptedStr,})}catch(e){return new baseUtil.BaseRespFac(null,false,`execEncrypt失败:${JSON.stringify(e)}`)}},execDecrypt:(msg:Uint8Array,cryptoKeyInfo:CryptoKeyInfo):BaseResp<string>=>{try{const{sharedByte}=cryptoKeyInfo;const nonce=msg.slice(0,ecc.box.nonceLength);const encrypted=msg.slice(ecc.box.nonceLength);const decrypted=ecc.box.open.after(encrypted,nonce,sharedByte);const decryptedStr=base64Util.encodeUTF8(decrypted);return new baseUtil.BaseRespFac(decryptedStr)}catch(err){return new baseUtil.BaseRespFac('',false,`execDecrypt失败:${JSON.stringify(err)}`)}},checkCryptoOpen:():boolean=>!!eccUtil._privateKeyInfo,closeCrypto:()=>{eccUtil._privateKeyInfo=null;eccUtil._updateGlobalPublicKeyInfo(true)},openCrypto:async():Promise<boolean>=>{const genPubSuccess=await eccUtil._refreshPubKeyInfo(false);if(!genPubSuccess)return false;eccUtil.getPrivateKeyInfo();return true},};const cryptRuleUtil={isServerOpen:():boolean=>!!wx.$_publicKey,pathInEnablePrefix:(path:string):boolean=>{if(!wx.$_publicKey){return false}const{pathRule}=wx.$_publicKey;if(pathRule==='*'){return true}const prefixArr=pathRule.split(',').map(item=>item.trim());for(let i=0,len=prefixArr.length;i<len;i++){if(path.indexOf(prefixArr[i])>-1){return true}}return false},isPerformanceReport:(path:string,params:any):boolean=>{if(path.indexOf('basic/event/upload')>-1){if(params.batch?.length===1&¶ms.batch[0]?.[31]==='tms-performance-log'){return true}return false}return false},_encryptPathRule:{'tim.map.qq.com':['^/user/login','^/api/getClientConfigs','^/basic/crypto/lastkey2'],'tim.sparta.html5.qq.com':['^/user/login','^/cnabroad','^~/ReChargeCard/','^/gasolinerecharge/v2/','^/gasolinerecharge/rechargecard/','^/tde','^/basic/crypto/lastkey2',],},isHostValid:(url)=>{const urlPattern=/^(https?:\/\/)?([^/?#]+)([/?#].*)?$/;const matches=url.match(urlPattern);if(!matches){console.error('Invalid URL:',url);return false}const domain=matches[2];const path=matches[3]||'/';if(cryptRuleUtil._encryptPathRule[domain]){const pathRules=cryptRuleUtil._encryptPathRule[domain];for(const rule of pathRules){const regex=new RegExp(rule);if(regex.test(path)){return false}}return true}return false},};const init=(composeFunc:Function):BaseResp<null>=>{composeParamsFunc=(...args)=>composeFunc(...args);return new baseUtil.BaseRespFac(null)};const isCryptoRuleMath=(path:string,reqData:any):BaseResp<boolean>=>{if(!wx.$_encryptEnvInfo.requestEncryptOpen){return new baseUtil.BaseRespFac(false,false,'本地加密未开启')}if(!cryptRuleUtil.isServerOpen()){return new baseUtil.BaseRespFac(false,false,'服务端加密未开启')}if(!cryptRuleUtil.pathInEnablePrefix(path)){return new baseUtil.BaseRespFac(false,false,'未命中服务端加密规则')}if(cryptRuleUtil.isPerformanceReport(path,reqData)){return new baseUtil.BaseRespFac(false,false,'性能埋点')}if(!cryptRuleUtil.isHostValid(path)){return new baseUtil.BaseRespFac(false,false,'非sinan网关加密接口')}return new baseUtil.BaseRespFac(true)};const reqEncrypt=(method:string,data:any,header:{[key:string]:any},encryptedResponseHeaderName:string):BaseResp<{cryptoKeyInfo:CryptoKeyInfo,header:any,data:any,}|null>=>{const reqHeader=baseUtil.formatHeader(header);if(reqHeader.contentType){return new baseUtil.BaseRespFac(null,false,'户自定义了请求contentType')}let finalData={};if(method.toUpperCase()==='GET'){const searchParams=baseUtil.formatGetData(data);if(!searchParams){return new baseUtil.BaseRespFac(null,false,`GET请求参数不满足加密的规则:${JSON.stringify(data)}`)}const{success,msg,res}=eccUtil.execEncrypt(searchParams);if(!success){return new baseUtil.BaseRespFac(null,false,`GET请求参数加密失败:${msg}`)}finalData={tmsec:res.encryptedContent}}else{const{success,msg,res}=eccUtil.execEncrypt(JSON.stringify(data));if(!success){return new baseUtil.BaseRespFac(null,false,`${method}请求参数加密失败:${msg}`)}finalData=res.encryptedContent}const{success,msg,res}=eccUtil.execEncrypt(JSON.stringify(header),true);if(!success){return new baseUtil.BaseRespFac(null,false,`请求Header加密失败:${msg}`)}const cryptoHeader={'X-Crypto-Mode':'2','X-Encrypted-Headers':res.encryptedContent,'X-Encrypt-Pub':res.cryptoKeyInfo.serverPubId,'X-Encrypt-Key':res.cryptoKeyInfo.clientPublicKey,'X-Encrypt-Response':'3','X-Response-Header-Name':encryptedResponseHeaderName,};const cryptoSign=eccUtil.getClientCryptoSign(baseUtil._isObject(finalData)?finalData:{body:finalData,},cryptoHeader,res.cryptoKeyInfo.sharedByte);return new baseUtil.BaseRespFac({cryptoKeyInfo:res.cryptoKeyInfo,data:finalData,header:{...cryptoHeader,'Content-Type':'text/plain','X-Crypto-Sign':cryptoSign,},})};const resDecrypt=async(requestTraceId:string,header,data,cryptoKeyInfo:CryptoKeyInfo):Promise<BaseResp<{retry:boolean,header:any,data:any,}>>=>{try{const formatHeader=baseUtil.formatHeader(header);const{'x-encrypt-response':encryptResponseMode,'x-response-header-name':encryptedResponseHeaderName,'x-encrypted-headers':encryptedHeaders,'x-gateway-code':gatewayCode,'content-type':contentType,}=formatHeader;if(!encryptResponseMode||encryptResponseMode==='0'){const dataStr=base64Util.encodeUTF8(new Uint8Array(data));return new baseUtil.BaseRespFac({header,data:JSON.parse(dataStr),retry:false,})}const{retry,success:gwSuccess}=await eccUtil.resolveGwCode(gatewayCode);if(!gwSuccess){const verified=eccUtil.verifyServerCryptoSign(requestTraceId,header);if(!verified){return new baseUtil.BaseRespFac({header:null,data:null,retry:false},false,'响应被篡改')}return new baseUtil.BaseRespFac({header:null,data:null,retry},false,`网关返回错误码:${gatewayCode}`)}let decryptedHeaders={};if(encryptedResponseHeaderName){const{success,msg,res}=eccUtil.execDecrypt(baseUtil.decUrl(encryptedHeaders),cryptoKeyInfo);if(!success){return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Header失败:${msg}`)}decryptedHeaders=JSON.parse(res)}const needDecode=contentType.indexOf('text/plain')>-1;const cipher=needDecode?baseUtil.decUrl(data):new Uint8Array(data);const{success,msg,res}=eccUtil.execDecrypt(cipher,cryptoKeyInfo);if(!success){return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Body失败:${msg}`)}const decryptedBody=JSON.parse(res);return new baseUtil.BaseRespFac({retry:false,header:{...header,...decryptedHeaders,},data:decryptedBody,})}catch(e){return new baseUtil.BaseRespFac({header:null,data:null,retry:true},false,`解密响应Body失败:${JSON.stringify(e)}`)}};let dealEncryptionSwitching=false;const dealEncryptionSwitch=async(path:string,traceId:string,resHeader):Promise<void>=>{if((!resHeader||dealEncryptionSwitching)){return}dealEncryptionSwitching=true;const formatHeader=baseUtil.formatHeader(resHeader);const cryptoDisabled=formatHeader['x-crypto-enable']==='0';if((eccUtil.checkCryptoOpen()&&cryptoDisabled)){const verified=eccUtil.verifyServerCryptoSign(traceId,formatHeader);if(!verified){dealEncryptionSwitching=false;baseUtil.logInfo(`验签失败:${path}:${traceId}`);return}}if(cryptoDisabled){eccUtil.closeCrypto()}else if(formatHeader['x-crypto-enable']==='1'){await eccUtil.openCrypto()}dealEncryptionSwitching=false;return};const dealRes=(path:string,traceId:string,resHeader,reqData):BaseResp<boolean>=>{const specialPath=[`${baseUtil.getSinanHost()}/user/login`,`${baseUtil.getSinanHost()}/basic/crypto/lastkey2`,].indexOf(path)>-1;if(specialPath){const formatHeader=baseUtil.formatHeader(resHeader);const verified=eccUtil.verifyServerCryptoSign(traceId,formatHeader);if(!verified){return new baseUtil.BaseRespFac(false,false,`验签失败:${path}:${traceId}`)}eccUtil._updateGlobalPublicKeyInfo(false,resHeader)}return new baseUtil.BaseRespFac(!cryptRuleUtil.isPerformanceReport(path,reqData))};const encryptUtil={init,isCryptoRuleMath,logInfo:baseUtil.logInfo,dealRes,reqEncrypt,resDecrypt,dealEncryptionSwitch,};export default encryptUtil;
|
package/src/encrypt/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import encryptUtil from'./encrypt-util';import{genTraceparent}from'./traceUtils';const util={logInfo:(...args)=>encryptUtil.logInfo(...args),reportFunc:(...args)=>{util.logInfo('reportFunc init fail:',...args)},reqEncrypt:({url,method,data,header
|
|
1
|
+
import encryptUtil from'./encrypt-util';import{genTraceparent}from'./traceUtils';const util={logInfo:(...args)=>encryptUtil.logInfo(...args),reportFunc:(...args)=>{util.logInfo('reportFunc init fail:',...args)},reqEncrypt:(obj:{url:string,method:string,data:any,header:any}):{data:any,header:any,msg:string,cryptoKeyInfo?:any}=>{const{url,method,data,header={}}=obj;const{success,msg}=encryptUtil.isCryptoRuleMath(url,data);if(!success){return{data,header,msg}}const reqEncryptRes=encryptUtil.reqEncrypt(method,data,header,'');if(!reqEncryptRes.success){return{data,header,msg}}return Object.assign({msg},reqEncryptRes.res)},};let originalRequestApi;let originalUploadFileApi;const requestInit=(utilFunc)=>{if(!wx.reqCryptoFlag){originalRequestApi=wx.request;const{report,composeParamsFunc}=utilFunc;encryptUtil.init(composeParamsFunc);util.reportFunc=(...args)=>{util.logInfo(...args);report('request_encrypt_log',...args)};proxyWxRequest();wx.reqCryptoFlag=true}if(!wx.uploadFileCryptoFlag){originalUploadFileApi=wx.uploadFile;proxyWxUploadFile();wx.uploadFileCryptoFlag=true}};function proxyWxRequest():void{Object.defineProperty(wx,'request',{writable:true,enumerable:true,configurable:true,value(options:any){const{url,method,data,header={},success,fail,complete,dataType,responseType}=options;const traceparent=genTraceparent();const traceId=traceparent.split('-')[1];const originalOptions={...options};if(dataType||responseType){util.reportFunc(url,traceparent,'用户自定义了dataType和responseType');originalRequestApi.call(this,{...originalOptions,success:(res)=>{encryptUtil.dealEncryptionSwitch(url,traceId,res.header);success?.call(this,res)},header:{...header,Traceparent:traceparent},});return}const{data:formatData,header:formatHeader,msg,cryptoKeyInfo}=util.reqEncrypt({url,method,data,header});if(!cryptoKeyInfo){util.logInfo(url,traceparent,msg);originalRequestApi.call(this,{...originalOptions,success:async(res)=>{const{success:dealSuccess,res:needDealHeader}=encryptUtil.dealRes(url,traceId,res.header,formatData);if(dealSuccess){needDealHeader&&encryptUtil.dealEncryptionSwitch(url,traceId,res.header);success?.call(this,res)}else{util.reportFunc(url,traceparent,`加密验签不通过:${JSON.stringify(res)}`);fail?.call(this,new Error('加密验签不通过'))}},header:{...header,Traceparent:traceparent},});return}let completeResolver;const completePromp=new Promise((resolve)=>{completeResolver=resolve});originalRequestApi.call(this,{...originalOptions,data:formatData,header:{...formatHeader,Traceparent:traceparent},dataType:'其他',responseType:'arraybuffer',success:async(result)=>{const{header:resHeader,data:resData}=result;const{success:decSuccess,msg,res}=await encryptUtil.resDecrypt(traceId,resHeader,resData,cryptoKeyInfo);if(res.retry){util.reportFunc(url,traceparent,`解密失败:${msg}`);const newTraceparent=genTraceparent();const traceId=newTraceparent.split('-')[1];originalRequestApi.call(this,{...originalOptions,success:(res)=>{encryptUtil.dealEncryptionSwitch(url,traceId,res.header);success?.call(this,res)},header:{...header,Traceparent:newTraceparent},});return}if(decSuccess){encryptUtil.dealEncryptionSwitch(url,traceId,resHeader);success?.call(this,res)}else{util.reportFunc(url,traceparent,`解密失败:${msg}`);fail?.call(this,new Error(msg))}const completeRes:any=await completePromp;completeRes.header=resHeader;completeRes.data=resData;complete?.call(this,completeRes)},fail:async(err)=>{fail?.call(this,err);const completeRes:any=await completePromp;complete?.call(this,completeRes)},complete:(res)=>{completeResolver(res)},})},})}function proxyWxUploadFile():void{Object.defineProperty(wx,'uploadFile',{writable:true,enumerable:true,configurable:true,value(options:any){originalUploadFileApi.call(this,Object.assign(options,{header:{...options.header,Traceparent:genTraceparent()},}))},})}export const encryptObjInit=(utilFunc:{composeParamsFunc:Function,report:Function,})=>{requestInit(utilFunc)};
|
package/src/encrypt/md5.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
function safeAdd(x,y){const lsw=(x&0xffff)+(y&0xffff);const msw=(x>>16)+(y>>16)+(lsw>>16);return(msw<<16)|(lsw&0xffff)}function bitRotateLeft(num,cnt){return(num<<cnt)|(num>>>(32-cnt))}function md5cmn(q,a,b,x,s,t){return safeAdd(bitRotateLeft(safeAdd(safeAdd(a,q),safeAdd(x,t)),s),b)}function md5ff(a,b,c,d,x,s,t){return md5cmn((b&c)|(~b&d),a,b,x,s,t)}function md5gg(a,b,c,d,x,s,t){return md5cmn((b&d)|(c&~d),a,b,x,s,t)}function md5hh(a,b,c,d,x,s,t){return md5cmn(b^c^d,a,b,x,s,t)}function md5ii(a,b,c,d,x,s,t){return md5cmn(c^(b|~d),a,b,x,s,t)}function binlMD5(x,len){x[len>>5]|=0x80<<len%32;x[(((len+64)>>>9)<<4)+14]=len;let a=1732584193
|
|
2
|
+
function safeAdd(x,y){const lsw=(x&0xffff)+(y&0xffff);const msw=(x>>16)+(y>>16)+(lsw>>16);return(msw<<16)|(lsw&0xffff)}function bitRotateLeft(num,cnt){return(num<<cnt)|(num>>>(32-cnt))}function md5cmn(q,a,b,x,s,t){return safeAdd(bitRotateLeft(safeAdd(safeAdd(a,q),safeAdd(x,t)),s),b)}function md5ff(a,b,c,d,x,s,t){return md5cmn((b&c)|(~b&d),a,b,x,s,t)}function md5gg(a,b,c,d,x,s,t){return md5cmn((b&d)|(c&~d),a,b,x,s,t)}function md5hh(a,b,c,d,x,s,t){return md5cmn(b^c^d,a,b,x,s,t)}function md5ii(a,b,c,d,x,s,t){return md5cmn(c^(b|~d),a,b,x,s,t)}function binlMD5(x,len){x[len>>5]|=0x80<<len%32;x[(((len+64)>>>9)<<4)+14]=len;let a=1732584193;let b=-271733879;let c=-1732584194;let d=271733878;for(let i=0;i<x.length;i+=16){const olda=a;const oldb=b;const oldc=c;const oldd=d;a=md5ff(a,b,c,d,x[i],7,-680876936);d=md5ff(d,a,b,c,x[i+1],12,-389564586);c=md5ff(c,d,a,b,x[i+2],17,606105819);b=md5ff(b,c,d,a,x[i+3],22,-1044525330);a=md5ff(a,b,c,d,x[i+4],7,-176418897);d=md5ff(d,a,b,c,x[i+5],12,1200080426);c=md5ff(c,d,a,b,x[i+6],17,-1473231341);b=md5ff(b,c,d,a,x[i+7],22,-45705983);a=md5ff(a,b,c,d,x[i+8],7,1770035416);d=md5ff(d,a,b,c,x[i+9],12,-1958414417);c=md5ff(c,d,a,b,x[i+10],17,-42063);b=md5ff(b,c,d,a,x[i+11],22,-1990404162);a=md5ff(a,b,c,d,x[i+12],7,1804603682);d=md5ff(d,a,b,c,x[i+13],12,-40341101);c=md5ff(c,d,a,b,x[i+14],17,-1502002290);b=md5ff(b,c,d,a,x[i+15],22,1236535329);a=md5gg(a,b,c,d,x[i+1],5,-165796510);d=md5gg(d,a,b,c,x[i+6],9,-1069501632);c=md5gg(c,d,a,b,x[i+11],14,643717713);b=md5gg(b,c,d,a,x[i],20,-373897302);a=md5gg(a,b,c,d,x[i+5],5,-701558691);d=md5gg(d,a,b,c,x[i+10],9,38016083);c=md5gg(c,d,a,b,x[i+15],14,-660478335);b=md5gg(b,c,d,a,x[i+4],20,-405537848);a=md5gg(a,b,c,d,x[i+9],5,568446438);d=md5gg(d,a,b,c,x[i+14],9,-1019803690);c=md5gg(c,d,a,b,x[i+3],14,-187363961);b=md5gg(b,c,d,a,x[i+8],20,1163531501);a=md5gg(a,b,c,d,x[i+13],5,-1444681467);d=md5gg(d,a,b,c,x[i+2],9,-51403784);c=md5gg(c,d,a,b,x[i+7],14,1735328473);b=md5gg(b,c,d,a,x[i+12],20,-1926607734);a=md5hh(a,b,c,d,x[i+5],4,-378558);d=md5hh(d,a,b,c,x[i+8],11,-2022574463);c=md5hh(c,d,a,b,x[i+11],16,1839030562);b=md5hh(b,c,d,a,x[i+14],23,-35309556);a=md5hh(a,b,c,d,x[i+1],4,-1530992060);d=md5hh(d,a,b,c,x[i+4],11,1272893353);c=md5hh(c,d,a,b,x[i+7],16,-155497632);b=md5hh(b,c,d,a,x[i+10],23,-1094730640);a=md5hh(a,b,c,d,x[i+13],4,681279174);d=md5hh(d,a,b,c,x[i],11,-358537222);c=md5hh(c,d,a,b,x[i+3],16,-722521979);b=md5hh(b,c,d,a,x[i+6],23,76029189);a=md5hh(a,b,c,d,x[i+9],4,-640364487);d=md5hh(d,a,b,c,x[i+12],11,-421815835);c=md5hh(c,d,a,b,x[i+15],16,530742520);b=md5hh(b,c,d,a,x[i+2],23,-995338651);a=md5ii(a,b,c,d,x[i],6,-198630844);d=md5ii(d,a,b,c,x[i+7],10,1126891415);c=md5ii(c,d,a,b,x[i+14],15,-1416354905);b=md5ii(b,c,d,a,x[i+5],21,-57434055);a=md5ii(a,b,c,d,x[i+12],6,1700485571);d=md5ii(d,a,b,c,x[i+3],10,-1894986606);c=md5ii(c,d,a,b,x[i+10],15,-1051523);b=md5ii(b,c,d,a,x[i+1],21,-2054922799);a=md5ii(a,b,c,d,x[i+8],6,1873313359);d=md5ii(d,a,b,c,x[i+15],10,-30611744);c=md5ii(c,d,a,b,x[i+6],15,-1560198380);b=md5ii(b,c,d,a,x[i+13],21,1309151649);a=md5ii(a,b,c,d,x[i+4],6,-145523070);d=md5ii(d,a,b,c,x[i+11],10,-1120210379);c=md5ii(c,d,a,b,x[i+2],15,718787259);b=md5ii(b,c,d,a,x[i+9],21,-343485551);a=safeAdd(a,olda);b=safeAdd(b,oldb);c=safeAdd(c,oldc);d=safeAdd(d,oldd)}return[a,b,c,d]}function binl2rstr(input){let output='';const length32=input.length*32;for(let i=0;i<length32;i+=8){output+=String.fromCharCode((input[i>>5]>>>i%32)&0xff)}return output}function rstr2binl(input){const output=[];output[(input.length>>2)-1]=undefined;for(let i=0;i<output.length;i+=1){output[i]=0}const length8=input.length*8;for(let i=0;i<length8;i+=8){output[i>>5]|=(input.charCodeAt(i/8)&0xff)<<i%32}return output}function rstrMD5(s){return binl2rstr(binlMD5(rstr2binl(s),s.length*8))}function str2rstrUTF8(input){return unescape(encodeURIComponent(input))}function rawMD5(s){return rstrMD5(str2rstrUTF8(s))}function rstr2uint8array(input){const output=new Uint8Array(input.length);for(let i=0;i<input.length;i++){output[i]=input.charCodeAt(i)}return output}function md5(string){return rstr2uint8array(rawMD5(string))}export default md5;
|
|
@@ -4,21 +4,4 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/* eslint-disable */
|
|
6
6
|
var CryptoJS=CryptoJS||function(t){var n={},i=n.lib={},s=function(){},r=i.Base={extend:function(t){s.prototype=this;var n=new s;return t&&n.mixIn(t),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var n in t)t.hasOwnProperty(n)&&(this[n]=t[n]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},e=i.WordArray=r.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=null!=n?n:4*t.length},toString:function(t){return(t||o).stringify(this)},concat:function(t){var n=this.words,i=t.words,r=this.sigBytes;if(t=t.sigBytes,this.clamp(),r%4)for(var e=0;e<t;e++)n[r+e>>>2]|=(i[e>>>2]>>>24-e%4*8&255)<<24-(r+e)%4*8;else if(65535<i.length)for(e=0;e<t;e+=4)n[r+e>>>2]=i[e>>>2];else n.push.apply(n,i);return this.sigBytes+=t,this},clamp:function(){var n=this.words,i=this.sigBytes;n[i>>>2]&=4294967295<<32-i%4*8,n.length=t.ceil(i/4)},clone:function(){var t=r.clone.call(this);return t.words=this.words.slice(0),t},random:function(n){for(var i=[],r=0;r<n;r+=4)i.push(4294967296*t.random()|0);return new e.init(i,n)}}),o=(n.enc={}).Hex={stringify:function(t){var n=t.words;t=t.sigBytes;for(var i=[],r=0;r<t;r++){var e=n[r>>>2]>>>24-r%4*8&255;i.push((e>>>4).toString(16)),i.push((15&e).toString(16))}return i.join("")},parse:function(t){for(var n=t.length,i=[],r=0;r<n;r+=2)i[r>>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new e.init(i,n/2)}};return n}(Math);
|
|
7
|
-
|
|
8
|
-
// 生成随机的16字节trace-id和8字节parent-id/span-id
|
|
9
|
-
function generateRandomHex(size) {
|
|
10
|
-
return CryptoJS.lib.WordArray.random(size).toString(CryptoJS.enc.Hex);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// 生成traceparent ID
|
|
14
|
-
function genTraceparent() {
|
|
15
|
-
const version = '00';
|
|
16
|
-
const traceId = generateRandomHex(16); // 16字节的trace-id
|
|
17
|
-
const parentId = generateRandomHex(8); // 8字节的parent-id/span-id
|
|
18
|
-
const traceFlags = '01'; // 示例标志,表示采样
|
|
19
|
-
return `${version}-${traceId}-${parentId}-${traceFlags}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export {
|
|
23
|
-
genTraceparent,
|
|
24
|
-
};
|
|
7
|
+
function generateRandomHex(size){return CryptoJS.lib.WordArray.random(size).toString(CryptoJS.enc.Hex)}function genTraceparent(){const version='00';const traceId=generateRandomHex(16);const parentId=generateRandomHex(8);const traceFlags='01';return`${version}-${traceId}-${parentId}-${traceFlags}`}export{genTraceparent,};
|