jt-framework icon indicating copy to clipboard operation
jt-framework copied to clipboard

求教 消息体 加密如何处理

Open dk980241 opened this issue 1 year ago • 4 comments

求教 消息体 加密如何处理

消息头,消息属性中的 第10位,11位,12位 为 010 时,表示消息体经过SM4算法加密

dk980241 avatar May 21 '24 07:05 dk980241

可以先使用 Jt808RequestFilter (需要配置jt808.features.request-filter.enabled=true)来修改请求体或响应体,可以实现请求和响应的加解密。 但是 Jt808RequestFilter 无法拦截 CommandSender 下发的指令消息。

由于之前没遇到过加解密的场景,一直没支持这个特性。 请提供几个加解密的示例性的调试报文(不方便公开的话群里私发我)。

最近太忙了,预计这周末或下周末提供加解密特性的支持。

hylexus avatar May 21 '24 15:05 hylexus

感谢。 刚接到的需求,还在摸索,目前只有文档,还没有报文。 后续有动态,再同步。

dk980241 avatar May 22 '24 01:05 dk980241

我现在通过自定义 Jt808MsgEncoder,Jt808MsgDecoder 做了一套加解密的方案,和客户端联调数据是ok的,修改点和数据附上,博主可以审查下。

实现

  • CustomJt808MsgEncoder

    private CompositeByteBuf buildPackage(Jt808Response response, ByteBuf body, int totalSubPackageCount, int currentPackageNo, int flowId) {
        // 消息体sm4加密
        body = sm4Encrypt(body);
        ByteBuf headerBuf = this.encodeMsgHeader(response, body, totalSubPackageCount > 0, totalSubPackageCount, currentPackageNo, flowId);
        ...
    }
    
    
    private ByteBuf encodeMsgHeader(Jt808Response response, ByteBuf body, boolean hasSubPackage, int totalSubPkgCount, int currentSubPkgNo, int flowId) {
        Jt808ProtocolVersion version = response.version();
        ByteBuf header = this.allocator.buffer();
        JtProtocolUtils.writeWord(header, response.msgId());
        /*
          加密类型,0b010
          —bit10 --bit12为数据加密标识位;
          —当第 11位为 1,第10位和第12位为0,表示消息体经过SM4算法加密
         */
        int bodyPropsForJt808 = JtProtocolUtils.generateMsgBodyPropsForJt808(body.readableBytes(), 0b010, hasSubPackage, response.version(), response.reversedBit15InHeader());
        ...
    }
    
  • CustomJt808MsgDecoder

    public MutableJt808Request decode(ByteBuf byteBuf) {
         if (log.isDebugEnabled()) {
             log.debug("- >>>>>>>>>>>>>>> : 7E{}7E", HexStringUtils.byteBufToString(byteBuf));
         }
    
         ByteBuf escaped = this.msgBytesProcessor.doEscapeForReceive(byteBuf);
         if (log.isDebugEnabled()) {
             log.debug("+ >>>>>>>>>>>>>>> : 7E{}7E", HexStringUtils.byteBufToString(escaped));
         }
    
         Jt808RequestHeader header = this.parseMsgHeaderSpec(escaped);
         int msgBodyStartIndex = Jt808RequestHeader.msgBodyStartIndex(header.version(), header.msgBodyProps().hasSubPackage());
         MsgType msgType = (MsgType)this.msgTypeParser.parseMsgType(header.msgId()).orElseThrow(() -> {
             JtProtocolUtils.release(new Object[]{escaped});
             return new Jt808UnknownMsgException(header.msgId(), byteBuf);
         });
         byte originalCheckSum = escaped.getByte(escaped.readableBytes() - 1);
         byte calculatedCheckSum = this.msgBytesProcessor.calculateCheckSum(escaped.slice(0, escaped.readableBytes() - 1));
         ByteBuf body = escaped.retainedSlice(msgBodyStartIndex, header.msgBodyLength());
         // 消息体 sm4 解密
         body = sm4Decrypt(body);
         ....
     }
    
    

数据

  • 终端注册 数据
    .setProvinceId(11)
    .setCityId(2)
    .setManufacturerId("id987654321")
    .setTerminalType("type00123456781234567887654321")
    .setTerminalId("ID0000123456781234567887654321")
    .setColor((byte) 1)
    .setCarIdentifier("甘J-123459")
    
  • sm4 key 8e47374be6b8d114cb47be6a9a128a37 ,填充 SM4/ECB/PKCS5Padding
  • 消息体加密后生成的报文
    7E0100486001000000000000000006660001F54379B894A198DF648501D34446F71B043C33FBC988AA593AD96111482DD6DBBE8C573E551F2A2349D911536E9A033692A9C8F5A4DEC595738119DB95BECFA5FCCBFC9F4B1A5BA57FB43DDE36E28E6F7FAF5EA024C18DC3F2BEB4DCD247D4651D7E
    

dk980241 avatar May 28 '24 06:05 dk980241

升级到 2.1.4-rc.4(最新版 Jar 预计 24 小时内同步到中央仓库),然后提供一个 Jt808MsgEncryptionHandler 的实现即可,示例如下:

@Component
public class Jt808MsgEncryptionHandlerDemo01 implements Jt808MsgEncryptionHandler {

    @Override
    public ByteBuf decryptRequestBody(Jt808RequestHeader header, ByteBuf body) {
        final int encryptionType = header.msgBodyProps().encryptionType();
        if (encryptionType == 0) {
            return body;
        }
        // @see https://github.com/hylexus/jt-framework/issues/82
        // 消息属性中的 第10位,11位,12位 为 010 时,表示消息体经过SM4算法加密
        if (encryptionType == 0b010) {
            try {
                return JtCryptoUtil.SM4.ecbDecrypt(getSecretKey(), body);
            } finally {
                JtProtocolUtils.release(body);
            }
        }
        throw new NotImplementedException("不支持的加密类型: 0b" + FormatUtils.toBinaryString(encryptionType, 3));
    }

    @Override
    public ByteBuf encryptResponseBody(Jt808Response response, ByteBuf plaintextBody) {
        // response.encryptionType(010);
        final int encryptionType = response.encryptionType();
        if (encryptionType == 0) {
            return plaintextBody;
        }

        // @see https://github.com/hylexus/jt-framework/issues/82
        // 消息属性中的 第10位,11位,12位 为 010 时,表示消息体经过SM4算法加密
        if (encryptionType == 0b010) {
            try {
                return JtCryptoUtil.SM4.ecbEncrypt(getSecretKey(), plaintextBody);
            } finally {
                JtProtocolUtils.release(plaintextBody);
            }
        }
        throw new NotImplementedException("不支持的加密类型: 0b" + FormatUtils.toBinaryString(encryptionType, 3));
    }

    private byte[] getSecretKey() {
        // 从其他配置中获取密钥
        return HexStringUtils.hexString2Bytes("8e47374be6b8d114cb47be6a9a128a37");
    }
}

hylexus avatar Jun 02 '24 10:06 hylexus