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