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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 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;
    }
}


相关文章
|
16天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
15天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
19天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
70 6
|
2天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
6天前
|
数据采集 存储 Web App开发
Java爬虫:深入解析商品详情的利器
在数字化时代,信息处理能力成为企业竞争的关键。本文探讨如何利用Java编写高效、准确的商品详情爬虫,涵盖爬虫技术概述、Java爬虫优势、开发步骤、法律法规遵守及数据处理分析等内容,助力电商领域市场趋势把握与决策支持。
|
10天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
11天前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
|
15天前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
56 3
|
16天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
25 4
|
19天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####