PBOC規範計算MAC在Android上的使用

近期,公司安排了一個新的功能,需要對內容進行簽名,文檔上面的文字就是:

  • 按PBOC规范计算MAC,初始向量为8字节0x00

之前並沒有用過這種方式計算簽名值,立馬百度搜索,確定了,這個計算mac,其實有兩種,後來後台的同事提供軟件以後發現,他們的簽名方法其實就是:PBOC3DESMAC,大家如果有別的疑問可以直接用這個關鍵字去搜索,就有很多文章了

我得也就是根據網上搜索的,然後對立面錯誤的代碼進行修改,得到了,在Android系統上,契合這次任務使用的代碼,如果有需要,直接複製黏貼,拿去用就行,假如幫不了你,那也只能抱歉了

下面直接來乾貨:

public class Pboc3DesMACUtils {
    /** 置换选择1的矩阵 */
    private static int[] iSelePM1 = {
            57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43,
            35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7,
            62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28,
            20, 12, 4};

    private static int[] iSelePM2 = { // 置换选择2的矩阵
            14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7,
            27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44,
            49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32};

    private static int[] iROLtime = { // 循环左移位数表
            1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};

    private static int[] iInitPM = { // 初始置换IP
            58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46,
            38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33,
            25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29,
            21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7};

    private static int[] iInvInitPM = { // 初始逆置换
            40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46,
            14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12,
            52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50,
            18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25};

    private static int[] iEPM = { // 选择运算E
            32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15,
            16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26,
            27, 28, 29, 28, 29, 30, 31, 32, 1};

    private static int[] iPPM = { // 置换运算P
            16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14,
            32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25};
    // 8个S盒

    private static int[][] iSPM = new int[8][];

    public void Mac3Encrypt() {
        iSPM[0] = new int[]{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7,
                4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8,
                13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4,
                9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13};

        iSPM[1] = new int[]{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4,
                7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11,
                10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3,
                15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9};
        iSPM[2] = new int[]{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0,
                9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8,
                15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9,
                8, 7, 4, 15, 14, 3, 11, 5, 2, 12};
        iSPM[3] = new int[]{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11,
                5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12,
                11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1,
                13, 8, 9, 4, 5, 11, 12, 7, 2, 14};
        iSPM[4] = new int[]{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2,
                12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10,
                13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14,
                2, 13, 6, 15, 0, 9, 10, 4, 5, 3};
        iSPM[5] = new int[]{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4,
                2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2,
                8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15,
                10, 11, 14, 1, 7, 6, 0, 8, 13};
        iSPM[6] = new int[]{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11,
                7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13,
                12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4,
                10, 7, 9, 5, 0, 15, 14, 2, 3, 12};
        iSPM[7] = new int[]{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13,
                8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9,
                12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10,
                8, 13, 15, 12, 9, 0, 3, 5, 6, 11};

    }


    private int[] iCipherKey = new int[64];
    private int[] iCKTemp = new int[56];
    private int[] iPlaintext = new int[64];
    private int[] iCiphertext = new int[64];
    private int[] iPKTemp = new int[64];
    private int[] iL = new int[32];
    private int[] iR = new int[32];

    // 数组置换
    // iSource与iDest的大小不一定相等

    private void permu(int[] iSource, int[] iDest, int[] iPM) {
        if (iDest == null) {
            iDest = new int[iPM.length];
        }

        for (int i = 0; i < iPM.length; i++) {
            iDest[i] = iSource[iPM[i] - 1];
        }
    }

    // 将字节数组进行 位-〉整数 压缩
    // 例如:{0x35,0xf3}->{0,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1}, bArray->iArray
    private void arrayBitToI(byte[] bArray, int[] iArray) {
        for (int i = 0; i < iArray.length; i++) {
            iArray[i] = (int) (bArray[i / 8] >> (7 - i % 8) & 0x01);
        }
    }

