银联标准之MAC算法实现(POS终端加密)

本文详细讲解银联标准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
  在CODE上查看代码片 派生到我的代码片
  1. package com.yuyh.keydemo;  
  2.   
  3. /** 
  4.  * 银联标准Mac 算法 
  5.  */  
  6. public class MacEcbUtils {  
  7.   
  8.     public static void main(String[] args) {  
  9.         byte[] key = new byte[]{0x5C, (byte0xBE0x7E0x38, (byte0xA10x46, (byte0xFD0x5C};  
  10.         byte[] input = new byte[]{0x010x020x03};  
  11.         System.out.println(Utils.bcd2Str(getMac(key, input)));  
  12.     }  
  13.   
  14.     /** 
  15.      * mac计算 
  16.      * 
  17.      * @param key   mac秘钥 
  18.      * @param Input 待加密数据 
  19.      * @return 
  20.      */  
  21.     public static byte[] getMac(byte[] key, byte[] Input) {  
  22.         int length = Input.length;  
  23.         int x = length % 8;  
  24.         // 需要补位的长度  
  25.         int addLen = 0;  
  26.         if (x != 0) {  
  27.             addLen = 8 - length % 8;  
  28.         }  
  29.         int pos = 0;  
  30.         // 原始数据补位后的数据  
  31.         byte[] data = new byte[length + addLen];  
  32.         System.arraycopy(Input, 0, data, 0, length);  
  33.         byte[] oper1 = new byte[8];  
  34.         System.arraycopy(data, pos, oper1, 08);  
  35.         pos += 8;  
  36.         // 8字节异或  
  37.         for (int i = 1; i < data.length / 8; i++) {  
  38.             byte[] oper2 = new byte[8];  
  39.             System.arraycopy(data, pos, oper2, 08);  
  40.             byte[] t = bytesXOR(oper1, oper2);  
  41.             oper1 = t;  
  42.             pos += 8;  
  43.         }  
  44.         // 将异或运算后的最后8个字节(RESULT BLOCK)转换成16个HEXDECIMAL:  
  45.         byte[] resultBlock = bytesToHexString(oper1).getBytes();  
  46.         // 取前8个字节MAK加密  
  47.         byte[] front8 = new byte[8];  
  48.         System.arraycopy(resultBlock, 0, front8, 08);  
  49.         byte[] behind8 = new byte[8];  
  50.         System.arraycopy(resultBlock, 8, behind8, 08);  
  51.         byte[] desfront8 = DesUtils.encrypt(front8, key);  
  52.         // 将加密后的结果与后8 个字节异或:  
  53.         byte[] resultXOR = bytesXOR(desfront8, behind8);  
  54.         // 用异或的结果TEMP BLOCK 再进行一次单倍长密钥算法运算  
  55.         byte[] buff = DesUtils.encrypt(resultXOR, key);  
  56.         // 将运算后的结果(ENC BLOCK2)转换成16 个HEXDECIMAL asc  
  57.         byte[] retBuf = new byte[8];  
  58.         // 取8个长度字节就是mac值  
  59.         System.arraycopy(bytesToHexString(buff).getBytes(), 0, retBuf, 08);  
  60.         return retBuf;  
  61.     }  
  62.   
  63.     /** 
  64.      * 单字节异或 
  65.      * 
  66.      * @param src1 
  67.      * @param src2 
  68.      * @return 
  69.      */  
  70.     public static byte byteXOR(byte src1, byte src2) {  
  71.         return (byte) ((src1 & 0xFF) ^ (src2 & 0xFF));  
  72.     }  
  73.   
  74.     /** 
  75.      * 字节数组异或 
  76.      * 
  77.      * @param src1 
  78.      * @param src2 
  79.      * @return 
  80.      */  
  81.     public static byte[] bytesXOR(byte[] src1, byte[] src2) {  
  82.         int length = src1.length;  
  83.         if (length != src2.length) {  
  84.             return null;  
  85.         }  
  86.         byte[] result = new byte[length];  
  87.         for (int i = 0; i < length; i++) {  
  88.             result[i] = byteXOR(src1[i], src2[i]);  
  89.         }  
  90.         return result;  
  91.     }  
  92.   
  93.     /** 
  94.      * 字节数组转HEXDECIMAL 
  95.      * 
  96.      * @param bArray 
  97.      * @return 
  98.      */  
  99.     public static final String bytesToHexString(byte[] bArray) {  
  100.         StringBuffer sb = new StringBuffer(bArray.length);  
  101.         String sTemp;  
  102.         for (int i = 0; i < bArray.length; i++) {  
  103.             sTemp = Integer.toHexString(0xFF & bArray[i]);  
  104.             if (sTemp.length() < 2)  
  105.                 sb.append(0);  
  106.             sb.append(sTemp.toUpperCase());  
  107.         }  
  108.         return sb.toString();  
  109.     }  
  110. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.yuyh.keydemo;  
  2.   
  3. import java.security.NoSuchAlgorithmException;  
  4. import java.security.SecureRandom;  
  5.   
  6. import javax.crypto.Cipher;  
  7. import javax.crypto.KeyGenerator;  
  8. import javax.crypto.SecretKey;  
  9. import javax.crypto.SecretKeyFactory;  
  10. import javax.crypto.spec.DESKeySpec;  
  11.   
  12. /** 
  13.  * DES 加解密 
  14.  */  
  15. public class DesUtils {  
  16.   
  17.     private final static String DES = "DES";  
  18.     private final static String CIPHER_ALGORITHM = "DES/ECB/NoPadding";  
  19.   
  20.     /** 
  21.      * 加密 
  22.      * 
  23.      * @param src 数据源 
  24.      * @param key 密钥,长度必须是8的倍数 
  25.      * @return 返回加密后的数据 
  26.      */  
  27.     public static byte[] encrypt(byte[] src, byte[] key) {  
  28.         SecureRandom sr = new SecureRandom();  
  29.         try {  
  30.             DESKeySpec dks = new DESKeySpec(key);  
  31.             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);  
  32.             SecretKey securekey = keyFactory.generateSecret(dks);  
  33.             Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);  
  34.             cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);  
  35.             return cipher.doFinal(src);  
  36.         } catch (Exception e) {  
  37.         }  
  38.         return null;  
  39.     }  
  40.   
  41.     /** 
  42.      * 生成密钥 
  43.      * 
  44.      * @return 
  45.      * @throws NoSuchAlgorithmException 
  46.      */  
  47.     public static byte[] initKey() throws NoSuchAlgorithmException {  
  48.         KeyGenerator kg = KeyGenerator.getInstance(DES);  
  49.         kg.init(16);  
  50.         SecretKey secretKey = kg.generateKey();  
  51.         return secretKey.getEncoded();  
  52.     }  
  53.   
  54.     /** 
  55.      * 解密 
  56.      * 
  57.      * @param src 数据源 
  58.      * @param key 密钥,长度必须是8的倍数 
  59.      * @return 返回解密后的原始数据 
  60.      */  
  61.     public static byte[] decrypt(byte[] src, byte[] key) {  
  62.         SecureRandom sr = new SecureRandom();  
  63.         try {  
  64.             DESKeySpec dks = new DESKeySpec(key);  
  65.             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);  
  66.             SecretKey securekey = keyFactory.generateSecret(dks);  
  67.             Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);  
  68.             cipher.init(Cipher.DECRYPT_MODE, securekey, sr);  
  69.             return cipher.doFinal(src);  
  70.         } catch (Exception e) {  
  71.         }  
  72.         return null;  
  73.     }  
  74.   
  75. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.yuyh.keydemo;  
  2.   
  3. /** 
  4.  * bcd码 与 String 转化 
  5.  */  
  6. public class Utils {  
  7.   
  8.     public static String bcd2Str(byte[] b) {  
  9.         char[] HEX_DIGITS = new char[]{'0''1''2''3''4''5''6''7''8''9''A''B''C''D''E''F'};  
  10.         StringBuilder sb = new StringBuilder(b.length * 2);  
  11.   
  12.         for (int i = 0; i < b.length; ++i) {  
  13.             sb.append(HEX_DIGITS[(b[i] & 240) >>> 4]);  
  14.             sb.append(HEX_DIGITS[b[i] & 15]);  
  15.         }  
  16.   
  17.         return sb.toString();  
  18.     }  
  19.   
  20.     public static byte[] str2Bcd(String asc) {  
  21.         int len = asc.length();  
  22.         int mod = len % 2;  
  23.         if (mod != 0) {  
  24.             asc = "0" + asc;  
  25.             len = asc.length();  
  26.         }  
  27.   
  28.         byte[] abt = new byte[len];  
  29.         if (len >= 2) {  
  30.             len /= 2;  
  31.         }  
  32.   
  33.         byte[] bbt = new byte[len];  
  34.         abt = asc.getBytes();  
  35.   
  36.         for (int p = 0; p < asc.length() / 2; ++p) {  
  37.             int j;  
  38.             if (abt[2 * p] >= 97 && abt[2 * p] <= 122) {  
  39.                 j = abt[2 * p] - 97 + 10;  
  40.             } else if (abt[2 * p] >= 65 && abt[2 * p] <= 90) {  
  41.                 j = abt[2 * p] - 65 + 10;  
  42.             } else {  
  43.                 j = abt[2 * p] - 48;  
  44.             }  
  45.   
  46.             int k;  
  47.             if (abt[2 * p + 1] >= 97 && abt[2 * p + 1] <= 122) {  
  48.                 k = abt[2 * p + 1] - 97 + 10;  
  49.             } else if (abt[2 * p + 1] >= 65 && abt[2 * p + 1] <= 90) {  
  50.                 k = abt[2 * p + 1] - 65 + 10;  
  51.             } else {  
  52.                 k = abt[2 * p + 1] - 48;  
  53.             }  
  54.   
  55.             int a = (j << 4) + k;  
  56.             byte b = (byte) a;  
  57.             bbt[p] = b;  
  58.         }  
  59.         return bbt;  
  60.     }  
  61. }  
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jsoh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值