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

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

不用关注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;
    }


}


©️2020 CSDN 皮肤主题: 成长之路 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值