• 前言
  • mac1验证、mac2计算、tac验证流程
  • 运行结果如下
  • 涉及的帮助类
    • DES工具类
    • ByteUtil

前言

CPU卡的各个密钥是需要通过加密机分散获得,因此这里使用的是之前自己发的一张复旦微电子的测试卡,现已经明确知道其对应的充值密钥及维护密钥,想了解简单的发卡流程可以参考之前的博客:复旦微电子CPU卡发卡流程

具体流程直接上代码,里面有比较详细的注释,按着代码一行一行的读就可以了

mac1验证、mac2计算、tac验证流程

import java.util.Locale;public class CardCenter
{public static void main(String[] args){// 圈存的keyString loadKey = "3F013F013F013F013F013F013F013F01";System.out.println("圈存的key:" + loadKey);// 验证tac的keyString tacKey = "34343434343434343434343434343434";System.out.println("验证tac的key:" + tacKey);System.out.println();// posidString posid = "112233445566";System.out.println("终端ID:" + posid);// 交易金额String tradeAmount = "00000001";System.out.println("交易金额:" + tradeAmount);// 交易金额十进制int ta = 1;// 交易类型String tradeType = "02";System.out.println("交易类型:" + tradeType);System.out.println();// 预充值指令System.out.println("组装预充值指令:805000020b0100000001112233445566");// 预消费指令805000020b0100000001112233445566的指令回复String preTopup = "0000001a0017000106b825d7684c81ce9000";System.out.println("得到预充值响应:" + preTopup);byte[] recvByte = ByteUtil.hexStr2Byte(preTopup);// 卡余额String balance = ByteUtil.hexToStr(recvByte, 0, 4);int bal = ByteUtil.hexToInt(recvByte, 0, 4);System.out.println("卡余额:" + balance);// 联机计数器String cardCnt = ByteUtil.hexToStr(recvByte, 4, 2);System.out.println("联机计数器:" + cardCnt);// 密钥版本String keyVersion = ByteUtil.hexToStr(recvByte, 6, 1);System.out.println("密钥版本:" + keyVersion);// 算法标识String alglndMark = ByteUtil.hexToStr(recvByte, 7, 1);System.out.println("算法标识:" + alglndMark);// 随机数String random = ByteUtil.hexToStr(recvByte, 8, 4);System.out.println("随机数:" + random);// mac1String mac1 = ByteUtil.hexToStr(recvByte, 12, 4);System.out.println("mac1:" + mac1);System.out.println("");System.out.println("开始验证mac1");// 验证mac1的正确性// 输入的数据为:随机数+联机计数器+“8000”String inputData = random + cardCnt + "8000";System.out.println("计算过程密钥数据:" + inputData);// 计算过程密钥String sessionKey = Des.getHintKey(inputData, loadKey);System.out.println("过程密钥:" + sessionKey);// 计算mac1需要输入的数据// 输入的数据为:余额+交易金额+交易类型+终端编号inputData = balance + tradeAmount + tradeType + posid;String legitMac1 = Des.PBOC_DES_MAC(sessionKey, "0000000000000000", inputData, 0).substring(0, 8);System.out.println("标准的mac1数据:" + legitMac1);if (!mac1.toUpperCase(Locale.getDefault()).equals(legitMac1)){System.out.println("mac1校验失败!");return;}System.out.println("mac1校验成功!");// 开始计算Mac2用于做充值确认操作System.out.println("");System.out.println("开始计算mac2");// 交易日期String tradeDate = "20170310";// 交易时间String tradeTime = "110734";// 计算mac2的输入数据// 输入数据为:交易金额+交易类型+终端编号+交易时间+交易日期inputData = tradeAmount + tradeType + posid + tradeDate + tradeTime;String mac2 = Des.PBOC_DES_MAC(sessionKey, "0000000000000000", inputData, 0).substring(0, 8);// 得到mac2System.out.println("计算后的mac2:" + mac2);// 组装写卡指令System.out.println("805200000b" + tradeDate + tradeTime + mac2);System.out.println("开始向卡片发送充值确认的指令");// 向卡发送指令System.out.println("apdu:805200000b201703101107349C8A0625");// 响应tacSystem.out.println("recv:16ead5169000");String tac = "16ead516";System.out.println("得到Tac:" + tac);System.out.println("");System.out.println("开始验证tac");System.out.println("tac验证密钥:" + tacKey);// 对tac验证密钥左边8个字节和右边8个字节做异或处理得到tac过程密钥String tacTessionKey = Des.xOr(tacKey.substring(0, 16), tacKey.substring(16, 32));System.out.println("tac过程密钥:" + tacTessionKey);// 充值成功之后新的金额为00000043+00000001=00000044String newBalance = ByteUtil.hexToStr(ByteUtil.intToHex(bal + ta, 4));// 计算标准的tac的输入数据// 输入的数据:新的余额+旧的联机计数器+交易金额+交易类型+终端编号+交易日期+交易时间inputData = newBalance + cardCnt + tradeAmount + tradeType + posid + tradeDate + tradeTime;String legitTac = Des.PBOC_DES_MAC(tacTessionKey, "0000000000000000", inputData, 0).substring(0, 8);System.out.println("标准的tac数据:" + legitTac);if (!tac.toUpperCase(Locale.getDefault()).equals(legitTac)){System.out.println("tac校验错误!");return;}System.out.println("tac校验成功!");}
}

运行结果如下

圈存的key:3F013F013F013F013F013F013F013F01
验证tac的key:34343434343434343434343434343434终端ID:112233445566
交易金额:00000001
交易类型:02组装预充值指令:805000020b0100000001112233445566
得到预充值响应:0000001a0017000106b825d7684c81ce9000
卡余额:0000001a
联机计数器:0017
密钥版本:00
算法标识:01
随机数:06b825d7
mac1:684c81ce开始验证mac1
计算过程密钥数据:06b825d700178000
过程密钥:6E76E8541E217D9A
D[0]=0000001a00000001
D[1]=0211223344556680
1**************
I=0000001A00000001
O=D42EE2FF647C9F00
I=D63FC0CC2029F980
I=684C81CE47609B5B
标准的mac1数据:684C81CE
mac1校验成功!开始计算mac2
D[0]=0000000102112233
D[1]=4455662017031011
D[2]=0734800000000000
1**************
I=0000000102112233
O=A0D9EACF4A0A833D
I=E48C8CEF5D09932C
2**************
I=E48C8CEF5D09932C
O=6DF657FD31FBAB05
I=6AC2D7FD31FBAB05
I=9C8A06256ACBF60A
计算后的mac2:9C8A0625
805200000b201703101107349C8A0625
开始向卡片发送充值确认的指令
apdu:805200000b201703101107349C8A0625
recv:16ead5169000
得到Tac:16ead516开始验证tac
tac验证密钥:34343434343434343434343434343434
tac过程密钥:0000000000000000
D[0]=0000001b00170000
D[1]=0001021122334455
D[2]=6620170310110734
D[3]=8000000000000000
1**************
I=0000001B00170000
O=F0DE01AF2D71051B
I=F0DF03BE0F42414E
2**************
I=F0DF03BE0F42414E
O=044D5B737F9A0282
I=626D4C706F8B05B6
3**************
I=626D4C706F8B05B6
O=2B5ED4A6C483D0BF
I=AB5ED4A6C483D0BF
I=16EAD5165389360A
标准的tac数据:16EAD516
tac校验成功!

涉及的帮助类

DES工具类
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;/*** DES工具类*/
public class Des
{/*** ***************************压缩替换S-Box************************************** *************/private static final int[][] s1 ={{ 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 } };private static final int[][] s2 ={{ 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 } };private static final int[][] s3 ={{ 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 } };private static final int[][] s4 ={{ 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 },// erorr{ 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 } };private static final int[][] s5 ={{ 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 } };private static final int[][] s6 ={{ 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 } };private static final int[][] s7 ={{ 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 } };private static final int[][] s8 ={{ 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 static final int[] 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 final int[] _ip ={ 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 final int[] LS ={ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };private static int[][] subKey = new int[16][48];public static int HEX = 0;public static int ASC = 1;/*** 将十六进制A--F转换成对应数* * @param ch* @return* @throws Exception*/public static int getIntByChar(char ch) throws Exception{char t = Character.toUpperCase(ch);int i = 0;switch (t){case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':i = Integer.parseInt(Character.toString(t));break;case 'A':i = 10;break;case 'B':i = 11;break;case 'C':i = 12;break;case 'D':i = 13;break;case 'E':i = 14;break;case 'F':i = 15;break;default:throw new Exception("getIntByChar was wrong");}return i;}/*** 将字符串转换成二进制数组* * @param source*            : 16字节* @return*/public static int[] string2Binary(String source){int len = source.length();int[] dest = new int[len * 4];char[] arr = source.toCharArray();for (int i = 0; i < len; i++){int t = 0;try{t = getIntByChar(arr[i]);}catch (Exception e){e.printStackTrace();}String[] str = Integer.toBinaryString(t).split("");int k = i * 4 + 3;for (int j = str.length - 1; j > 0; j--){dest[k] = Integer.parseInt(str[j]);k--;}}return dest;}/*** 返回x的y次方* * @param x* @param y* @return*/public static int getXY(int x, int y){int temp = x;if (y == 0)x = 1;for (int i = 2; i <= y; i++){x *= temp;}return x;}/*** s位长度的二进制字符串* * @param s* @return*/public static String binary2Hex(String s){int len = s.length();int result = 0;int k = 0;if (len > 4)return null;for (int i = len; i > 0; i--){result += Integer.parseInt(s.substring(i - 1, i)) * getXY(2, k);k++;}switch (result){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:return "" + result;case 10:return "A";case 11:return "B";case 12:return "C";case 13:return "D";case 14:return "E";case 15:return "F";default:return null;}}/*** 将int转换成Hex* * @param i* @return* @throws Exception*/public static String int2Hex(int i){switch (i){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:return "" + i;case 10:return "A";case 11:return "B";case 12:return "C";case 13:return "D";case 14:return "E";case 15:return "F";default:return null;}}/*** 将二进制字符串转换成十六进制字符* * @param s* @return*/public static String binary2ASC(String s){String str = "";int ii = 0;int len = s.length();// 不够4bit左补0if (len % 4 != 0){while (ii < 4 - len % 4){s = "0" + s;}}for (int i = 0; i < len / 4; i++){str += binary2Hex(s.substring(i * 4, i * 4 + 4));}return str;}/*** IP初始置换* * @param source* @return*/public static int[] changeIP(int[] source){int[] dest = new int[64];for (int i = 0; i < 64; i++){dest[i] = source[ip[i] - 1];}return dest;}/*** IP-1逆置* * @param source* @return*/public static int[] changeInverseIP(int[] source){int[] dest = new int[64];for (int i = 0; i < 64; i++){dest[i] = source[_ip[i] - 1];}return dest;}/*** 2bit扩展8bit* * @param source* @return*/public static int[] expend(int[] source){int[] ret = new int[48];int[] temp ={ 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 };for (int i = 0; i < 48; i++){ret[i] = source[temp[i] - 1];}return ret;}/*** 8bit压缩2bit* * @param source*            (48bit)* @return R(32bit) B=E(R)⊕K,将48 位的B 分成8 个分组,B=B1B2B3B4B5B6B7B8*/public static int[] press(int[] source){int[] ret = new int[32];int[][] temp = new int[8][6];int[][][] s ={ s1, s2, s3, s4, s5, s6, s7, s8 };StringBuffer str = new StringBuffer();for (int i = 0; i < 8; i++){for (int j = 0; j < 6; j++){temp[i][j] = source[i * 6 + j];}}for (int i = 0; i < 8; i++){// (16)int x = temp[i][0] * 2 + temp[i][5];// (2345)int y = temp[i][1] * 8 + temp[i][2] * 4 + temp[i][3] * 2 + temp[i][4];int val = s[i][x][y];String ch = int2Hex(val);// System.out.println("x=" + x + ",y=" + y + "-->" + ch);// String ch = Integer.toBinaryString(val);str.append(ch);}// System.out.println(str.toString());ret = string2Binary(str.toString());// printArr(ret);// 置换Pret = dataP(ret);return ret;}/*** 置换P(32bit)* * @param source* @return*/public static int[] dataP(int[] source){int[] dest = new int[32];int[] temp ={ 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 };int len = source.length;for (int i = 0; i < len; i++){dest[i] = source[temp[i] - 1];}return dest;}/*** @param R*            (2bit)* @param K*            (48bit的轮子密* @return 32bit*/public static int[] f(int[] R, int[] K){int[] dest = new int[32];int[] temp = new int[48];// 先将输入32bit扩展8bitint[] expendR = expend(R);// 48bit// 与轮子密钥进行异或运temp = diffOr(expendR, K);// 压缩2bitdest = press(temp);// printArr(temp);return dest;}/*** 两个等长的数组做异或* * @param source1* @param source2* @return*/public static int[] diffOr(int[] source1, int[] source2){int len = source1.length;int[] dest = new int[len];for (int i = 0; i < len; i++){dest[i] = source1[i] ^ source2[i];}return dest;}/*** DES加密--->对称密钥 D = Ln(32bit)+Rn(32bit) 经过16轮置* * @param D*            (16byte)明文* @param K*            (16byte)轮子密钥* @return (16byte)密文*/public static String encryption(String D, String K){String str = "";int[] temp = new int[64];int[] data = string2Binary(D);// printArr(data);// 第一步初始置data = changeIP(data);// printArr(data);int[][] left = new int[17][32];int[][] right = new int[17][32];for (int j = 0; j < 32; j++){left[0][j] = data[j];right[0][j] = data[j + 32];}// printArr(left[0]);// printArr(right[0]);setKey(K);// sub key okfor (int i = 1; i < 17; i++){// 获取(48bit)的轮子密int[] key = subKey[i - 1];// L1 = R0left[i] = right[i - 1];// R1 = L0 ^ f(R0,K1)int[] fTemp = f(right[i - 1], key);// 32bitright[i] = diffOr(left[i - 1], fTemp);}// 组合的时候,左右调换**************************************************for (int i = 0; i < 32; i++){temp[i] = right[16][i];temp[32 + i] = left[16][i];}temp = changeInverseIP(temp);str = binary2ASC(intArr2Str(temp));return str;}/*** DES解密--->对称密钥 解密算法与加密算法基本相同,不同之处仅在于轮子密钥的使用顺序逆序,即解密的第1 轮子密钥为加密的6 轮子密钥,解密的* 轮子密钥为加密的5 轮子密钥,…, 解密的第16 轮子密钥为加密的 轮子密钥* * @param source*            密文* @param key*            密钥* @return*/public static String discryption(String source, String key){String str = "";int[] data = string2Binary(source);// 64bit// 第一步初始置data = changeIP(data);int[] left = new int[32];int[] right = new int[32];int[] tmp = new int[32];for (int j = 0; j < 32; j++){left[j] = data[j];right[j] = data[j + 32];}setKey(key);// sub key okfor (int i = 16; i > 0; i--){// 获取(48bit)的轮子密/** *******不同之处********* */int[] sKey = subKey[i - 1];tmp = left;// R1 = L0left = right;// L1 = R0 ^ f(L0,K1)int[] fTemp = f(right, sKey);// 32bitright = diffOr(tmp, fTemp);}// 组合的时候,左右调换**************************************************for (int i = 0; i < 32; i++){data[i] = right[i];data[32 + i] = left[i];}data = changeInverseIP(data);for (int i = 0; i < data.length; i++){str += data[i];}str = binary2ASC(str);return str;}/*** 单长密钥DES(16byte)* * @param source* @param key* @param type*            0:encrypt 1:discrypt* @return*/public static String DES_1(String source, String key, int type){if (source.length() != 16 || key.length() != 16)return null;if (type == 0){return encryption(source, key);}if (type == 1){return discryption(source, key);}return null;}/*** @param source* @param key* @param type*            0:encrypt 1:discrypt* @return*/public static String DES_2(String source, String key, int type){return null;}/*** 三重DES算法(双长密32byte)) 密钥K1和K2 1、先用K1加密明文 2、接K2对上的结果进行解 3、然后用K1对上的结果进行加* * @param source* @param key* @param type*            0:encrypt 1:discrypt* @return*/public static String DES_3(String source, String key, int type){if (key.length() != 32 || source.length() != 16)return null;String temp = null;String K1 = key.substring(0, key.length() / 2);String K2 = key.substring(key.length() / 2);System.out.println("K1--->" + K1);System.out.println("K2--->" + K2);if (type == 0){temp = encryption(source, K1);System.out.println("step1--->" + temp);temp = discryption(temp, K2);System.out.println("step2--->" + temp);return encryption(temp, K1);}if (type == 1){temp = discryption(source, K1);temp = encryption(temp, K2);return discryption(temp, K1);}return null;}/*** **********************************48bit的轮子密钥的生成*************************** ********************************//*** 4bit的密钥转换成56bit* * @param source* @return*/public static int[] keyPC_1(int[] source){int[] dest = new int[56];int[] temp ={ 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 };for (int i = 0; i < 56; i++){dest[i] = source[temp[i] - 1];}return dest;}/*** 将密钥循环左移i* * @param source*            二进制密钥数* @param i*            循环左移位数* @return*/public static int[] keyLeftMove(int[] source, int i){int temp = 0;int len = source.length;int ls = LS[i];for (int k = 0; k < ls; k++){temp = source[0];for (int j = 0; j < len - 1; j++){source[j] = source[j + 1];}source[len - 1] = temp;}return source;}/*** 6bit的密钥转换成48bit* * @param source* @return*/public static int[] keyPC_2(int[] source){int[] dest = new int[48];int[] temp ={ 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 };for (int i = 0; i < 48; i++){dest[i] = source[temp[i] - 1];}return dest;}/*** 获取轮子密钥(48bit)* * @param source* @return*/public static void setKey(String source){if (subKey.length > 0)subKey = new int[16][48];// 装换4bitint[] temp = string2Binary(source);// 6bit均分成两部分int[] left = new int[28];int[] right = new int[28];// 经过PC-14bit转换6bitint[] temp1 = new int[56];temp1 = keyPC_1(temp);// printArr(temp1);// 将经过转换的temp1均分成两部分for (int i = 0; i < 28; i++){left[i] = temp1[i];right[i] = temp1[i + 28];}// 经过16次循环左移,然后PC-2置换for (int i = 0; i < 16; i++){left = keyLeftMove(left, LS[i]);right = keyLeftMove(right, LS[i]);for (int j = 0; j < 28; j++){temp1[j] = left[j];temp1[j + 28] = right[j];}// printArr(temp1);subKey[i] = keyPC_2(temp1);}}public static void printArr(int[] source){int len = source.length;for (int i = 0; i < len; i++){System.out.print(source[i]);}System.out.println();}/*** 将ASC字符串转16进制字符* * @param asc* @return*/public static String ASC_2_HEX(String asc){StringBuffer hex = new StringBuffer();try{byte[] bs = asc.toUpperCase().getBytes("UTF-8");for (byte b : bs){hex.append(Integer.toHexString(new Byte(b).intValue()));}}catch (UnsupportedEncodingException e){e.printStackTrace();}return hex.toString();}/*** 16进制的字符串转换成ASC的字符串* 16进制的字符串压缩成BCD(30313233343536373839414243444546)-->(0123456789ABCDEF)* * @param hex* @return*/public static String HEX_2_ASC(String hex){String asc = null;int len = hex.length();byte[] bs = new byte[len / 2];for (int i = 0; i < len / 2; i++){bs[i] = Byte.parseByte(hex.substring(i * 2, i * 2 + 2), 16);}try{asc = new String(bs, "UTF-8");}catch (UnsupportedEncodingException e){e.printStackTrace();}return asc;}/*** 计算MAC(hex) ANSI-X9.9-MAC(16的整数不补) PBOC-DES-MAC(16的整数补8000000000000000)* 使用单倍长密钥DES算法* * @param key*            密钥 (16byte)* @param vector*            初始向量0000000000000000* @param data*            数据* @return mac*/public static String PBOC_DES_MAC(String key, String vector, String data, int type){if (key.length() != 16){return null;}if (type == ASC){data = ASC_2_HEX(data);}int len = data.length();int arrLen = len / 16 + 1;String[] D = new String[arrLen];if (vector == null)vector = "0000000000000000";if (len % 16 == 0){data += "8000000000000000";}else{data += "80";for (int i = 0; i < 15 - len % 16; i++){data += "00";}}for (int i = 0; i < arrLen; i++){D[i] = data.substring(i * 16, i * 16 + 16);System.out.println("D[" + i + "]=" + D[i]);}// D0 Xor VectorString I = xOr(D[0], vector);String O = null;for (int i = 1; i < arrLen; i++){System.out.println(i + "**************");System.out.println("I=" + I);O = DES_1(I, key, 0);System.out.println("O=" + O);I = xOr(D[i], O);System.out.println("I=" + I);}I = DES_1(I, key, 0);System.out.println("I=" + I);return I;}/*** 计算MAC(hex) PBOC_3DES_MAC(16的整数补8000000000000000) 前n-1组使用单长密钥DES* CBC算法(使用密钥是密钥的左8字节) 最后1组使用双长密钥3DES CBC算法(使用全部16字节密钥)* * @param key*            密钥 (32byte)* @param vector*            初始向量0000000000000000* @param data*            数据* @return mac*/public static String PBOC_3DES_MAC(String key, String vector, String data, int type){if (key.length() != 32){return null;}if (type == ASC){data = ASC_2_HEX(data);}int len = data.length();int arrLen = len / 16 + 1;String[] D = new String[arrLen];if (vector == null)vector = "0000000000000000";if (len % 16 == 0){data += "8000000000000000";}else{data += "80";for (int i = 0; i < 15 - len % 16; i++){data += "00";}}for (int i = 0; i < arrLen; i++){D[i] = data.substring(i * 16, i * 16 + 16);System.out.println("D[" + i + "]=" + D[i]);}// D0 Xor VectorString I = xOr(D[0], vector);String O = null;String kl = key.substring(0, 16);System.out.println("KL8:" + kl);for (int i = 1; i < arrLen; i++){System.out.println(i + "**************");System.out.println("I=" + I);O = DES_1(I, kl, 0);System.out.println("O=" + O);I = xOr(D[i], O);System.out.println("I=" + I);}I = DES_3(I, key, 0);return I;}/*** 将s1和s2做异或,然后返回* * @param s1* @param s2* @return*/public static String xOr(String s1, String s2){int[] iArr = diffOr(string2Binary(s1), string2Binary(s2));return binary2ASC(intArr2Str(iArr));}/*** 将int类型数组拼接成字符串* * @param arr* @return*/public static String intArr2Str(int[] arr){StringBuffer sb = new StringBuffer();for (int i = 0; i < arr.length; i++){sb.append(arr[i]);}return sb.toString();}/*** 将data分散* * @param data*            数据8字节 16个长度* @param key*            密钥* @param type* @return*/public static String divData(String data, String key, int type){String left = null;String right = null;if (type == HEX){left = key.substring(0, 16);right = key.substring(16, 32);}if (type == ASC){left = ASC_2_HEX(key.substring(0, 8));right = ASC_2_HEX(key.substring(8, 16));}// 加密data = DES_1(data, left, 0);// 解密data = DES_1(data, right, 1);// 加密data = DES_1(data, left, 0);return data;}/*** 取反(10001)--->(01110)* * @param source*            数据源* @return*/public static String reverse(String source){int[] data = string2Binary(source);int j = 0;for (int i : data){data[j++] = 1 - i;}return binary2ASC(intArr2Str(data));}/*** 主密钥需要经过两次分散获得IC卡中的子密钥 空圈的通讯类过程密钥使用这种密钥分散机制* * @param issuerFlag*            发卡方标识符* @param appNo*            应用序列号即卡号* @param mpk*            主密钥* @return*/public static String getDPK(String issuerFlag, String appNo, String mpk){// 第一次分散StringBuffer issuerMPK = new StringBuffer();// 获取Issuer MPK左半边issuerMPK.append(divData(issuerFlag, mpk, 0));// 获取Issuer MPK右半边issuerMPK.append(divData(reverse(issuerFlag), mpk, 0));// 第二次分散StringBuffer dpk = new StringBuffer();// 获取DPK左半边dpk.append(divData(appNo, issuerMPK.toString(), 0));// 获取DPK右半边dpk.append(divData(reverse(appNo), issuerMPK.toString(), 0));return dpk.toString();}/*** 主密钥需要经过一次分散获得的子密钥 空圈的交易类过程密钥使用这种密钥分散机制* * @param mpk*            主密钥* @return*/public static String getDPK4Once(String data, String mpk){// 第一次分散StringBuffer dpk = new StringBuffer();// 获取DPK左半边dpk.append(divData(data, mpk, 0));// 获取DPK右半边dpk.append(divData(reverse(data), mpk.toString(), 0));return dpk.toString();}/*** @Title: getHintKey 获取过程密钥* @Description: TODO* @param: @param data 加密数据* @param: @param key 密钥* @param: @return* @return: String* @throws:*/public static String getHintKey(String data, String key){if (null == data || data.length() != 16)return null;if (null != key && key.length() > 16)key = key.substring(0, 16);if (null == key || key.length() != 16)return null;// 加密数据return encryption(data, key);}// 字符串转hex玛public static byte[] hexStr2Byte(String hex){int len = (hex.length() / 2);byte[] result = new byte[len];char[] achar = hex.toCharArray();for (int i = 0; i < len; i++){int pos = i * 2;result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));}return result;}private static byte toByte(char c){return mask[c];}static byte[] mask = new byte[128];private static final String Algorithm = "DESede"; // 定义 加密算法,可用// DES,DESede,Blowfish// keybyte为加密密钥,长度为24字节// src为被加密的数据缓冲区(源)public static byte[] encryptMode(byte[] keybyte, byte[] src){try{// 生成密钥SecretKey deskey = new SecretKeySpec(keybyte, Algorithm);// 从原始密钥数据创建DESKeySpec对象// DESedeKeySpec dks = new DESKeySpec(key);// 加密Cipher c1 = Cipher.getInstance(Algorithm);c1.init(Cipher.ENCRYPT_MODE, deskey);return c1.doFinal(src);}catch (java.security.NoSuchAlgorithmException e1){e1.printStackTrace();}catch (javax.crypto.NoSuchPaddingException e2){e2.printStackTrace();}catch (Exception e3){e3.printStackTrace();}return null;}public static String printHexString(String hint, byte[] b){System.out.print(hint);String abc = "";for (int i = 0; i < b.length; i++){String hex = Integer.toHexString(b[i] & 0xFF);if (hex.length() == 1){hex = '0' + hex;}System.out.print(hex.toUpperCase() + " ");abc = abc + hex;}System.out.println("");return abc.toUpperCase();}private static String hexString = "0123456789ABCDEFabcdef";/*** 将string(包括中文)转为hex* * @param str* @return*/public static String encode(String str){byte[] bytes = str.getBytes();StringBuilder sb = new StringBuilder(bytes.length * 2);// 转换hex编码for (byte b : bytes){sb.append(Integer.toHexString(b + 0x800).substring(1));}str = sb.toString();return str;}/*** 把hex编码转换为string* * @param bytes* @return*/public static String decode(String bytes){bytes = bytes.toUpperCase();ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length() / 2);// 将每2位16进制整数组装成一个字节for (int i = 0; i < bytes.length(); i += 2)baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString.indexOf(bytes.charAt(i + 1))));return new String(baos.toByteArray());}
}
ByteUtil
public class ByteUtil
{static byte[] mask = new byte[128];static{initMask();}static byte[] asciiMask = new byte[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };private static void initMask(){ // init maskfor (int i = 0; i <= 9; i++){mask[i + 48] = (byte) i;}for (int i = 0; i <= 5; i++){mask[i + 97] = (byte) (10 + i);}for (int i = 0; i <= 5; i++){mask[i + 65] = (byte) (10 + i);}}public static int[] byte2Bitmap(int b){int[] bitmap = new int[8];for (int i = 0; i < 8; i++){bitmap[i] = ((b >> (8 - i - 1)) & 0x01);}return bitmap;}public static byte[] intToBCD(int n, int balen){byte[] ret = new byte[balen];int tmp;for (int i = 1; i <= balen; i++){tmp = n % 100;ret[balen - i] = (byte) (tmp / 10 * 16 + tmp % 10);n -= tmp;if (n == 0){break;}n /= 100;}return ret;}public static int bcdToInt(byte[] ba, int idx, int len){int jinwei = len * 2;int ret = 0;int temp = 0;int pow;int posNum; // 正数for (int i = 0, n = len; i < n; i++){pow = pow(10, (jinwei - 1));posNum = ba[idx + i] >= 0 ? ba[idx + i] : ba[idx + i] + 256;temp = (posNum / 16) * pow + posNum % 16 * pow / 10;ret += temp;jinwei -= 2;}return ret;}public static int pow(int x, int y){int n = x;for (int i = 1; i < y; i++){n *= x;}return n;}public static byte[] hexStr2Byte(String hex){int len = (hex.length() / 2);byte[] result = new byte[len];char[] achar = hex.toCharArray();for (int i = 0; i < len; i++){int pos = i * 2;result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));}return result;}public static int revAsciiHexToInt(byte[] ah){int ret = 0;for (int i = 0; i < ah.length / 2; i++){int hex = (mask[ah[i * 2]] << 4) + (mask[ah[i * 2 + 1]]);ret += (hex << (8 * i));}return ret;}public static int revHexToInt(byte[] data, int off, int len){int ret = 0;for (int i = 0; i < len; i++){ret += (data[off + i] & 0xff) << (8 * i);}return ret;}public static int revAsciiHexToInt(byte[] ah, int off, int len){int ret = 0;for (int i = 0, n = len / 2; i < n; i++){int hex = (mask[ah[i * 2 + off]] << 4) + (mask[ah[i * 2 + off + 1]]);ret += (hex << (8 * i));}return ret;}public static byte[] intToRevAsciiHex(int value, int hexlen){byte[] ret = new byte[hexlen * 2];for (int i = 0; i < hexlen; i++){if (value > 0){ret[i * 2] = asciiMask[((value & 0xf0) >> 4)];ret[i * 2 + 1] = asciiMask[(value & 0xf)];value >>= 8;}else{ret[i * 2] = '0';ret[i * 2 + 1] = '0';}}return ret;}public static String intToRevHexString(int value, int hexlen){byte[] ret = new byte[hexlen];for (int i = 0; i < hexlen; i++){ret[i] = (byte) (value & 0xff);value >>= 8;if (value == 0)break;}return hexToStr(ret);}public static byte[] intToAsciiHex(int value, int len){byte[] ret = new byte[len * 2];for (int i = 0; i < len; i++){ret[i * 2] = asciiMask[((value & 0xf0) >> 4)];ret[i * 2 + 1] = asciiMask[value & 0xf];value >>= 8;}return ret;}public static byte[] intToHex(int value, int len){byte[] ret = new byte[len];for (int i = 0; i < len; i++){ret[i] = (byte) ((value >> 8 * (len - i - 1)) & 0xff);}return ret;}public static byte[] intToRevHex(int value, int len){byte[] ret = new byte[len];for (int i = 0; i < len; i++){ret[i] = (byte) (value & 0xff);value >>= 8;}return ret;}public static int hexToInt(byte[] buf, int idx, int len){int ret = 0;final int e = idx + len;for (int i = idx; i < e; ++i){ret <<= 8;ret |= buf[i] & 0xFF;}return ret;}public static int hexToInt(byte[] buf){return hexToInt(buf, 0, buf.length);}public static String hexToStr(byte[] buf){return hexToStr(buf, 0, buf.length);}public static String hexToStr(byte[] buf, int idx, int len){StringBuffer sb = new StringBuffer();int n;for (int i = 0; i < len; i++){n = buf[i + idx] & 0xff;if (n < 0x10){sb.append("0");}sb.append(Integer.toHexString(n));}return sb.toString();}private static byte toByte(char c){return mask[c];}public static String strToHex(String str){try{byte[] bytes = str.getBytes("GBK");StringBuilder sb = new StringBuilder(bytes.length * 2);// 转换hex编码for (byte b : bytes){sb.append(Integer.toHexString(b + 0x800).substring(1));}return sb.toString();}catch (Exception e){e.printStackTrace();}return null;}public static String toHex(byte b) {String result = Integer.toHexString(b & 0xFF);if (result.length() == 1) {result = '0' + result;}return result;}}

CPU卡校验MAC1、计算MAC2、校验TAC的方式及流程相关推荐

  1. bcc校验码计算_CRC校验你会吗?计算、校验、C语言实现,三步教你轻松搞定

    目录 前言 CRC算法简介 CRC计算 CRC校验 CRC计算的C语言实现 CRC计算工具 总结 前言 最近的工作中,要实现对通信数据的CRC计算,所以花了两天的时间好好研究了一下,周末有时间整理了一 ...

  2. C语言CRC校验码计算与校验

    循环冗余校验(cyclicredundancy check,CRC)对传输序列进行一次规定的除法操作,将除法操作的余数附加在传输信息的后面.在接收端,也对收到的数据做相同的除法.如果接收端除法得到的结 ...

  3. IC 卡、M1 卡、CPU 卡、SAM 卡、PSAM 卡的联系与区别

    一. 技术方面(非接触式 IC 卡) 1. 逻辑加密卡又叫存储卡,卡内的集成电路具有加密逻辑和 EEPROM (电可 擦除可编程只读存储器). 2. CPU 卡又叫智能卡, 卡内的集成电路包括中央处理 ...

  4. 四个方面比较CPU卡加密系统与M1加密系统

    http://www.dzsc.com/data/html/2010-11-24/87326.html 密钥管理系统(Key Management System),也简称KMS,是IC项目安全的核心. ...

  5. CPU卡加密系统与M1加密系统比较!

    非接触CPU卡与逻辑加密卡 1.          逻辑加密存储卡:在非加密存储卡的基础上增加了加密逻辑电路,加密逻辑电路通过校验密码方式来保护卡内的数据对于外部访问是否开放,但只是低层次的安全保护, ...

  6. CPU卡加密系统与M1加密系统比较

    非接触CPU卡与逻辑加密卡 1. 逻辑加密存储卡:在非加密存储卡的基础上增加了加密逻辑电路,加密逻辑电路通过校验密码方式来保护卡内的数据对于外部访问是否开放,但只是低层次的安全保护,无法防范恶意性的攻 ...

  7. CPU卡PSAM卡 响应指令

    http://m.blog.csdn.net/xinxinsky/article/details/52315592             PIN 二进制文件:数据以字节为单位进行读写,每次读写的长度 ...

  8. CRC 计算及校验原理

    第一步 给定要发送的数据码 原数据码 =10110011 第二步 根据给定的生成多项式,确定除数码, 除数码 = 11001 第三步 根据生成多项式的最高阶次幂(此处为4),在数据码后补 4个0 新数 ...

  9. 一个计算crc校验的小工具的编写。

    计算LPC校验 计算CRC8校验 计算CRC16校验 计算多种校验(没有分离单个界面显示) 可以作为平时使用的一个小工具 下载链接:https://download.csdn.net/download ...

最新文章

  1. 操作临时表+事务级别临时表操作+会话级别临时表操作
  2. jQuery mobile 图标
  3. B组前导码功率偏移(messagePowerOffsetGroupB)
  4. ajax获取数据用弹窗显示_Vue之 点击返回弹出推荐商品弹窗
  5. python note 29 线程创建
  6. 论文浅尝 | 基于复杂查询图编码的知识库问答
  7. 【剑指offer】面试题58 - I:翻转单词顺序(Java)
  8. 三周的 软件工程实践课 课程安排建议
  9. Redis入门(三)Redis的基本数据类型
  10. 我新买的GTX1050为何装上去一直是黑屏
  11. 【302】C# TreeView 控件使用说明
  12. linux系统的总父目录,Linux虚拟文件系统-资料路径名的解析(2)-回退父目录
  13. 安卓版的水经注地图_水经注万能地图下载器
  14. Linux驱动-内核uart串口驱动分析
  15. 269个JavaScript工具函数,助你提升工作效率(2)
  16. lammps+colvars计算PMF
  17. (二)Java并行程序基础
  18. java word导出表格_Java Word模板导出包含表格单元格合并
  19. HttpReques
  20. [参考]C# JSON字符串序列化与反序列化

热门文章

  1. 电子样本管理系统v1 v1.1.3
  2. RocketMQ报错提示logics disk maybe full soon, so reclaim space, -1.0
  3. 团队管理11--管理规划四要素及四步走
  4. 【STM32学习笔记】(14)——独立看门狗(IWDG)详解
  5. 无参考图像质量评价之可察觉模糊程度方法(JNB)
  6. 中国软考2006年各地报名方式和咨询地址
  7. 【Excel 教程系列第 7 篇】多张图片如何居中单元格?
  8. Unable to locate Attribute with the the given name [] on this ManagedType
  9. 循环匹配出图片地址(即src属性)
  10. uniapp图片地址以文件流的形式传给后端