前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java版银联8583协议解析,超简单超直观的实现及示例(全互联网最简单)

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

作者头像
杨永贞
发布2020-08-05 11:14:01
2K0
发布2020-08-05 11:14:01
举报

一直以来做嵌入式软件开发,跟银联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域。

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

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

代码语言:javascript
复制
/**
 * 签到报文组帧 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

关键方法

代码语言:javascript
复制
pack8583Fields

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

解析部分关键方法

代码语言:javascript
复制
ans8583Fields

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

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

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

请看 报文结构类定义:

代码语言:javascript
复制
/**
 * 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:

代码语言:javascript
复制
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方法打印出了各个域的信息。

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

代码语言:javascript
复制

 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错误

源码如下:

代码语言:javascript
复制
/**
 * 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++; //通讯流水每次加一
    }
代码语言:javascript
复制
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;
    }


}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-06-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档