X银行POS终端规范解读

中国X银行间联POS终端规范解读

 

 

一、标准POS报文设计思路解读

以下表格为标准POS报文结构,由TPDU+HEAD(报文头)+ISO8583MSG(8583报文数据)构成;其中8583报文体中包括”位元素”,代表后面的数据是哪几个域被用到,这样就最大化的缩小了发送报文的字节数。(详细见附录A

TPDU

报文头

应用数据

ISO8583Msg

ID

 

使

交易数据

60H

N2

N2

N2

N2

N1

N1

N6

不定长度

 

XXX银行对此进行了改造,加入了双倍加密模式,由于3des算法对原文字节长度要求为8的倍数,所以需要加入一个“加密信息”区域了解原始报文长度,从而能在解密后得到原始报文。

二、报文结构解读

2.1X银行pos报文格式

TPDU

报文头

加密信息

应用数据

ISO8583Msg

ID

 

使

报文长度

算法标识

商户号

终端号

交易

标识

响应码

保留

交易数据

60H

N2

N2

N2

N2

N1

N1

N6

A3

A1

A15

A8

A10

A2

A2

 

 

文档《X银行间联POS终端规范》中对各个字段已有详细描述,简单解释如下:

整个报文由TUDU+HEAD(报文头)+ISOPASS(加密信息)+ISO8583MSG(8583报文体00-64域)

签到(明文)格式: TPDU+HEAD+00-63域明文

交易(明文)格式: TPDU+HEAD+00-63域明文+64 (64域来源于对02-63域的ECB加密) 

如果想用明文方式发送报文,则需要先按照签到的明文格式进行签到,此时加密信息区域不需要传递,只需在发送交易时对02-63域进行ECB加密;

签到(密文)格式:TPDU+HEAD+ISOPASS+00-63域明文       

交易(密文)格式: TPDU+HEAD+ISOPASS+全报文加密(00-64域进行双倍加密处理后的结果) 

如果想用密文方式发送报文,则需要先按照签到的密文格式进行签到,这时要添加加密信息区域(注意报文长度一定是3个字节长度,若报文长度小于100时则第一位补零),

全报文加密是先对02-63域进行ECB处理得到64域,然后将00-64域(如果不是8的倍数,则在后面补零)进行双倍(3DES)加密得到整个报文加密后的结果。

2.2手工解包文示例

  下面以一段预授权返回报文进行解析,由于采用socket方式进行传输消息,故将其字节流转换为16进制可见文进行解析:

600000000660801001080231303332313035323930303534353130383338303030313638313430313030343232313238303046468e1ac77c445b27a7bc382059ece00931c60534c3cbd03055e980ee1f5ebcf7e344ba3a865e712bd2979ed0387c1d7bf62f8cb5b020a16b667473f5bde85ec080b6237335ee500a993acf095617aa8cb87cd1bfd58ce33b5bbc49f3dec793d1ccc2acddc73442f0aa

根据8583定义格式,TPDU长度为5个字节,报文头12个字节,用BCD码压缩后为6个字节长度,加密信息41个字节,剩下的就是报文体内容,由于1个字节可用2个16进制字符表示,故而得到以下解析内容

TPDU: 6000000006 (前十位)

HEAD: 608010010802  (紧接着12位)

ISOPASS(加密信息): 3130333231303532393030353435313038333830303031363831343031303034323231323830304646(紧接着82位)

报文体: 8e1ac77c445b27a7bc382059ece00931c60534c3cbd03055e980ee1f5ebcf7e344ba3a865e712bd2979ed0387c1d7bf62f8cb5b020a16b667473f5bde85ec080b6237335ee500a993acf095617aa8cb87cd1bfd58ce33b5bbc49f3dec793d1ccc2acddc73442f0aa

 

TPDU和HEAD一般都是固定值,就不再赘述;由于加密信息区域是ASCII值表示的,取出来的值根据ascii表转换得到原始报文内容,得到如下表格:

加密信息列

类型

响应值

转换后报文明文

含义

报文长度

A3

313033

103

整个报文体的原始报文长度为103,即对后面的报文双倍解密后前103个字符即为报文明文;

算法标识

A1

32

2

代表加密算法为“3des”

商户号

A15

313035323930303534353130383338

105290054510838

 

终端号

A8

3030303136383134

00016814

 

交易标识

A10

30313030343232313238

0100422128

0100为预授权,422128为发送过去的流水号(应与11域相同)

响应码

A2

3030

00

对方解密成功

保留

A2

4646

FF

 

 

首先对报文体进行3des解密(密钥=2afdbf46292a299e97da318f9d76e3ab)解密后结果为0110703800800ad0800316436745007172945903000000000000000142212809020901210635303231303939313830393039363030303136383134313035323930303534353130383338153031303530303030202020303030313135360000433342393531433200

剩下的就是按照文档标准对各个域进行解析;

消息类型:0110

位元素:703800800ad08003 (64位=8个字节=16个16进制字符)

转换为二进制后:1110000001110000000000010000000000010101101000010000000000000110,一共64位,第一位1代表域2有值,第二位为1代表域3有值,依次类推;

 

字段名称

属性

格式

类型

响应值

解析后明文

含义

域2

n..19

LLVAR

BCD

164367450071729459

4367450071729459

域2为变长的字符类型,前两个字符16代表该字段的长度,故截取后16位长度字符即为该字段的值;

域3

n6

 

BCD

030000

030000

 

域4

n12

 

BCD

000000000001

000000000001

 

域11

n6

 

BCD

422128

422128

 

域12

n6

hhmmss

BCD

90209

090209

 

域13

n4

MMDD

BCD

0121

0121

 

域25

n2

 

BCD

06

06

 

域37

an12

 

ASCII

353032313039393138303930

502109918090

定长12个字节,ASCII形式标示

域39

an2

 

ASCII

3936

96

 

域41

ans8

 

ASCII

3030303136383134

00016814

 

域42

ans15

 

ASCII

313035323930303534353130383338

105290054510838

 

域44

ans..25

LLVAR

ASCII

15303130353030303020202030303031

0105000   0001

变长ASCII形式,前两个数字代表长度

域49

an3

 

ASCII

313536

156

 

域63

ans…96

 

ASCII

0000

0

长度为0,63域返回值为空

域64

b64

 

BINARY

4333423935314332

C3B951C2

Mac校验值

 

关于pos终端的数据类型解释,请参考附录B

三、名词解释

3.1 BCD码

BCD码是用4位二进制码的组合代表十进制数的0,1,2,3,4,5,6,7,8,9 十个数符。

对应关系如下图表格所示:

十进制数

BCD码(8421)

0

0000

1

0001

2

0010

3

0011

4

0100

5

0101

6

0110

7

0111

8

1000

9

1001

示例: 对原文“113802”进行BCD转换

 

原文

1

1

3

8

0

2

原文六个字节

二进制

0000 0001

0000 0001

0000 0011

0000 1000

0000 0000

0000 0010

 

BCD码(8421)

0001

0001

          0011

          1000

          0000

          0010

 

十进制

17

     56

2

转换为BCD码后变成三个字节

十六进制

      11

      38

02

 

 

将字符串转换为BCD字节数组方法:

public static byte[]toBcd(String value) {

      int charpos =0;

      int bufpos =0;

      byte[] buf = null;

      int len =value.length() / 2;

      if (value.length()% 2 == 0) {

         buf = new byte[len];

      }

      if(value.length() % 2 == 1) {

         buf = new byte[len + 1];

         buf[0] = ((byte)(value.charAt(0) -'0'));

         charpos = 1;

         bufpos = 1;

      }

      while (charpos< value.length()) {

         buf[bufpos] = ((byte)(value.charAt(charpos) -'0' << 4 | value

                .charAt(charpos + 1) - '0'));

         charpos += 2;

         bufpos++;

 

      }

      return buf;

   }

3.2单倍长密钥算法、双倍长密钥算法和ECB算法

单倍长密钥算法 和双倍长密钥算法分别对应java里的des和3des加密算法;

DES加密算法

public static byte[] des(byte[]reqBytes,byte[] key)

         throwsInvalidKeyException, NoSuchAlgorithmException,

         NoSuchPaddingException,IllegalBlockSizeException,

         BadPaddingException,UnsupportedEncodingException,

         ShortBufferException {

      SecretKeySpec keySpec = null;

      DESKeySpec deskey = null;

      deskey = newDESKeySpec(key);

      keySpec = newSecretKeySpec(deskey.getKey(),"DES");

      Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");

      cipher.init(Cipher.ENCRYPT_MODE,keySpec);

      byte[]cipherText = new byte[cipher.getOutputSize(reqBytes.length)];

      cipherText = cipher.doFinal(reqBytes);

      returncipherText;

   }

DES解密算法

public static byte[]decryptByDes(byte[] reqBytes,byte[] key)

         throwsInvalidKeyException, NoSuchAlgorithmException,

         NoSuchPaddingException,IllegalBlockSizeException,

         BadPaddingException,UnsupportedEncodingException,

         ShortBufferException {

      SecretKeySpec keySpec = null;

      DESKeySpec deskey = null;

      deskey = newDESKeySpec(key);

      keySpec = newSecretKeySpec(deskey.getKey(),"DES");

 

      Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");

      cipher.init(Cipher.DECRYPT_MODE,keySpec);

 

      byte[]cipherText = new byte[cipher.getOutputSize(reqBytes.length)];

      cipherText = cipher.doFinal(reqBytes);

      returncipherText;

   }

3des加密算法:

private static final String DESede_ALG ="DESede/ECB/NOPADDING"; // 定义加密算法,可用DES,DESede,Blowfish

/**

    * 3DES加密

    *

    * @param src

    * @param key

    * @return

    */

   public static byte[]encryptDESede(byte[] src,byte[] key) {

      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

      try {

         Cipher c1 = Cipher.getInstance(DESede_ALG,"BC");

         c1.init(Cipher.ENCRYPT_MODE,newSecretKeySpec(key, DESede_ALG));

         returnc1.doFinal(src);

      } catch(Exception e) {

         logger.info("[3DES]双倍加密异常!Exception=[" +e.getMessage() +"]");

         e.printStackTrace();

      }

      return null;

   }

 

3des解密算法:

 

/**

    * 3DES解密

    *

    * @param src

    * @param key

    * @return

    */

   public static byte[]decryptDESede(byte[] src,byte[] key) {

      Security.addProvider(neworg.bouncycastle.jce.provider.BouncyCastleProvider());

      try {

         Cipher c1 = Cipher.getInstance(DESede_ALG,"BC");

         c1.init(Cipher.DECRYPT_MODE,newSecretKeySpec(key, DESede_ALG));

         returnc1.doFinal(src);

      } catch(Exception e) {

         logger.info("[3DES]双倍解密异常!Exception=[" +e.getMessage() +"]");

         e.printStackTrace();

      }

      return null;

   }

 

ECB加密算法:

 

/**

    * 建行Mac加密

    * @param msg

    * @param key

    * @return

    */

   public static StringccbEncrypt(byte[] macBytes,String key){    

      byte[]resultEncrypt=null;

      //签名字符串

      byte[]tmpBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

      //macBytes MAC原始字符串

      int times =macBytes.length/8;

      if(macBytes.length % 8 != 0) {

         times ++;

      }

      for (int t = 0; t<= times; t++) {

         for (int i = 0; i< 8; i++) {

            if ((t*8 +i) < macBytes.length) {

                tmpBytes[i] = (byte)(tmpBytes[i]^ macBytes[t*8 + i]);

            }

         }

      }

      System.out.println("b)MAC字符串=[" +byte2hex(macBytes)+ "],异或后["byte2hex(tmpBytes)+ "]");

      //前四个字节

      byte[] frontBytes= new byte[4];

      //后四个字节

      byte[]backBytes = new byte[4];

      System.arraycopy(tmpBytes, 0,frontBytes, 0, 4);

      System.arraycopy(tmpBytes, 4,backBytes, 0, 4);

      try {

         byte[]frontEncrypt =des(byte2hex(frontBytes).getBytes(), hex2byte(key));

         System.out.println("c,d)前半段加密后结果=[" +byte2hex(frontEncrypt)+ "]");

         byte[] tmp = byte2hex(backBytes).getBytes();

         for (int i = 0; i< 8; i++) {

            tmp[i] = (byte)(frontEncrypt[i]^ tmp[i]);

         }    

         System.out.println("e)异或后结果=[" +byte2hex(tmp)+ "]");

         resultEncrypt = des(tmp, hex2byte(key));

         System.out.println("f)加密结果=[" +byte2hex(resultEncrypt)+ "]");

      } catch(Exception e) {

         // TODOAuto-generated catch block

         e.printStackTrace();

      }

      byte[]macResult=newbyte[4];

      System.arraycopy(resultEncrypt, 0,macResult, 0, 4);

      System.out.println("g,h)ECB加密最终返回结果=[" +byte2hex(macResult)+ "]");

      return byte2hex(macResult);

   }

 

 

/**

     * byte array to hex

     *

     * @param b bytearray

     * @return hexstring

     * 字节数组转为16进制码

     */

    public static String byte2hex(byte[] b) {

        StringBuffer hs = newStringBuffer();

        String stmp;

        for (int i = 0; i< b.length; i++) {

            stmp = Integer.toHexString(b[i]& 0xFF).toUpperCase();

            if (stmp.length()== 1) {

                hs.append("0").append(stmp);

            } else {

                hs.append(stmp);

            }

        }

        returnhs.toString();

    }

四、附录

附录A:Socket报文设计

假设甲乙双方要通过Socket发送通信,双方约定了发送和接受的报文字段数不超过64,那么怎么设计才能达到所发送的报文长度最小的目的呢?

字段名称

类型

长度

备注

域1

变长字符

20

 

域2

定长字符

20

 

….

 

 

域64

数字

 

 

假设有一封报文需要发送

字段名称

类型

长度

域1

变长字符

20

123456

域2

定长字符

20

ABCDEF

..

 

 

域64

数字

 

369

 

方案一:不管某个字段是否有值,一律按照最大长度将其封装后发送,如果无值默认补空格或零,这样双方就能按照字段规定的长度来解析了。

 发送报文的值=123456ABCDEF……369;(省略号为补足空格)

方案二:如果字段需要发送才做报文封装,否则舍弃,在整个报文前端加上哪些字段被发送的标识,这样双方也能先通过解析域值封装信息来解析后面的报文。

 发送报文的值=c000000000000001123456ABCDE369;(前面16个字符转成二进制之后就能看到哪几个域被用到)

 c000000000000001 –>1100000000000000000000000000000000000000000000000000000000000001

由于要发送的字段数目不会超过64,所以只需要添加固定长度为8个字节(64位)来表示报文体中哪几个域被用到,改区域被称作”位元素”;

 

由上所述,方案一的报文长度为最大值,方案二的报文长度最简短,故方案二为最佳模式。

附录B:Pos终端数据类型

类型名称

备注

实例说明

A

字母向左靠,右部多余部分填空格。

 

AN

字母和/或数字,左靠,右部多余部分填空格。

 

 

ANS

 

字母和/或特殊符号,左靠,右部多余部分填空格。

 

B

二进制bit 位。

 

 

DD

 

 

hh

 

LL

可变长域的长度值(二位数)

前面两位数字代表长度,按照此长度截取该字段的值;

LLL

可变长域的长度值(三位数)

前面三位数字代表长度,按照此长度截取该字段的值;

MM

 

mm

 

N

数值,右靠,首位有效数字前充零。若表示金额,则最右二位为角分

 

S

特殊符号

 

ss

 

VAR

可变长域

 

X

借贷符号,在数值之前,D 表示借,C 表示贷

 

YY

 

Z

由ISO 7811 和ISO 7813 制定的磁条卡第二、三磁道的数据类型

 

CN

BCD 压缩编码数值

 

 

 

 

 

 

 

对可变长数据元,以下例说明:

—— 变量XYZ 的数据类型为ANS...999(LLLVAR),则表示:该变量中可含字母、数字和特殊符号,

最长不超过999 个字符,长度由三位数字确定。

—— 变量XYZ 的数据类型为N...999(LLLVAR),则在压缩时,其长度位用右靠的BCD 码压缩,而

其后紧随的数字内容用左靠的BCD 码压缩。这是为了保证有效内容和其位数中间无缺省填充

值。若不为偶数位,左靠的数字内容后补零。由于有长度位表征该域有效内容的长度,因此后

补零不会改变该域的真实值。

注: 本文档中声明的压缩变量属性是针对POS终端与POS中心之间的消息,POS中心与任何金融机构之间的消息将全

部采用ASCII码且不压缩的格式。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值