java版银联8583协议解析,超简单超直观的实现及示例(全互联网最简单)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: java版银联8583协议解析,超简单超直观的实现及示例(全互联网最简单)

一直以来做嵌入式软件开发,跟银联8583协议通信打交道太多了。


最近有需要把8383协议的解析用到android上,但是搜遍了整个互联网,没发现有哪个简单好用点的java版8583解析库。就自己动手自己做一个吧,让其尽可能的简单,直观


如果在这个互联网上谁遇到过比这个还简单直观的,请留言我,我观摩下再进一步改进。


来做个对比,J8583CN :中国版的8583报文Java实现,实现了对8583报文创建、编辑、读写、解析。使用起来比较简单,且能灵活配置。 j8583cn 的报文标准参考了中国银联2.0和部分商行的标准。代码参考了源j8583程序


附带J8583CN的下载链接:https://sourceforge.net/projects/j8583cn/?source=typ_redirect


这份代码我下载下来了看,在懂8383协议的基础上一时半会儿也没看明白怎么用的,也不想花时间研究他了


我的github地址


https://github.com/yangyongzhen/Easy8583Ans.git


详细使用介绍参见:


银联卡8583协议小额免密免签交易总结


以下来看一个银联签到报文的组包:


不用关注BitMap如何填,如何组织,不用关注报文结构和长度,只需要根据协议填 你需要的域就行啦!


就是这么简单,filed[0] 到filed[63] 分别对应 1到 64域。


有多么简单?有多么直观?


请看以下签到报文组包示例:


/**
 * 签到报文组帧 yangyongzhen add 180627
 * @param field
 * @param tx
 */
public void frame8583QD( __8583Fields[] field, Pack tx){
    init8583Fields(fieldsSend);
    //消息类型
    tx.msgType[0] = 0x08;
    tx.msgType[1] = 0x00;
    //11域,受卡方系统跟踪号BCD 通讯流水
    field[10].ishave = 1;
    field[10].len = 3;
    String tmp = String.format("%06d",commSn);
    field[10].data = hexStringToBytes(tmp);
    //41域,终端号
    field[40].ishave = 1;
    field[40].len = 8;
    field[40].data = posNum.getBytes();
    //42域,商户号
    field[41].ishave = 1;
    field[41].len = 15;
    field[41].data = manNum.getBytes();
    //60域
    field[59].ishave = 1;
    field[59].len = 0x11;
    field[59].data = new byte[6];
    field[59].data[0] = 0x00;
    arraycopy(piciNum,0,field[59].data,1,3);
    field[59].data[4] = 0x00;
    field[59].data[5] = 0x30;
    //62域
    field[61].ishave = 1;
    field[61].len = 0x25;
    field[61].data = new byte[25];
    String str = "Sequence No12";
    arraycopy(str.getBytes(),0,field[61].data,0,13);
    arraycopy(licenceNum,0,field[61].data,13,4);
    arraycopy(posNum.getBytes(),0,field[61].data,17,8);
    //63域
    field[62].ishave = 1;
    field[62].len = 0x03;
    field[62].data = new byte[3];
    field[62].data[0] = 0x30;
    field[62].data[1] = 0x30;
    field[62].data[2] = 0x31;
    /*报文组帧,自动组织这些域到Pack的TxBuffer中*/
    pack8583Fields(field,tx);
    commSn++; //通讯流水每次加一
}
/**
 * 8583签到的响应报文解析
 * @param rxbuf
 * @param rxlen
 * @return 0,成功 非0,失败
 */