    // 将整形数组进行 整数-〉位 压缩
    // arrayBitToI的逆变换,iArray->bArray
    private void arrayIToBit(byte[] bArray, int[] iArray) {
        for (int i = 0; i < bArray.length; i++) {
            bArray[i] = (byte) iArray[8 * i];
            for (int j = 1; j < 8; j++) {
                bArray[i] = (byte) (bArray[i] << 1);
                bArray[i] += (byte) iArray[8 * i + j];
            }
        }
    }

    // 数组的逐项模2加
    // array1[i]=array1[i]^array2[i]
    private void arrayM2Add(int[] array1, int[] array2) {
        for (int i = 0; i < array2.length; i++) {
            array1[i] ^= array2[i];
        }
    }

    // 一个数组等分成两个数组-数组切割
    private void arrayCut(int[] iSource, int[] iDest1, int[] iDest2) {
        int k = iSource.length;
        for (int i = 0; i < k / 2; i++) {
            iDest1[i] = iSource[i];
            iDest2[i] = iSource[i + k / 2];
        }
    }

    // 两个等大的数组拼接成一个
    // arrayCut的逆变换
    private void arrayComb(int[] iDest, int[] iSource1, int[] iSource2) {
        int k = iSource1.length;
        for (int i = 0; i < k; i++) {
            iDest[i] = iSource1[i];
            iDest[i + k] = iSource2[i];
        }
    }

    /** 子密钥产生算法中的循环左移 */
    private void ROL(int[] array) {
        int temp = array[0];
        for (int i = 0; i < 27; i++) {
            array[i] = array[i + 1];
        }
        array[27] = temp;

        temp = array[28];
        for (int i = 0; i < 27; i++) {
            array[28 + i] = array[28 + i + 1];
        }
        array[55] = temp;
    }

    /** 16个子密钥完全倒置 */
    private int[][] invSubKeys(int[][] iSubKeys) {
        int[][] iInvSubKeys = new int[16][];
        for (int i = 0; i < 16; i++) {
            iInvSubKeys[i] = new int[48];
            for (int j = 0; j < 48; j++) {
                iInvSubKeys[i][j] = iSubKeys[15 - i][j];
            }
        }
        return iInvSubKeys;
    }

    /** S盒代替 输入输出皆为部分数组,因此带偏移量 */
    private void Sbox(int[] iInput, int iOffI, int[] iOutput, int iOffO,
                      int[] iSPM) {
        /** S盒中的行号 */
        int iRow = iInput[iOffI] * 2 + iInput[iOffI + 5];
        int iCol = iInput[iOffI + 1] * 8 + iInput[iOffI + 2] * 4
                + iInput[iOffI + 3] * 2 + iInput[iOffI + 4];
        // S盒中的列号
        int x = iSPM[16 * iRow + iCol];
        iOutput[iOffO] = x >> 3 & 0x01;
        iOutput[iOffO + 1] = x >> 2 & 0x01;
        iOutput[iOffO + 2] = x >> 1 & 0x01;
        iOutput[iOffO + 3] = x & 0x01;
    }

    /** 加密函数f */
    private int[] encFunc(int[] iInput, int[] iSubKey) {
        Mac3Encrypt();
        int[] iTemp1 = new int[48];
        int[] iTemp2 = new int[32];
        int[] iOutput = new int[32];
        permu(iInput, iTemp1, iEPM);
        arrayM2Add(iTemp1, iSubKey);
        for (int i = 0; i < 8; i++) {
            Sbox(iTemp1, i * 6, iTemp2, i * 4, iSPM[i]);
        }
        permu(iTemp2, iOutput, iPPM);
        return iOutput;
    }

    /** 子密钥生成 */
    private int[][] makeSubKeys(byte[] bCipherKey) {
        int[][] iSubKeys = new int[16][];
        arrayBitToI(bCipherKey, iCipherKey);
        // int[] tmp = iCipherKey;
        permu(iCipherKey, iCKTemp, iSelePM1);
        for (int i = 0; i < 16; i++) {
            for (int j = 0; j < iROLtime[i]; j++) {
                ROL(iCKTemp);
            }
            iSubKeys[i] = new int[48];
            permu(iCKTemp, iSubKeys[i], iSelePM2);
        }
        return iSubKeys;
    }

