銀聯標準之MAC演算法實現(POS終端加密)

LeBron_Six發表於2016-06-24

轉載請註明出處:http://blog.csdn.net/yyh352091626/article/details/51751120


本文詳細講解銀聯標準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演算法過程如下:

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();
    }
}

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;
    }

}

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;
    }
}


相關文章