public int ans8583QD(byte[] rxbuf,int rxlen){
    int ret = 0;
    ret = ans8583Fields(rxbuf,rxlen,fieldsRecv);
    if(ret != 0) {
        //Log.d(TAG,"解析失败!");
        System.out.println("<-Er 解析失败!");
        return ret;
    }
    //Log.d(TAG,"解析成功!");
    System.out.println("->ok 解析成功!");
    //消息类型判断
    if((pack.msgType[0] != 0x08)||(pack.msgType[1]!= 0x10)) {
        //Log.d(TAG,"消息类型错!");
        return 2;
    }
    //应答码判断
    if((fieldsRecv[38].data[0] != 0x30)||(fieldsRecv[38].data[1] != 0x30)){
        //Log.d(TAG,"应答码不正确!");
        //Log.d(TAG,String.format("应答码:%02x%02x",fieldsRecv[38].data[0],fieldsRecv[38].data[1]));
        return 3;
    }
    //跟踪号比较
    if(!Arrays.equals(fieldsSend[10].data,fieldsRecv[10].data)){
        //return 4;
    }
    //终端号比较
    if(!Arrays.equals(fieldsSend[40].data,fieldsRecv[40].data)){
        //return 5;
    }
    //商户号比较
    if(!Arrays.equals(fieldsSend[41].data,fieldsRecv[41].data)){
        //return 6;
    }
    //3DES解密PIN KEY
    byte[] data = new byte[16];
    arraycopy(fieldsRecv[61].data,0,data,0,16);
    byte[] pinkey = DES_decrypt_3(data,hexStringToBytes(mainKey));
    //解密后的结果对8Byte全0做3DES加密运算
    System.out.println("pinkey:"+bytesToHexString(pinkey));
    byte[] tmp= {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    byte[] out =  DES_encrypt_3(tmp,pinkey);
    //对比pincheck是否一致
    byte[] check = new byte[4];
    byte[] pincheck = new byte[4];
    arraycopy(out,0,check,0,4);
    arraycopy(fieldsRecv[61].data,16,pincheck,0,4);
    if(!Arrays.equals(check,pincheck)) {
        System.out.println("<-Er PIK错误");
        return 7;
    }
    else {
        System.out.println("<-ok PIK正确");
    }
    //3DES解密MAC KEY
    arraycopy(fieldsRecv[61].data,20,data,0,16);
    byte[] mackey = DES_decrypt_3(data,hexStringToBytes(mainKey));
    //解密后的结果对8Byte全0做DES加密运算
    System.out.println("mackey:"+bytesToHexString(mackey));
    out =  DES_encrypt(tmp,mackey);
    byte[] maccheck = new byte[4];
    arraycopy(out,0,check,0,4);
    arraycopy(fieldsRecv[61].data,36,maccheck,0,4);
    if(!Arrays.equals(check,maccheck)) {
        System.out.println("<-Er MAC错误");
        return 8;
    }
    else {
        System.out.println("<-ok MAC正确");
        setMacKey(bytesToHexString(mackey));
    }
    //签到成功
    return 0;
}


最终组织好的报文在哪呢?在报文结构类Pack中的Txbuffer中,长度为Txlen


关键方法


pack8583Fields


它完成自动的配置位图和计算长度,并把组织好的报文放到TxBuffer中,供通信发送使用。


解析部分关键方法


ans8583Fields


它完成自动解析收到的报文,把各个解析出来的域和长度放到fieldRecv的各个域中。


需要外部设置的参数在哪放?


外部需要配置的有:终端号,商户号,主秘钥,TPDU。这些在类里定义的有set和get方法。


请看 报文结构类定义:


/**
 * Created by yangyongzhen on 2018/06/27
 * simple 8583 Protocol Analysis
 */
public class Easy8583Ans {
    private static final String TAG= " Easy8583Ans";
    private static String macKey ; //工作秘钥
    public static void setMacKey(String macKey) {
        Easy8583Ans.macKey = macKey;
    }
    public static String getMacKey() {
        return macKey;
    }
    /**
     * 定义8583的报文协议包结构 Len+Tpdu+Head+MsgType+BitMap+Data
     */
    public class Pack {
        public byte[] len;
        public byte[] tpdu;
        public byte[] head;
        public byte[] msgType;
        public byte[] bitMap;
        public byte[] txBuffer;
        public int txLen;
        public Pack() {
            len = new byte[2];
            tpdu = new byte[]{0x60, 0x05, 0x01, 0x00, 0x00};
            head = new byte[]{0x61, 0x31, 0x00, 0x31, 0x11,0x08};//这几项一般固定,不会变动
            msgType = new byte[2];
            bitMap = new byte[8];
            txBuffer = new byte[1024];
            txLen = 0;
        }
        @Override
        public String toString() {
            return "Pack{" +
                    "len=" + bytesToHexString(len) +
                    ", tpdu=" + bytesToHexString(tpdu) +
                    ", head=" + bytesToHexString(head) +
                    ", msgType=" + bytesToHexString(msgType) +
                    ", bitMap=" + bytesToHexString(bitMap) +
                    ", txLen=" + txLen +
                    ", txBuffer=" + bytesToHexString(txBuffer) +
                    '}';
        }
    }
    /**
     * 域定义
     */
    public class __8583Fields {
        int ishave;   /*是否存在*/
        int type;    /*类型(取值范围0-2,组帧时0:表示不计长度 1:表示长度占1字节,2:表示2长度占2字节)*/
        int len;     /*域长度(需根据各个域的定义要求正确取值)*/
        byte[] data; /*域内容*/
        __8583Fields()
        {
            ishave = 0;
            type = 0;
            len = 0;
            data =null;
        }
    }
    public __8583Fields[] fieldsSend; //发送的域
    public __8583Fields[] fieldsRecv; //接收的域
    public Pack pack;
    public Easy8583Ans()
    {
        fieldsSend = new __8583Fields[64];
        fieldsRecv = new __8583Fields[64];
        pack = new Pack();
        init8583(fieldsSend);
        init8583(fieldsRecv);
        init8583Fields(fieldsSend);
        init8583Fields(fieldsRecv);
    }


如何发送报文给银联后台呢?


假如你通信有Send(byte[] sendbuf, int len) 方法


那么只需 Send(pack.TxBuffer,pack.TxLen)


想打印出来报文日志在哪看? 直接pack.ToString()即可


附带一个调用的demo:


public static void main(String[] args) {
    My8583Ans myans = new My8583Ans();
    //签到组包
    myans.frame8583QD(myans.fieldsSend,myans.pack);
    //打印出待发送的报文
    byte[] send = new byte[myans.pack.txLen];
    arraycopy(myans.pack.txBuffer,0,send,0,myans.pack.txLen);
    System.out.println("->send:");
    System.out.println(My8583Ans.bytesToHexString(send));
    System.out.println(myans.pack.toString());
    System.out.println(myans.getFields(myans.fieldsSend));
    //接收解析,假设收到的报文在recv中
    String recvstr ="007960000001386131003111080810003800010AC0001450021122130107200800085500323231333031343931333239303039393939393930363030313433303137303131393939390011000005190030004046F161A743497B32EAC760DF5EA57DF5900ECCE3977731A7EA402DDF0000000000000000CFF1592A";
    System.out.println("->recv:"+recvstr);
    byte[] recv = My8583Ans.hexStringToBytes(recvstr);
    // mypack.ans8583Fields(bt,bt.length,mypack.fieldsRecv);
    //解析
    System.out.println("开始解析...");
    int ret = myans.ans8583QD(recv,recv.length);
    if(ret == 0){
        //打印出解析成功的各个域
        System.out.println("签到成功!");
        System.out.println(myans.getFields(myans.fieldsRecv));
    }


做了个getDields方法打印出了各个域的信息。


输出结果如下,连带每个域的日志都有了,够简单直观了吧:


com.example.yang.myapplication.My8583Ans
->send:
0057600501000061310031110808000020000000c0001600000139393939393930363030313433303137303131393939390011000000000030002553657175656e6365204e6f31323330363039393939393930360003303031
Pack{len=0057, tpdu=6005010000, head=613100311108, msgType=0800, bitMap=0020000000c00016, txLen=89, txBuffer=0057600501000061310031110808000020000000c0001600000139393939393930363030313433303137303131393939390011000000000030002553657175656e6365204e6f31323330363039393939393930360003303031}
Len: 0057
TPDU: 6005010000
Head: 613100311108
MsgType: 0800
BitMap: 0020000000c00016
-------------------------------------------------
[field:11] [000001]
-------------------------------------------------
[field:41] [3939393939393036]
-------------------------------------------------
[field:42] [303031343330313730313139393939]
-------------------------------------------------
[field:60] [len:0011] [000000000030]
-------------------------------------------------
[field:62] [len:0025] [53657175656e6365204e6f3132333036303939393939393036]
-------------------------------------------------
[field:63] [len:0003] [303031]
-------------------------------------------------
----------------------------------------------------------------------------------------------------
->recv:
007960000001386131003111080810003800010AC0001450021122130107200800085500323231333031343931333239303039393939393930363030313433303137303131393939390011000005190030004046F161A743497B32EAC760DF5EA57DF5900ECCE3977731A7EA402DDF0000000000000000CFF1592A
开始解析...
->ok 解析成功!
pinkey:d931648f3de313a4a22c15dca4f4299e
<-ok PIK正确
mackey:ab7c577cc7a180455fc2a085d7208a04
<-ok MAC正确
签到成功!
Len: 0079
TPDU: 6000000138
Head: 613100311108
MsgType: 0810
BitMap: 003800010ac00014
-------------------------------------------------
[field:11] [500211]
-------------------------------------------------
[field:12] [221301]
-------------------------------------------------
[field:13] [0720]
-------------------------------------------------
[field:32] [len:08] [00085500]
-------------------------------------------------
[field:37] [323231333031343931333239]
-------------------------------------------------
[field:39] [3030]
-------------------------------------------------
[field:41] [3939393939393036]
-------------------------------------------------
[field:42] [303031343330313730313139393939]
-------------------------------------------------
[field:60] [len:0011] [000005190030]
-------------------------------------------------
[field:62] [len:0040] [46f161a743497b32eac760df5ea57df5900ecce3977731a7ea402ddf0000000000000000cff1592a]
-------------------------------------------------


附带一个使用retrofit网络库访问的输出结果:


D/OkHttp: --> POST https://1xx.xx.xx.xx:xxxx/ http/1.1
          Content-Type: x-ISO-TPDU/x-auth
D/OkHttp: Content-Length: 89
          User-Agent: Donjin Http 0.1
          Cache-Control: no-cache
          Accept-Encoding: *
D/OkHttp: Host: xxx.xxx.xxx.xx:xxxx
          Connection: Keep-Alive
          --> END POST
D/OkHttp: <-- 200 OK https://xxx.xxx.xxx.xx:xxxx/ (421ms)
D/OkHttp: Allow: POST, PUT
          Content-Type: x-ISO-TPDU/x-auth
          Date: Sat, 30 Jun 2018 09:58:41 GMT
D/OkHttp: Content-Length: 123
D/OkHttp: Server: Access-Guard-1000-Software/1.0
          Connection: close
          <-- END HTTP
D/AA: 成功
      Response{protocol=http/1.1, code=200, message=OK, url=https://xxxxxxxx:xxxxx/}
D/respondAA:: 007960000005016131003111080810003800010ac0001400000117563506300800094900313735363335353837303233303037333738323231343839383431313334313331303031340011000007500030004050bc9eb4774a92544c29dad2c764150bb93eba92d9f11a222efa9c2300000000000000002b580802
I/System.out: 开始解析...
I/System.out: ->ok 解析成功!
I/System.out: pinkey:b1a7ab3cb49c9757390f39a19ce71ae7
I/System.out: <-Er PIK错误


源码如下:


/**
 * Created by yangyongzhen on 2018/06/30
 * simple 8583 Protocol Analysis,业务处理
 */
public class My8583Ans extends Easy8583Ans {
    private static final String TAG= " My8583Ans";
    //通信涉及到的一些参数,内部使用
    private static long commSn = 1; //通讯流水号
    private static byte[] piciNum = new byte[3];//批次号
    private static byte[] licenceNum = {0x33,0x30,0x36,0x30};//入网许可证编号
    //需要外部设置的参数有:商户号,终端号,主秘钥,TPDU(以下的为默认值,并提供set和get方法)
    //需要持久化存储这些参数,每次使用时加载
    private static String manNum  = "898411341310014"; //商户号
    private static String posNum  = "73782214"; //终端号
    private static String mainKey = "258FB0Ab70D025CDB99DF2C4D302D646"; //主秘钥
    private static String TPDU    = "6005010000";
    private static long   posSn = 1; //终端交易流水
    My8583Ans(){
        //通过子类修改父类的配置
        pack.tpdu = hexStringToBytes(TPDU);
    }
    /**
     * 签到报文组帧
     * @param field
     * @param tx
     */
    public void frame8583QD( __8583Fields[] field, Pack tx){
        init8583Fields(fieldsSend);
        //消息类型
        tx.msgType[0] = 0x08;
        tx.msgType[1] = 0x00;
        //11域,受卡方系统跟踪号BCD 通讯流水
        field[10].ishave = 1;
        field[10].len = 3;
        String tmp = String.format("%06d",commSn);
        field[10].data = hexStringToBytes(tmp);
        //41域,终端号
        field[40].ishave = 1;
        field[40].len = 8;
        field[40].data = posNum.getBytes();
        //42域,商户号
        field[41].ishave = 1;
        field[41].len = 15;
        field[41].data = manNum.getBytes();
        //60域
        field[59].ishave = 1;
        field[59].len = 0x11;
        field[59].data = new byte[6];
        field[59].data[0] = 0x00;
        arraycopy(piciNum,0,field[59].data,1,3);
        field[59].data[4] = 0x00;
        field[59].data[5] = 0x30;
        //62域
        field[61].ishave = 1;
        field[61].len = 0x25;
        field[61].data = new byte[25];
        String str = "Sequence No12";
        arraycopy(str.getBytes(),0,field[61].data,0,13);
        arraycopy(licenceNum,0,field[61].data,13,4);
        arraycopy(posNum.getBytes(),0,field[61].data,17,8);
        //63域
        field[62].ishave = 1;
        field[62].len = 0x03;
        field[62].data = new byte[3];
        field[62].data[0] = 0x30;
        field[62].data[1] = 0x30;
        field[62].data[2] = 0x31;
        /*报文组帧,自动组织这些域到Pack的TxBuffer中*/
        pack8583Fields(field,tx);
        commSn++; //通讯流水每次加一
    }


package com.example.yang.myapplication;
import java.util.Arrays;
import static com.example.yang.myapplication.DesUtil.DES_encrypt;
import static java.lang.System.arraycopy;
/**
 * Created by yangyongzhen on 2018/06/27
 * simple 8583 Protocol Analysis
 */
public class Easy8583Ans {
    private static final String TAG= " Easy8583Ans";
    private static String macKey ; //工作秘钥
    public static void setMacKey(String macKey) {
        Easy8583Ans.macKey = macKey;
    }
    public static String getMacKey() {
        return macKey;
    }
    /**
     * 定义8583的报文协议包结构 Len+Tpdu+Head+MsgType+BitMap+Data
     */
    public class Pack {
        public byte[] len;
        public byte[] tpdu;
        public byte[] head;
        public byte[] msgType;
        public byte[] bitMap;
        public byte[] txBuffer;
        public int txLen;
        public Pack() {
            len = new byte[2];
            tpdu = new byte[]{0x60, 0x05, 0x01, 0x00, 0x00};
            head = new byte[]{0x61, 0x31, 0x00, 0x31, 0x11,0x08};//这几项一般固定,不会变动
            msgType = new byte[2];
            bitMap = new byte[8];
            txBuffer = new byte[1024];
            txLen = 0;
        }
        @Override
        public String toString() {
            return "Pack{" +
                    "len=" + bytesToHexString(len) +
                    ", tpdu=" + bytesToHexString(tpdu) +
                    ", head=" + bytesToHexString(head) +
                    ", msgType=" + bytesToHexString(msgType) +
                    ", bitMap=" + bytesToHexString(bitMap) +
                    ", txLen=" + txLen +
                    ", txBuffer=" + bytesToHexString(txBuffer) +
                    '}';
        }
    }
    /**
     * 域定义
     */
    public class __8583Fields {
        int ishave;   /*是否存在*/
        int type;    /*类型(取值范围0-2,组帧时0:表示不计长度 1:表示长度占1字节,2:表示2长度占2字节)*/
        int len;     /*域长度(需根据各个域的定义要求正确取值)*/
        byte[] data; /*域内容*/
        __8583Fields()
        {
            ishave = 0;
            type = 0;
            len = 0;
            data =null;
        }
    }
    public __8583Fields[] fieldsSend; //发送的域
    public __8583Fields[] fieldsRecv; //接收的域
    public Pack pack;
    public Easy8583Ans()
    {
        fieldsSend = new __8583Fields[64];
        fieldsRecv = new __8583Fields[64];
        pack = new Pack();
        init8583(fieldsSend);
        init8583(fieldsRecv);
        init8583Fields(fieldsSend);
        init8583Fields(fieldsRecv);
    }
    /**
     * 各个域的配置,初始化
     * @param field
     */
    private void init8583(__8583Fields[] field){
        for(int i = 0; i < 64; i++)
        {
            field[i] = new __8583Fields();
        }
    }
    public void init8583Fields(__8583Fields[] field) {
        for(int i = 0;i <64; i++){
            field[i].ishave = 0;
        }
        field[0].type = 0;
        field[1].type = 1;//LLVAR
        field[2].type = 0;
        field[2].len = 3;
        field[3].type = 0;
        field[3].len = 6;
        field[10].type = 0;
        field[10].len = 3;
        field[11].type = 0;
        field[11].len = 3;
        field[12].type = 0;
        field[12].len = 2;
        field[13].type = 0;
        field[13].len = 2;
        field[14].type = 0;
        field[14].len = 2;
        field[21].type = 0;
        field[21].len = 2;
        field[22].type = 0;
        field[22].len = 2;
        field[24].type = 0;
        field[24].len = 1;
        field[25].type = 0;
        field[25].len = 1;
        field[31].type = 1;//LLVAR
        field[34].type = 1;//LLVAR
        field[36].type = 0;
        field[36].len = 12;
        field[37].type = 0;
        field[37].len = 6;
        field[38].type = 0;
        field[38].len = 2;
        field[39].type = 1;
        field[40].type = 0;
        field[40].len = 8;
        field[41].type = 0;
        field[41].len = 15;
        field[43].type = 1;
        field[47].type = 2;
        field[48].type = 0;
        field[48].len = 3;
        field[51].type = 0;
        field[51].len = 8;
        field[52].type = 0;
        field[52].len = 8;
        field[54].type = 2;//LLLVAR
        field[58].type = 2;
        field[59].type = 2;
        field[60].type = 2;
        field[61].type = 2;
        field[62].type = 2;
        field[63].type = 0;
        field[63].len = 8;
    }
    /**
     * 该方法不需要外部调用,该方法自动完成各个域的组包和BitMap的形成及报文长度的计算
     * 该方法最终组织各个域中的内容到 Pack的TxBuffer中,形成一完整报文
     * @param field
     * @param pk
    */
    public void pack8583Fields( __8583Fields[] field, Pack pk)
    {
        int j = 0;
        int len = 23;
        int tmplen = 0;
        int seat = 0x80;
        Arrays.fill(pack.txBuffer,(byte)0);
        for(int i = 0;i < 64; i++) {
            seat = (seat >>1 );
            if((i%8) == 0) {
                j++;
                seat = 0x80;
            }
            if(field[i].ishave == 1) {
                pk.bitMap[j-1] |= seat;//根据每个filed中的ishave是否为1,自动计算BitMap
                if(field[i].type == 0){
                    //根据每个域的数据类型,自动截取长度组包
                    //System.out.println("i = "+i);
                    arraycopy(field[i].data,0,pk.txBuffer,len,field[i].len);//数据
                    len += field[i].len;
                }
                else if(field[i].type == 1){
                    //域长度
                    pk.txBuffer[len] = (byte)field[i].len;
                    tmplen = Integer.parseInt(String.format("%02x",pk.txBuffer[len]),10);
                    //域数据
                    if((i==1)||(i==31)||(i==34)||(i==47)||(i==59)||(i==60))
                    {
                        tmplen = ((tmplen/2) + (tmplen%2));
                    }
                    len += 1;
                    arraycopy(field[i].data,0,pk.txBuffer,len,tmplen);//数据
                    len += tmplen;
                }
                else if(field[i].type == 2){
                    pk.txBuffer[len] = (byte)(field[i].len>>8);
                    pk.txBuffer[len+1] = (byte)field[i].len;
                    tmplen = Integer.parseInt(String.format("%02x%02x",pk.txBuffer[len],pk.txBuffer[len+1]),10);
                    if((i==1)||(i==31)||(i==34)||(i==47)||(i==59)||(i==60))
                    {
                        tmplen = ((tmplen/2) + (tmplen%2));
                    }
                    len += 2;
                    arraycopy(field[i].data,0,pk.txBuffer,len,tmplen);//数据
                    len += tmplen;
                }
            }
        }
        pk.txLen = len;
        pk.len[0] = (byte)((len-2) << 8);
        pk.len[1] = (byte)(len-2);
        arraycopy(pk.len,0,pk.txBuffer,0,2);
        arraycopy(pk.tpdu,0,pk.txBuffer,2,5);
        arraycopy(pk.head,0,pk.txBuffer,7,6);
        arraycopy(pk.msgType,0,pk.txBuffer,13,2);
        arraycopy(pk.bitMap,0,pk.txBuffer,15,8);
        //如果64域存在,自动计算MAC并填充
        if(field[63].ishave == 1){
            byte[] mac = upGetMac(pk.txBuffer,13,len-13-8,hexStringToBytes(macKey));
            arraycopy(mac,0,pk.txBuffer,len-8,8);
            arraycopy(mac,0,field[63].data,0,8);
        }
    }
    /**
     * 解析8583报文,解析成功后,可在fieldRecv中查看各个域
     * @param rxbuf
     * @param rxlen
     * @param fieldrcv
     * @return
     */
    public int ans8583Fields( byte[] rxbuf,int rxlen,__8583Fields[] fieldrcv)
    {
        int len = 0;
        int tmplen = 0;
        long buf = 0,seat=1;
        byte[] bitMap = new byte[8];
        init8583Fields(fieldsRecv);
        arraycopy(rxbuf,15,bitMap,0,8);
        arraycopy(rxbuf,0,pack.len,0,2);
        arraycopy(rxbuf,7,pack.head,0,6);
        arraycopy(rxbuf,13,pack.msgType,0,2);
        arraycopy(rxbuf,15,pack.bitMap,0,8);
        len += 23;
        for(int i = 0;i < 8;i++) {
            buf = ((buf<<8) | (bitMap[i]&0xff));
        }
        for(int i = 0; i < 64; i++) {
            if ((buf & (seat << (63 - i))) > 0) {
                fieldrcv[i].ishave = 1;
                if(fieldrcv[i].type == 0){
                    fieldrcv[i].data = new byte[fieldrcv[i].len];
                    arraycopy(rxbuf,len,fieldrcv[i].data,0,fieldrcv[i].len);
                    len += fieldrcv[i].len;
                }
                else if(fieldrcv[i].type == 1){
                    fieldrcv[i].len = rxbuf[len];
                    tmplen = Integer.parseInt(String.format("%02x",rxbuf[len]),10);
                    if((i==1)||(i==31)||(i==47)||(i==59)||(i==60))
                    {
                        tmplen = ((tmplen/2) + (tmplen%2));
                    }
                    len += 1;
                    fieldrcv[i].data = new byte[tmplen];
                    arraycopy(rxbuf,len,fieldrcv[i].data,0,tmplen);
                    len += tmplen;
                }
                else if(fieldrcv[i].type == 2){
                    fieldrcv[i].len = ((rxbuf[len]<<8) | rxbuf[len+1]);
                    tmplen = Integer.parseInt(String.format("%02x%02x",rxbuf[len],rxbuf[len+1]),10);
                    if((i==1)||(i==31)||(i==47)||(i==59)||(i==60))
                    {
                        tmplen = ((tmplen/2) + (tmplen%2));
                    }
                    len += 2;
                    fieldrcv[i].data = new byte[tmplen];
                    arraycopy(rxbuf,len,fieldrcv[i].data,0,tmplen);
                    len += tmplen;
                }
            }
        }
        if(len > rxlen)
        {
            return 1;
        }
        return 0;
    }
    public String getFields( __8583Fields[] field){
        StringBuffer str= new StringBuffer();
        str.append(String.format("Len:\t%s\n",bytesToHexString(pack.len)));
        str.append(String.format("TPDU:\t%s\n",bytesToHexString(pack.tpdu)));
        str.append(String.format("Head:\t%s\n",bytesToHexString(pack.head)));
        str.append(String.format("MsgType:\t%s\n",bytesToHexString(pack.msgType)));
        str.append(String.format("BitMap:\t%s\n",bytesToHexString(pack.bitMap)));
        str.append("-------------------------------------------------\n");
        for(int i = 0; i < 64; i++) {
            if(field[i].ishave == 1) {
                str.append(String.format("[field:%d] ", i+1));
                if(field[i].type == 1) {
                    str.append(String.format("[len:%02x] ", field[i].len));
                }else if(field[i].type == 2){
                    str.append(String.format("[len:%04x] ", field[i].len));
                }
                str.append(String.format("[%s]", bytesToHexString(field[i].data)));
                str.append("\n-------------------------------------------------\n");
            }
        }
        return  str.toString();
    }
    private static void dataXor1(byte[] in,int[] out, int len){
        for(int i =0; i < len; i++){
            out[i] |= (in[i]&0xff);
        }
    }
    private static void dataXor(byte[] source, byte[] dest, int size, byte[] out ) {
        for( int i = 0; i < size; i++ ) {
            out[i] = (byte)((dest[i]&0xff) ^ (source[i]&0xff));
        }
    }
    /**
     * 计算通信的MAC
     * @param buf
     * @param datasize
     * @param mackey
     * @return
     */
    public byte[] upGetMac( byte[] buf, int seat,int datasize, byte[] mackey){
        int x = datasize / 8;     //计算有多少个完整的块
        int n = datasize % 8;
       int[] val = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
        byte[] block = new byte[1024];
        Arrays.fill(block, (byte) 0);//清零
        arraycopy(buf,seat,block,0,datasize);
        //y非0,则在其后补上0x00...
        if( n != 0 ){
            x += 1;                                    //将补上的这一块加上去
        }
        byte[] tmp = new byte[8];
        for(int i = 0,j = 0;i < x;i++){
            arraycopy(block,j,tmp,0,8);
            dataXor1(tmp,val,8);
            j += 8;
        }
        String Bbuf = String.format("%02x%02x%02x%02x%02x%02x%02x%02x",val[0],val[1],
                val[2],val[3],val[4],val[5],val[6],val[7]);
        byte[] bbuf =  Bbuf.getBytes();
        byte[] b1 = new byte[8];
        byte[] b2 = new byte[8];
        arraycopy(bbuf,0,b1,0,8);
        arraycopy(bbuf,8,b2,0,8);
        byte[] tmpmac;
        tmpmac = DES_encrypt(b1,mackey);
        byte[] Abuf = new byte[8];
        dataXor( tmpmac, b2, 8, Abuf );
        tmpmac = DES_encrypt(Abuf,mackey);
        String str1 = String.format("%02x%02x%02x%02x%02x%02x%02x%02x",tmpmac[0],tmpmac[1],tmpmac[2]
                ,tmpmac[3],tmpmac[4],tmpmac[5],tmpmac[6],tmpmac[7]);
        byte[] mac = new byte[8];
        arraycopy(str1.getBytes(),0,mac,0,8);
        return mac;
    }
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
    public static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }
}


相关文章
|
14天前
|
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
35 0
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
84 29
RTSP协议规范与SmartMediaKit播放器技术解析
RTSP协议是实时流媒体传输的重要规范,大牛直播SDK的rtsp播放器基于此构建,具备跨平台支持、超低延迟(100-300ms)、多实例播放、高效资源利用、音视频同步等优势。它广泛应用于安防监控、远程教学等领域,提供实时录像、快照等功能,优化网络传输与解码效率,并通过事件回调机制保障稳定性。作为高性能解决方案,它推动了实时流媒体技术的发展。
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
46 5
重学Java基础篇—类的生命周期深度解析
本文全面解析了Java类的生命周期,涵盖加载、验证、准备、解析、初始化、使用及卸载七个关键阶段。通过分阶段执行机制详解(如加载阶段的触发条件与技术实现),结合方法调用机制、内存回收保护等使用阶段特性,以及卸载条件和特殊场景处理,帮助开发者深入理解JVM运作原理。同时,文章探讨了性能优化建议、典型异常处理及新一代JVM特性(如元空间与模块化系统)。总结中强调安全优先、延迟加载与动态扩展的设计思想,并提供开发建议与进阶方向,助力解决性能调优、内存泄漏排查及框架设计等问题。
33 5
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
36 3
|
14天前
|
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
24 1
|
28天前
|
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
48 5
|
2月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
173 60
【Java并发】【线程池】带你从0-1入门线程池
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
73 23

热门文章

最新文章

推荐镜像

更多