银联标准之MAC算法实现(POS终端加密)
2017-04-13 09:49
661 查看
本文详细讲解银联标准MAC算法的过程,以及通过Java代码来实现这一运算过程。
POS终端采用ECB的加密方式,简述如下:
1、将欲发送给POS中心的消息中,从消息类型(MTI)到63域之间的部分构成MAC ELEMEMENT BLOCK (MAB)
2、对MAB,按每8个字节做异或(不管信息中的字符格式),如果最后不满8个字节,则添加“0x00”
下面举个例子来说明MAC算法的过程:
MAB = M1 M2 M3 M4 (假设MAB有M1,M2,M3,M4这四块构成,每块8字节)
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
1、按如下规则进行异或运算(每8个字节进行异或最后得到8字节的结果)
(1)MS11 MS12 MS13 MS14 MS15 MS16 MS17 MS18 (xor) MS21 MS22 MS23 MS24 MS25 MS26 MS27 MS28
= TM11 TM12 TM13 TM14 TM15 TM16 TM17 TM18
(2)TM11 TM12 TM13 TM14 TM15 TM16 TM17 TM18 (xor)MS31 MS32 MS33 MS34 MS35 MS36 MS37 MS38
= TM21 TM22 TM23 TM24 TM25 TM26 TM27 TM28
(3)TM21 TM22 TM23 TM24 TM25 TM26 TM27 TM28(xor)MS41 MS42 MS43 MS44 MS45 MS46 MS47 MS48
= TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38
2、最后我们可以得到TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38这8个字节,转换成16 个HEXDECIMAL
TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38
==> TM311 TM312 TM321 TM322 TM331 TM332 TM341 TM342 TM351 TM352 TM361 TM362 TM371 TM372 TM381 TM382
3、然后取这16 个HEXDECIMAL的前8个字节,用MAK进行DES加密(或者3DES加密)
eMAK(TM311 TM312 TM321 TM322 TM331 TM332 TM341 TM342)
= EN11 EN12 EN13 EN14 EN15 EN16 EN17 EN18
4、将加密后的结果与6 个HEXDECIMAL的后8个字节进行异或运算
EN11 EN12 EN13 EN14 EN15 EN16 EN17 EN18 (xor) TM351 TM352 TM361 TM362 TM371 TM372 TM381 TM382
= TE11 TE12 TE13 TE14 TE15 TE16 TE17 TE18
5、再将异或的结果进行一次单倍长的秘钥算法运算
eMAK(TE11 TE12 TE13 TE14 TE15 TE16 TE17 TE18)
= EN21 EN22 EN23 EN24 EN25 EN26 EN27 EN28
6、然后将加密运算后的结果,转换成16 个HEXDECIMAL
EN21 EN22 EN23 EN24 EN25 EN26 EN27 EN28
==> EM211 EM212 EM221 EM222 EM231 EM232 EM241 EM242 EM251 EM252 EM261 EM262 EM271 EM272 EM281 EM282
7、最后,取16 个HEXDECIMAL的前8个字节,就是MAC值。
result = EM211 EM212 EM221 EM222 EM231 EM232 EM241 EM242
8、Java代码实现Mac算法过程如下:
[java] view
plain copy
package com.yuyh.keydemo;
/**
* 银联标准Mac 算法
*/
public class MacEcbUtils {
public static void main(String[] args) {
byte[] key = new byte[]{0x5C, (byte) 0xBE, 0x7E, 0x38, (byte) 0xA1, 0x46, (byte) 0xFD, 0x5C};
byte[] input = new byte[]{0x01, 0x02, 0x03};
System.out.println(Utils.bcd2Str(getMac(key, input)));
}
/**
* mac计算
*
* @param key mac秘钥
* @param Input 待加密数据
* @return
*/
public static byte[] getMac(byte[] key, byte[] Input) {
int length = Input.length;
int x = length % 8;
// 需要补位的长度
int addLen = 0;
if (x != 0) {
addLen = 8 - length % 8;
}
int pos = 0;
// 原始数据补位后的数据
byte[] data = new byte[length + addLen];
System.arraycopy(Input, 0, data, 0, length);
byte[] oper1 = new byte[8];
System.arraycopy(data, pos, oper1, 0, 8);
pos += 8;
// 8字节异或
for (int i = 1; i < data.length / 8; i++) {
byte[] oper2 = new byte[8];
System.arraycopy(data, pos, oper2, 0, 8);
byte[] t = bytesXOR(oper1, oper2);
oper1 = t;
pos += 8;
}
// 将异或运算后的最后8个字节(RESULT BLOCK)转换成16个HEXDECIMAL:
byte[] resultBlock = bytesToHexString(oper1).getBytes();
// 取前8个字节MAK加密
byte[] front8 = new byte[8];
System.arraycopy(resultBlock, 0, front8, 0, 8);
byte[] behind8 = new byte[8];
System.arraycopy(resultBlock, 8, behind8, 0, 8);
byte[] desfront8 = DesUtils.encrypt(front8, key);
// 将加密后的结果与后8 个字节异或:
byte[] resultXOR = bytesXOR(desfront8, behind8);
// 用异或的结果TEMP BLOCK 再进行一次单倍长密钥算法运算
byte[] buff = DesUtils.encrypt(resultXOR, key);
// 将运算后的结果(ENC BLOCK2)转换成16 个HEXDECIMAL asc
byte[] retBuf = new byte[8];
// 取8个长度字节就是mac值
System.arraycopy(bytesToHexString(buff).getBytes(), 0, retBuf, 0, 8);
return retBuf;
}
/**
* 单字节异或
*
* @param src1
* @param src2
* @return
*/
public static byte byteXOR(byte src1, byte src2) {
return (byte) ((src1 & 0xFF) ^ (src2 & 0xFF));
}
/**
* 字节数组异或
*
* @param src1
* @param src2
* @return
*/
public static byte[] bytesXOR(byte[] src1, byte[] src2) {
int length = src1.length;
if (length != src2.length) {
return null;
}
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
result[i] = byteXOR(src1[i], src2[i]);
}
return result;
}
/**
* 字节数组转HEXDECIMAL
*
* @param bArray
* @return
*/
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2)
sb.append(0);
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
[java] view
plain copy
package com.yuyh.keydemo;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* DES 加解密
*/
public class DesUtils {
private final static String DES = "DES";
private final static String CIPHER_ALGORITHM = "DES/ECB/NoPadding";
/**
* 加密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回加密后的数据
*/
public static byte[] encrypt(byte[] src, byte[] key) {
SecureRandom sr = new SecureRandom();
try {
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
return cipher.doFinal(src);
} catch (Exception e) {
}
return null;
}
/**
* 生成密钥
*
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] initKey() throws NoSuchAlgorithmException {
KeyGenerator kg = KeyGenerator.getInstance(DES);
kg.init(16);
SecretKey secretKey = kg.generateKey();
return secretKey.getEncoded();
}
/**
* 解密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回解密后的原始数据
*/
public static byte[] decrypt(byte[] src, byte[] key) {
SecureRandom sr = new SecureRandom();
try {
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
return cipher.doFinal(src);
} catch (Exception e) {
}
return null;
}
}
[java] view
plain copy
package com.yuyh.keydemo;
/**
* bcd码 与 String 转化
*/
public class Utils {
public static String bcd2Str(byte[] b) {
char[] HEX_DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; ++i) {
sb.append(HEX_DIGITS[(b[i] & 240) >>> 4]);
sb.append(HEX_DIGITS[b[i] & 15]);
}
return sb.toString();
}
public static byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}
byte[] abt = new byte[len];
if (len >= 2) {
len /= 2;
}
byte[] bbt = new byte[len];
abt = asc.getBytes();
for (int p = 0; p < asc.length() / 2; ++p) {
int j;
if (abt[2 * p] >= 97 && abt[2 * p] <= 122) {
j = abt[2 * p] - 97 + 10;
} else if (abt[2 * p] >= 65 && abt[2 * p] <= 90) {
j = abt[2 * p] - 65 + 10;
} else {
j = abt[2 * p] - 48;
}
int k;
if (abt[2 * p + 1] >= 97 && abt[2 * p + 1] <= 122) {
k = abt[2 * p + 1] - 97 + 10;
} else if (abt[2 * p + 1] >= 65 && abt[2 * p + 1] <= 90) {
k = abt[2 * p + 1] - 65 + 10;
} else {
k = abt[2 * p + 1] - 48;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
}
POS终端采用ECB的加密方式,简述如下:
1、将欲发送给POS中心的消息中,从消息类型(MTI)到63域之间的部分构成MAC ELEMEMENT BLOCK (MAB)
2、对MAB,按每8个字节做异或(不管信息中的字符格式),如果最后不满8个字节,则添加“0x00”
下面举个例子来说明MAC算法的过程:
MAB = M1 M2 M3 M4 (假设MAB有M1,M2,M3,M4这四块构成,每块8字节)
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
1、按如下规则进行异或运算(每8个字节进行异或最后得到8字节的结果)
(1)MS11 MS12 MS13 MS14 MS15 MS16 MS17 MS18 (xor) MS21 MS22 MS23 MS24 MS25 MS26 MS27 MS28
= TM11 TM12 TM13 TM14 TM15 TM16 TM17 TM18
(2)TM11 TM12 TM13 TM14 TM15 TM16 TM17 TM18 (xor)MS31 MS32 MS33 MS34 MS35 MS36 MS37 MS38
= TM21 TM22 TM23 TM24 TM25 TM26 TM27 TM28
(3)TM21 TM22 TM23 TM24 TM25 TM26 TM27 TM28(xor)MS41 MS42 MS43 MS44 MS45 MS46 MS47 MS48
= TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38
2、最后我们可以得到TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38这8个字节,转换成16 个HEXDECIMAL
TM31 TM32 TM33 TM34 TM35 TM36 TM37 TM38
==> TM311 TM312 TM321 TM322 TM331 TM332 TM341 TM342 TM351 TM352 TM361 TM362 TM371 TM372 TM381 TM382
3、然后取这16 个HEXDECIMAL的前8个字节,用MAK进行DES加密(或者3DES加密)
eMAK(TM311 TM312 TM321 TM322 TM331 TM332 TM341 TM342)
= EN11 EN12 EN13 EN14 EN15 EN16 EN17 EN18
4、将加密后的结果与6 个HEXDECIMAL的后8个字节进行异或运算
EN11 EN12 EN13 EN14 EN15 EN16 EN17 EN18 (xor) TM351 TM352 TM361 TM362 TM371 TM372 TM381 TM382
= TE11 TE12 TE13 TE14 TE15 TE16 TE17 TE18
5、再将异或的结果进行一次单倍长的秘钥算法运算
eMAK(TE11 TE12 TE13 TE14 TE15 TE16 TE17 TE18)
= EN21 EN22 EN23 EN24 EN25 EN26 EN27 EN28
6、然后将加密运算后的结果,转换成16 个HEXDECIMAL
EN21 EN22 EN23 EN24 EN25 EN26 EN27 EN28
==> EM211 EM212 EM221 EM222 EM231 EM232 EM241 EM242 EM251 EM252 EM261 EM262 EM271 EM272 EM281 EM282
7、最后,取16 个HEXDECIMAL的前8个字节,就是MAC值。
result = EM211 EM212 EM221 EM222 EM231 EM232 EM241 EM242
8、Java代码实现Mac算法过程如下:
[java] view
plain copy
package com.yuyh.keydemo;
/**
* 银联标准Mac 算法
*/
public class MacEcbUtils {
public static void main(String[] args) {
byte[] key = new byte[]{0x5C, (byte) 0xBE, 0x7E, 0x38, (byte) 0xA1, 0x46, (byte) 0xFD, 0x5C};
byte[] input = new byte[]{0x01, 0x02, 0x03};
System.out.println(Utils.bcd2Str(getMac(key, input)));
}
/**
* mac计算
*
* @param key mac秘钥
* @param Input 待加密数据
* @return
*/
public static byte[] getMac(byte[] key, byte[] Input) {
int length = Input.length;
int x = length % 8;
// 需要补位的长度
int addLen = 0;
if (x != 0) {
addLen = 8 - length % 8;
}
int pos = 0;
// 原始数据补位后的数据
byte[] data = new byte[length + addLen];
System.arraycopy(Input, 0, data, 0, length);
byte[] oper1 = new byte[8];
System.arraycopy(data, pos, oper1, 0, 8);
pos += 8;
// 8字节异或
for (int i = 1; i < data.length / 8; i++) {
byte[] oper2 = new byte[8];
System.arraycopy(data, pos, oper2, 0, 8);
byte[] t = bytesXOR(oper1, oper2);
oper1 = t;
pos += 8;
}
// 将异或运算后的最后8个字节(RESULT BLOCK)转换成16个HEXDECIMAL:
byte[] resultBlock = bytesToHexString(oper1).getBytes();
// 取前8个字节MAK加密
byte[] front8 = new byte[8];
System.arraycopy(resultBlock, 0, front8, 0, 8);
byte[] behind8 = new byte[8];
System.arraycopy(resultBlock, 8, behind8, 0, 8);
byte[] desfront8 = DesUtils.encrypt(front8, key);
// 将加密后的结果与后8 个字节异或:
byte[] resultXOR = bytesXOR(desfront8, behind8);
// 用异或的结果TEMP BLOCK 再进行一次单倍长密钥算法运算
byte[] buff = DesUtils.encrypt(resultXOR, key);
// 将运算后的结果(ENC BLOCK2)转换成16 个HEXDECIMAL asc
byte[] retBuf = new byte[8];
// 取8个长度字节就是mac值
System.arraycopy(bytesToHexString(buff).getBytes(), 0, retBuf, 0, 8);
return retBuf;
}
/**
* 单字节异或
*
* @param src1
* @param src2
* @return
*/
public static byte byteXOR(byte src1, byte src2) {
return (byte) ((src1 & 0xFF) ^ (src2 & 0xFF));
}
/**
* 字节数组异或
*
* @param src1
* @param src2
* @return
*/
public static byte[] bytesXOR(byte[] src1, byte[] src2) {
int length = src1.length;
if (length != src2.length) {
return null;
}
byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
result[i] = byteXOR(src1[i], src2[i]);
}
return result;
}
/**
* 字节数组转HEXDECIMAL
*
* @param bArray
* @return
*/
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2)
sb.append(0);
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
[java] view
plain copy
package com.yuyh.keydemo;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* DES 加解密
*/
public class DesUtils {
private final static String DES = "DES";
private final static String CIPHER_ALGORITHM = "DES/ECB/NoPadding";
/**
* 加密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回加密后的数据
*/
public static byte[] encrypt(byte[] src, byte[] key) {
SecureRandom sr = new SecureRandom();
try {
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
return cipher.doFinal(src);
} catch (Exception e) {
}
return null;
}
/**
* 生成密钥
*
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] initKey() throws NoSuchAlgorithmException {
KeyGenerator kg = KeyGenerator.getInstance(DES);
kg.init(16);
SecretKey secretKey = kg.generateKey();
return secretKey.getEncoded();
}
/**
* 解密
*
* @param src 数据源
* @param key 密钥,长度必须是8的倍数
* @return 返回解密后的原始数据
*/
public static byte[] decrypt(byte[] src, byte[] key) {
SecureRandom sr = new SecureRandom();
try {
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey securekey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
return cipher.doFinal(src);
} catch (Exception e) {
}
return null;
}
}
[java] view
plain copy
package com.yuyh.keydemo;
/**
* bcd码 与 String 转化
*/
public class Utils {
public static String bcd2Str(byte[] b) {
char[] HEX_DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; ++i) {
sb.append(HEX_DIGITS[(b[i] & 240) >>> 4]);
sb.append(HEX_DIGITS[b[i] & 15]);
}
return sb.toString();
}
public static byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}
byte[] abt = new byte[len];
if (len >= 2) {
len /= 2;
}
byte[] bbt = new byte[len];
abt = asc.getBytes();
for (int p = 0; p < asc.length() / 2; ++p) {
int j;
if (abt[2 * p] >= 97 && abt[2 * p] <= 122) {
j = abt[2 * p] - 97 + 10;
} else if (abt[2 * p] >= 65 && abt[2 * p] <= 90) {
j = abt[2 * p] - 65 + 10;
} else {
j = abt[2 * p] - 48;
}
int k;
if (abt[2 * p + 1] >= 97 && abt[2 * p + 1] <= 122) {
k = abt[2 * p + 1] - 97 + 10;
} else if (abt[2 * p + 1] >= 65 && abt[2 * p + 1] <= 90) {
k = abt[2 * p + 1] - 65 + 10;
} else {
k = abt[2 * p + 1] - 48;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
}
相关文章推荐
- 银联标准之MAC算法实现(POS终端加密)
- 银联标准之MAC算法实现(POS终端加密)
- 转:标准银联POS加解密、及MAC算法
- POS终端MAC算法-C语言实现
- 传统POS/终端/银联POS简介
- POS终端MAC算法-C语言实现 (转贴)
- 银联Pos终端签到、签退、批结算、批上送、PinKey、MacKey、KEK、主密钥、工作密钥、TPDU、报文头
- 银联POS标准中的BCD压缩
- 银联Pos终端签到、签退、批结算、批上送
- PDA移动POS终端系统,实现专柜或店铺的收货、零售、盘点通过无线网络直接连接总部中央数据库,实现高效安全的移动供应链管理
- 银联Pos终端签到、签退、批结算、批上送、PinKey、MacKey、KEK、主密钥、工作密钥、TPDU、报文头
- 银联Pos终端签到、签退、批结算、批上送、PinKey、MacKey、KEK、主密钥、工作密钥
- POS终端MAC算法-C语言实现
- 银联Iso8583报文 POS终端报文结构
- 任何国家都无法限制数字货币。为什么呢? 要想明白这个问题需要具备一点区块链的基础知识: 区块链使用的大致技术包括以下几种: a.点对点网络设计 b.加密技术应用 c.分布式算法的实现 d.数据存储技术 e.拜占庭算法 f.权益证明POW,POS,DPOS 原因一: 点对点网络设计 其中点对点的P2P网络是bittorent ,由于是点对点的网络,没有中心化,因此在全球分布式的网
- 【前端Js】高级加密解密标准AES加密(Javascript代码实现)
- 任何国家都无法限制数字货币。为什么呢? 要想明白这个问题需要具备一点区块链的基础知识: 区块链使用的大致技术包括以下几种: a.点对点网络设计 b.加密技术应用 c.分布式算法的实现 d.数据存储技术 e.拜占庭算法 f.权益证明POW,POS,DPOS 原因一: 点对点网络设计 其中点对点的P2P网络是bittorent ,由于是点对点的网络,没有中心化,因此在全球分布式的网
- Java实现3DES加密--及ANSI X9.8 Format标准 PIN PAN获取PIN BlOCK
- 银联-POS终端-密钥卡-规范-TMK-方法其一
- POS终端MAC算法-C语言实现