    /** 加密 */
    private byte[] encrypt(byte[] bPlaintext, int[][] iSubKeys) {
        byte[] bCiphertext = new byte[8];
        arrayBitToI(bPlaintext, iPlaintext);
        permu(iPlaintext, iPKTemp, iInitPM);
        arrayCut(iPKTemp, iL, iR);
        for (int i = 0; i < 16; i++) {
            if (i % 2 == 0) {
                arrayM2Add(iL, encFunc(iR, iSubKeys[i]));
            } else {
                arrayM2Add(iR, encFunc(iL, iSubKeys[i]));
            }
        }
        arrayComb(iPKTemp, iR, iL);
        permu(iPKTemp, iCiphertext, iInvInitPM);
        arrayIToBit(bCiphertext, iCiphertext);
        return bCiphertext;
    }

    /** 解密 */
    private byte[] decrypt(byte[] bCiphertext, int[][] iSubKeys) {
        int[][] iInvSubKeys = invSubKeys(iSubKeys);
        return encrypt(bCiphertext, iInvSubKeys);
    }

    /** Bit XOR */
    private byte[] BitXor(byte[] Data1, byte[] Data2, int Len) {
        int i;
        byte[] Dest = new byte[Len];

        for (i = 0; i < Len; i++) {
            Dest[i] = (byte) (Data1[i] ^ Data2[i]);
        }
        return Dest;
    }

    /** 3DesMac */
    private byte[] MAC16(int[][] iSubKeys1, int[][] iSubKeys2, byte[] bInit,
                         byte[] bCiphertext) {
        byte[] pbySrcTemp = new byte[8];
        byte[] pbyInitData = new byte[8];
        byte[] pbyDeaSrc = new byte[8];
        /**這個就是需要輸出的mac字符串,網上的工具,只需要4字節就可以,由於我得項目需要用到8字節16個字符,所有我這裡改成了8*/
        byte[] pbyMac = new byte[8];
        int i, j, n, iAppend;
        int nCur = 0;
        int iSrcLen = bCiphertext.length;
        n = iSrcLen / 8 + 1;
        iAppend = 8 - (n * 8 - iSrcLen);

        for (nCur = 0; nCur < 8; nCur++) {
            pbyInitData[nCur] = bInit[nCur];
        }
        for (i = 0; i < n; i++) {
            for (nCur = 0; nCur < 8; nCur++) {
                pbySrcTemp[0] = 0x00;
            }
            if (i == (n - 1)) {
                for (nCur = 0; nCur < iAppend; nCur++) {
                    pbySrcTemp[nCur] = bCiphertext[i * 8 + nCur];
                }
                pbySrcTemp[iAppend] = (byte) 0x80;
                for (j = iAppend + 1; j < 8; j++) {
                    pbySrcTemp[j] = 0x00;
                }
            } else {
                for (nCur = 0; nCur < 8; nCur++) {
                    pbySrcTemp[nCur] = bCiphertext[i * 8 + nCur];
                }
            }

            pbyDeaSrc = BitXor(pbySrcTemp, pbyInitData, 8);

            pbyInitData = encrypt(pbyDeaSrc, iSubKeys1);
        }
        pbyDeaSrc = decrypt(pbyInitData, iSubKeys2);
        pbyInitData = encrypt(pbyDeaSrc, iSubKeys1);

        /**這個就是需要輸出的mac字符串,網上的工具,只需要4字節就可以,由於我得項目需要用到8字節16個字符,所有我這裡改成了8
         * 就是下面的循環次數,假如你的當前項目想網上的軟件一樣,只需要4字節,那只需要將下面的8改成4即可
         * */
        for (nCur = 0; nCur < 8; nCur++) {
            pbyMac[nCur] = pbyInitData[nCur];
        }
        return pbyMac;
    }


