本文根据《中国银联直联pos终端规范》的阐述,用c语言编程实现,该算法在实际的商业pos终端中使用。pos终端采用ECB的加密方式,简述如下:
a) 将欲发送给pos中心的消息中,从消息类型(mti)到63域之间的部分构成mac
elemement block (mab)。
b) 对mab,按每8个字节做异或(不管信息中的字符格式),如果最后不满8个字节,则添加“0x00”。
示例 :
mab = m1 m2 m3 m4
其中:
m1 = ms11 ms12 ms13 ms14 ms15 ms16 ms17 ms18
m2 = ms21 ms22 ms23 ms24 ms25 ms26 ms27 ms28
m3 = ms31 ms32 ms33 ms34 ms35 ms36 ms37 ms38
m4 = ms41 ms42 ms43 ms44 ms45 ms46 ms47 ms48
按如下规则进行异或运算:
ms11 ms12 ms13 ms14 ms15 ms16 ms17 ms18
xor) ms21 ms22 ms23 ms24 ms25 ms26 ms27 ms28
---------------------------------------------------
temp block1 = tm11 tm12 tm13 tm14 tm15 tm16 tm17 tm18
然后,进行下一步的运算:
tm11 tm12 tm13 tm14 tm15 tm16 tm17 tm18
xor) ms31 ms32 ms33 ms34 ms35 ms36 ms37 ms38
temp block2 = tm21 tm22 tm23 tm24 tm25 tm26 tm27 tm28
再进行下一步的运算:
tm21 tm22 tm23 tm24 tm25 tm26 tm27 tm28
xor) ms41 ms42 ms43 ms44 ms45 ms46 ms47 ms48
result block = tm31 tm32 tm33 tm34 tm35 tm36 tm37 tm38
c) 将异或运算后的最后8个字节(result block)转换成16 个hexdecimal:
= tm311 tm312 tm321 tm322 tm331 tm332 tm341 tm342 ||
tm351 tm352 tm361 tm362 tm371 tm372 tm381 tm382
d) 取前8 个字节用mak加密:
enc block1 = emak(tm311 tm312 tm321 tm322 tm331 tm332 tm341 tm342)
= en11 en12 en13 en14 en15 en16 en17 en18
e) 将加密后的结果与后8 个字节异或:
en11 en12 en13 en14 en15 en16 en17 en18
xor) tm351 tm352 tm361 tm362 tm371 tm372 tm381 tm382
------------------------------------------------------------
temp block= te11 te12 te13 te14 te15 te16 te17 te18
f) 用异或的结果temp block 再进行一次单倍长密钥算法运算。
enc block2 = emak(te11 te12 te13 te14 te15 te16 te17 te18)
= en21 en22 en23 en24 en25 en26 en27 en28
g) 将运算后的结果(enc block2)转换成16 个hexdecimal:
enc block2 = en21 en22 en23 en24 en25 en26 en27 en28
= em211 em212 em221 em222 em231 em232 em241 em242 ||
em251 em252 em261 em262 em271 em272 em281 em282
enc result= %h84, %h56, %hb1, %hcd, %h5a, %h3f, %h84, %h84
转换成16 个hexdecimal:
“8456b1cd5a3f8484”
h) 取前8个字节作为mac值。
取”8456b1cd”为mac值。

数据报文:
0x 1234567890abcdefabcdef1234567890 //$body
mak:2222222222222222
mac计算:
m1 = 0x 1234567890abcdef
m2 = 0x abcdef1234567890
m1 xor m2 结果: 0x b9f9b96aa4fdb57f
扩展成16字节数据:0x 42394639423936414134464442353746
mak加密前半部分数据结果:0x 9fde90a34cf73b2e
加密结果与后半部分数据异或,结果:0x deead6e70ec20c68
mak加密异或结果:0x e267b6e21913d339
扩展成16字节数据:0x45323637423645323139313344333339
mac:e267b6e2
php mac

public function splitdata($hexmacdatasource, $num=16)
{
$len = 0;
$modvalue = strlen($hexmacdatasource) % $num;
if($modvalue != 0)
{
$hexsrcdatalen = strlen($hexmacdatasource);
$totallen = $hexsrcdatalen + ($num - $modvalue);
$hexmacdatasource = str_pad($hexmacdatasource, $totallen, "0");//16进制右补0
}
$len = strlen($hexmacdatasource) / $num;
$ds = array();
for ($i = 0; $i < $len; $i++)
if (strlen($hexmacdatasource) >= $num)
{
$ds[] = substr($hexmacdatasource,0, $num);
$hexmacdatasource = substr($hexmacdatasource,$num);
} else
throw new exception("填充的数据非法!",6008);
}
return $ds;
}
public function hexxor($hexstr1 , $hexstr2)
return str_pad(strtoupper(gmp_strval(gmp_xor(gmp_init($hexstr1, 16),gmp_init($hexstr2, 16)), 16)),16,'0',str_pad_left);
public function getbodymac($body)
//拆分mac数据源,每组16位hex(8 byte())
$ds = $this->splitdata($body);
$des = "";
for ($i = 0; $i < count($ds); $i++)
if ($i == 0)
$des = $ds[$i];//$ds[$i] 是16进制数
$des = $this->hexxor($des, $ds[$i]);
#异或结果扩展成 16字节 的 hex
$hexstr16 = $this->encodehex($des);
#加密前8字节数据
$left8 = substr($hexstr16,0,16);
$right8 = substr($hexstr16,16,16);
$tmpxor = $this->hexxor($this->encryptbydes($left8,$this->mackey), $right8);
$bodymac = substr($this->encryptbydes($tmpxor,$this->mackey),0,8);
return $bodymac;
public function encodehex($str)
return strtoupper(bin2hex($str));
#开始4个字节报文长度
$requestbodylength = intval(trim(substr($request, 0, 4)));
$body = substr($request, 4, $requestbodylength);
#报文体
$requestbody = substr($body, 0, $requestbodylength - 8);
#最后8个字节为mac
$requestmac = substr($body, -8);