//build: 20240301 //微光二维码码制规则,包括101,103 等 //依赖组件: dxDriver,dxCommon,dxLogger import common from './dxCommon.js' import base64 from './dxBase64.js' import logger from './dxLogger.js' let sqliteObj // 比较两个字符串的前N个字符是否相等 function comparePrefix(str1, str2, N) { let substring1 = str1.substring(0, N); let substring2 = str2.substring(0, N); return substring1 === substring2; } // 101码值解码 function decode101(str) { if (str.length < 5) { logger.info("无效二维码") throw new Error("code invalid,length too short") } let decodeBuf = base64.decode(str.slice(0, -4)) decodeBuf=decodeBuf.substring(4) return decodeBuf } function hexStringToArrayBuffer (hexString) { var byteString = hexString.match(/.{1,2}/g); var byteArray = byteString.map(function (byte) { return parseInt(byte, 16); }); var buffer = new Uint8Array(byteArray).buffer; return buffer; } /** * 103码值解码 * 1、base64解码 * 2、解析组织编号 * 3、RSA解密 * 4、解析身份类型 * 5、解析权限标识 * 6、解析生码时间 * 7、解析码过期时间 * 8、校验通行码是否过期 * @param {*} str * @returns */ function decode103(str) { // FIXME 这个pubKey后期需要从配置中查询 let TLV_T_SIZE = 2, TLV_L_SIZE = 2, offset = 0, code, decryptedData, generationCodeTime, expirationTime // 1、base64解码 let decodeBuf = base64.toHexString(str) decodeBuf= hexStringToArrayBuffer(decodeBuf) let view = new Uint8Array(decodeBuf) let organizationNumber; // 2、解析组织编号 if (view[offset] == 0x01) { offset += TLV_T_SIZE let orgNumLen = view[offset] offset += TLV_L_SIZE let orgNumBuf = new Uint8Array(decodeBuf, offset, orgNumLen); organizationNumber = String.fromCharCode.apply(null, new Uint8Array(orgNumBuf)); logger.info("组织编号: " + organizationNumber) offset += orgNumLen } else { throw new Error("code invalid,organization number error") } // 3、RSA解密 if (view[offset] == 0x02) { // 组织编号数据长度 offset += TLV_T_SIZE let cipherTextLen = view[offset] offset += TLV_L_SIZE // 对密文进行RSA解密 let encryptedData = decodeBuf.slice(offset, offset + cipherTextLen) // TODO 秘钥写死,后续需要暴露出来 // RSA 查询密钥(也可以固定,也可以写在文本中),根据密钥再次解密 // RSA解密后的数据 let arr = sqliteObj.securityFindAllByCodeAndTypeAndTimeAndkey(undefined, undefined, undefined, Math.floor(Date.parse(new Date()) / 1000), organizationNumber) if (arr && arr.length > 0) { for (let data of arr) { decryptedData = common.arrayBufferRsaDecrypt(encryptedData, data.value) if (decryptedData != null) { break } } } if (!arr && arr.length <= 0 || decryptedData == null) { return str } } // 一个设备一个密钥,相当于设备内用于解密的公钥是固定的,可以把公钥放到配置中,这里先默认写死 // "MTlBODExMDA2MTkwMzQ4Q0I5QUY3QTc4QzAzOTQzNUU5NzNFODAzMEU4QUU1QzBEMkZFOEYwRjEzRjU4M0M5MTU5QUU5MTdDMDIzRDU0RDgxRUY2NTI0NkUyQ0Y2MUMzMTQzNTNENjA2NDU5N0Y2OTY5RUE4QjA5MUY1RTYyODM="; // let buf = common.arrayBufferRsaDecrypt(deData, deData.length) // 0 3 0 3 0 31 30 33 4 0 a 0 31 30 35 34 33 32 33 33 32 33 5 0 4 0 af 8c fa 5a 6 0 1 0 35 7 0 0 0 // 0 // 3 0 3 0 31 30 33 // 4 0 a 0 31 30 35 34 33 32 33 33 32 33 // 5 0 4 0 af 8c fa 5a // 6 0 1 0 35 // 7 0 0 0 offset = 1; view = new Uint8Array(decryptedData) // 4、解析身份类型(type:103) if (view[offset] == 0x03) { // 身份类型数据长度 offset += TLV_T_SIZE let identityTypeLength = view[offset] // 身份类型数据 offset += TLV_L_SIZE let identityTypeBuf = new Uint8Array(decryptedData, offset, identityTypeLength); let identityType = String.fromCharCode.apply(null, identityTypeBuf); offset += identityTypeLength logger.info("身份类型数据: " + identityType) } // 5、解析权限标识(code) if (view[offset] == 0x04) { // 权限标识数据长度 offset += TLV_T_SIZE let identityCodeLength = view[offset] // 权限标识数据 offset += TLV_L_SIZE let identityCodeBuf = new Uint8Array(decryptedData, offset, identityCodeLength); offset += identityCodeLength code = String.fromCharCode.apply(null, identityCodeBuf); } // 6、解析生码时间 if (view[offset] == 0x05) { // 生码时间数据长度 offset += TLV_T_SIZE let createTimeLength = view[offset] // 生码时间数据 offset += TLV_L_SIZE let createTimeBuf = new Uint8Array(decryptedData, offset, createTimeLength); offset += createTimeLength generationCodeTime = parseInt(common.arrayBufferToHexString(createTimeBuf.reverse()), 16) } // 7、解析码过期时间 if (view[offset] == 0x06) { // 码过期时间数据长度 offset += TLV_T_SIZE let expireTimeLength = view[offset] // 码过期时间数据 offset += TLV_L_SIZE let expireTimeBuf = new Uint8Array(decryptedData, offset, expireTimeLength); offset += expireTimeLength expirationTime = parseInt(String.fromCharCode.apply(null, expireTimeBuf)) } // 8、校验通行码是否过期 let timestamp = Date.now(); expirationTime = generationCodeTime + expirationTime if (expirationTime * 1000 > timestamp) { return code } else { return null } } const code = { formatCode: function (msg, sqlObj) { if (!msg) { throw new Error("msg should not be null or empty") } if (!sqlObj) { throw new Error("sqlObj should not be null or empty") } if (!sqliteObj) { sqliteObj = sqlObj } let data = {} // 判断码值 if (comparePrefix(msg, "&llgy", "&llgy".length) || comparePrefix(msg, "&v101", "&v101".length)) { // 101码值 data.type = 101 data.code = decode101(msg.substring(5)) } else if (comparePrefix(msg, "vg://v103", "vg://v103".length)) { // 103码值 data.type = 103 data.code = decode103(msg.substring(9)) ? decode103(msg.substring(9)) : msg.substring(9) } else if (comparePrefix(msg, "___VBAR_CONFIG_V1.1.0___", "___VBAR_CONFIG_V1.1.0___".length) || comparePrefix(msg, "___VBAR_KZB_V1.1.0___", "___VBAR_KZB_V1.1.0___".length)) { //TODO 先这样写,讨论好后更改流转逻辑 data.type = 'config' data.code = msg } else { data.type = 100 data.code = msg } if (data.code) { return data } else { console.log("decode fail") return } } } export default code;