    private static byte[] hex2byte(byte[] b) {
        if ((b.length % 2) != 0) {
            throw new IllegalArgumentException();
        }
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            b2[n / 2] = (byte) Integer.parseInt(item, 16);
        }
        return b2;
    }


    /**
     * @param strKey      密钥,Hex字符串,如:78B49F4BF5B16A17DF4AF5A36E49F4A0.长度必须为32
     * @param strInitData 初始因子.长度必须为16,一般为:0000000000000000 strMacData:MAC数据,长度必须为偶数
     * @param strMacData  需要簽名的內容
     * @throws Exception
     */
    public String Str3MAC(String strKey, String strInitData, String strMacData) throws Exception {
        String strKey1;
        String strKey2;
        if ((strKey.length()) != 32) {
            throw new Exception("密钥长度不正确,必须为32");
        }
        if ((strInitData.length()) != 16) {
            throw new Exception("初始因子长度不正确,必须为16");
        }
        if ((strMacData.length() % 2) != 0) {
            throw new Exception("MAC Data长度不是偶数");
        }

        strKey1 = strKey.substring(0, 16);
        strKey2 = strKey.substring(16);
        // 3DES的密钥K1
        byte[] cipherKey1 = hex2byte(strKey1.getBytes());
        // 3DES的密钥K2
        byte[] cipherKey2 = hex2byte(strKey2.getBytes());
        // 初始因子
        byte[] bInit = hex2byte(strInitData.getBytes());
        // MAC数据
        byte[] bCiphertext = hex2byte(strMacData.getBytes());

        int[][] subKeys1 = new int[16][]; // 用于存放K1产生的子密钥
        int[][] subKeys2 = new int[16][]; // 用于存放K2产生的子密钥
        subKeys1 = makeSubKeys(cipherKey1);
        subKeys2 = makeSubKeys(cipherKey2);

        byte[] byMac = MAC16(subKeys1, subKeys2, bInit, bCiphertext);

        String sRet = byte2hex(byMac);
        return sRet;
    }

    public String calculatorMac(String communicationKey, String strData) throws Exception {
        /**初始因子.长度必须为16,一般为:0000000000000000 */
        return this.Str3MAC(communicationKey, "0000000000000000", strData);
    }


    public static String byte2hex(byte[] b) { // 一个字节的数,
        // 转成16进制字符串
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            // 整数转成十六进制表示
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                hs = hs + "0" + stmp;
            } else {
                hs = hs + stmp;
            }
        }
        return hs.toUpperCase();
    }

}

代碼使用方法:

val userPrvKey = "";//必須32位長度的字符串
val signData = "";必須是偶數
val result =  Pboc3DesMACUtils().calculatorMac(userPrvKey, signData)

OK,代碼分享就到這裡,希望能幫助到你

上面的算法,我也不太理解,所以。。。。就這樣吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用和引用的内容,RSA PBOC工具是一种用于银行卡的密码算法工具,主要用于密钥生成、公私钥加密与解密以及联机交易验证和脱机数据验证等功能。在银行卡的应用中,RSA算法用于脱机数据验证,而PBOC标准则是指中国的银行卡标准,其中包括PBOC1.0和PBOC2.0标准,分别支持使用3DES算法的MAC、数据加解密和密钥离散算法。国密算法是用于PBOC3.0标准的增强安全部分的内容。根据引用的描述,该工具在智能卡、USB Key、POS等产品中发挥重要作用,并适用于需要基于命令行测试的嵌入式产品。如果您有更多疑问,可以咨询引用中提供的联系方式。123 #### 引用[.reference_title] - *1* *3* [银行卡安全密码算法辅助计算工具(3DES+RSA)](https://blog.csdn.net/goldboar/article/details/39254663)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *2* [PowerScript--功能强大的智能卡,USB Key, POS脚本命令工具](https://blog.csdn.net/l_z_h/article/details/124048086)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值