针对银联生成ARQC,ARPC,还有MAC进行了软加密实现,一般的银行都是进行调用加密机实现,为了方便测试使用自己对其进行了软加密算法实现:

基本帮助类如下:

package com.omini.common.utils;import java.nio.ByteBuffer;
import java.util.Arrays;/*** @author sandy* @version $Revision: 1.1 $ 建立日期 2012-9-11*/
public class StringUtils
{/*** 抽取字符或是数字 若isNumber传入true则表示抽取数字,false则表示抽取字符* * @param result* @param isNumber* @return*/public static String extract(String result, boolean isNumber){if (null == result || result.equals("") || result.length() == 0){throw new IllegalArgumentException("参数不正确,不能为空!");}StringBuffer resultBuffer = new StringBuffer();char[] chars = result.toUpperCase().toCharArray();for (char c : chars){boolean flag = Character.isDigit(c);if (isNumber && flag){resultBuffer.append(c);}if (!flag && !isNumber){resultBuffer.append(c);}}return resultBuffer.toString();}/*** 减去10* * @param input* @return*/public static String divide(String input){if (null == input || input.equals("") || input.length() == 0){throw new IllegalArgumentException("参数不正确,不能为空!");}char[] output = new char[input.length()];for (int i = 0; i < input.length(); i++){if (output[i] > 96){output[i] = (char) (output[i] - 49);} else if (output[i] > 64){output[i] = (char) (output[i] - 17);} else{output[i] = output[i];}}return Arrays.toString(output);}/*** 追加字符到指定长度的字符* * @param srcData*            :原数据* @param alignMode*            :对齐方式* @param paddCharacter*            :填补的字符* @param totalLen*            :填充到的长度* @return*/public static String padding(String srcData, String alignMode, String paddCharacter, int totalLen){if (srcData == null || null == alignMode || null == paddCharacter || totalLen == 0){throw new IllegalArgumentException("传入的数据不能为空或0,请检查数据!");}int paddLen = totalLen - srcData.length();StringBuffer paddResultBuffer = new StringBuffer();if (alignMode.equalsIgnoreCase("left")){for (int i = 0; i < paddLen; i++){paddResultBuffer.append(paddCharacter);}paddResultBuffer.append(srcData);} else if (alignMode.equalsIgnoreCase("right")){paddResultBuffer.append(srcData);for (int i = 0; i < paddLen; i++){paddResultBuffer.append(paddCharacter);}} else{throw new IllegalArgumentException("paddAlign  is not left or right,please check !");}return paddResultBuffer.toString();}/*** 两个数据进行异或操作* * @param hexSrcData1*            :32CB95B36D89477C* @param hexSrcData2*            :3030000000000000* @return*/public static String XOR(String hexSrcData1, String hexSrcData2){if (hexSrcData1.length() != hexSrcData2.length()){throw new IllegalArgumentException("异或的两个数据长度不相等,请检查数据!");}byte[] bytes1 = HexBinary.decode(hexSrcData1);byte[] bytes2 = HexBinary.decode(hexSrcData2);ByteBuffer buffer = ByteBuffer.allocate(bytes2.length);for (int i = 0; i < bytes2.length; i++){byte temp = (byte) ((int) bytes1[i] ^ (int) bytes2[i]);buffer.put(temp);}return HexBinary.encode(buffer.array());}/*** 按位取反操作* * @param hexSrcData* @return*/public static String reversBytes(String hexSrcData){if (null == hexSrcData || hexSrcData.equals("") || hexSrcData.length() == 0){throw new IllegalArgumentException("非法的按位取反的数据,请检查数据");}byte[] srcBytes = HexBinary.decode(hexSrcData);ByteBuffer destBuffer = ByteBuffer.allocate(srcBytes.length);for (int i = 0; i < srcBytes.length; i++){byte temp = (byte) (~(int) srcBytes[i]);destBuffer.put(temp);}return HexBinary.encode(destBuffer.array());}}
package com.omini.common.utils;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;/*** @author sandy* @version $Revision: 1.1 $ 建立日期 2012-9-11*/
public class DESUtils
{/*** DES加密数据非填充方式* * @param hexKey* @param hexData* @param mode* @return* @throws Exception*/public static String decEncNoPaddingDES(String hexKey, String hexData, int mode) throws Exception{SecretKey desKey = new SecretKeySpec(HexBinary.decode(hexKey), "DES/ECB/NoPadding");Cipher cp = Cipher.getInstance("DES/ECB/NoPadding");cp.init(mode, desKey);byte[] bytes = cp.doFinal(HexBinary.decode(hexData));return HexBinary.encode(bytes);}public static String encrypt(String hexKey, String hexData) throws Exception{SecretKey desKey = new SecretKeySpec(HexBinary.decode(hexKey), "DES");Cipher cp = Cipher.getInstance("DES");cp.init(Cipher.ENCRYPT_MODE, desKey);byte[] bytes = cp.doFinal(HexBinary.decode(hexData));return HexBinary.encode(bytes);}/*** 3Des加密非填充* * @param hexKey* @param hexData* @return* @throws Exception*/public static String encryptDesSede(String hexKey, String hexData) throws Exception{SecretKey desKey = new SecretKeySpec(HexBinary.decode(hexKey), "DESede/ECB/NoPadding");Cipher cp = Cipher.getInstance("DESede/ECB/NoPadding");cp.init(Cipher.ENCRYPT_MODE, desKey);byte[] bytes = cp.doFinal(HexBinary.decode(hexData));return HexBinary.encode(bytes);}}
package com.omini.common.utils;/*** @author sandy* @version $Revision: 1.1 $ 建立日期 2012-9-11*/
public class HexBinary
{/*** Creates a clone of the given byte array.*/public static byte[] getClone(byte[] pHexBinary){byte[] result = new byte[pHexBinary.length];System.arraycopy(pHexBinary, 0, result, 0, pHexBinary.length);return result;}/*** Converts the string <code>pValue</code> into an array of hex bytes.*/public static byte[] decode(String pValue){if ((pValue.length() % 2) != 0){throw new IllegalArgumentException("A HexBinary string must have even length.");}byte[] result = new byte[pValue.length() / 2];int j = 0;for (int i = 0; i < pValue.length();){byte b;char c = pValue.charAt(i++);char d = pValue.charAt(i++);if (c >= '0' && c <= '9'){b = (byte) ((c - '0') << 4);} else if (c >= 'A' && c <= 'F'){b = (byte) ((c - 'A' + 10) << 4);} else if (c >= 'a' && c <= 'f'){b = (byte) ((c - 'a' + 10) << 4);} else{throw new IllegalArgumentException("Invalid hex digit: " + c);}if (d >= '0' && d <= '9'){b += (byte) (d - '0');} else if (d >= 'A' && d <= 'F'){b += (byte) (d - 'A' + 10);} else if (d >= 'a' && d <= 'f'){b += (byte) (d - 'a' + 10);} else{throw new IllegalArgumentException("Invalid hex digit: " + d);}result[j++] = b;}return result;}/*** Converts the byte array <code>pHexBinary</code> into a string.*/public static String encode(byte[] pHexBinary){StringBuffer result = new StringBuffer();for (int i = 0; i < pHexBinary.length; i++){byte b = pHexBinary[i];byte c = (byte) ((b & 0xf0) >> 4);if (c <= 9){result.append((char) ('0' + c));} else{result.append((char) ('A' + c - 10));}c = (byte) (b & 0x0f);if (c <= 9){result.append((char) ('0' + c));} else{result.append((char) ('A' + c - 10));}}return result.toString();}public static void main(String[] args){String a = "12";System.out.println(encode(a.getBytes()));String ab = "00000000000100000000000001560000000000015611041501112233447C00000603A00002";System.out.println("::::::::::[" + HexBinary.encode(ab.getBytes()) + "]");}}
package com.omini.common.utils;import javax.crypto.Cipher;/*** 生成ARQC,ARPC,MAC,CVN,CVN2工具类* * @author sandy* @version $Revision: 1.1 $ 建立日期 2012-9-11*/
public class UnionUtils
{/*** 其实生成ARQC就是调用的生成MAC方法,只不过MAC值是取得的前4个字节的值* * @param pan* @param panSN* @param hexATC* @param mainKey* @param arqcDataSource* @return* @throws Exception*/public static String generateARQC(String pan, String panSN, String hexATC, String mainKey, String arqcDataSource) throws Exception{if (null == mainKey || mainKey.length() != 32){throw new IllegalArgumentException("非法的工作主密钥的值");}if (pan == null || pan.equals("") || pan.length() == 0){throw new IllegalArgumentException("卡号不能为空,请检查传入的卡号");}String processKey = generateProcesKey(pan, panSN, hexATC, mainKey);String result = process(processKey, arqcDataSource);if (result.length() != 16){throw new IllegalArgumentException("返回的mac结果非8字节(16位hex)");}return result;}/*** 根据ARQC来生成ARPC* * @param hexArqc表示ARQC的值8字节* @param pan*            :表示卡号* @param panSN*            :表示卡序号00或01* @param hexATC*            :交易记数器* @param mainKey*            :工作主密钥* @param authCode*            :授权响应码2个字节* @return* @throws Exception*/public static String generateARPC(String hexArqc, String pan, String panSN, String hexATC, String mainKey, String authCode) throws Exception{if (null == mainKey || mainKey.length() != 32){throw new IllegalArgumentException("非法的工作主密钥的值");}if (pan == null || pan.equals("") || pan.length() == 0){throw new IllegalArgumentException("卡号不能为空,请检查传入的卡号");}if (hexArqc == null || hexArqc.equals("") || pan.length() == 0 || hexArqc.length() != 16){throw new IllegalArgumentException("非法的ARQC数据");}String processKey = generateProcesKey(pan, panSN, hexATC, mainKey);String paddARC = StringUtils.padding(authCode, "right", "0", 16);String arqcAndARCXORResult = StringUtils.XOR(hexArqc, paddARC);String arpc = DESUtils.encryptDesSede(processKey, arqcAndARCXORResult);System.out.println(arpc);return arpc;}/*** 生成MAC,并获取计算后的前4个字节 命令中需要加密的数据加密以后再计算MAC。MAC使用对称密钥算法计算的, 步骤如下: 步骤1:初始值为8* 字节全零(此步骤可省略); 步骤2:下列数据按顺序排列得到一个数据块D: ——CLA、INS、P1、P2 和Lc(Lc 的长度包括MAC* 的长度); ——ATC(对于发卡行脚本处理,此ATC 在请求中报文中上送);* ——应用密文(对于发卡行脚本处理,此应用密文通常是ARQC,或AAC,在请求报文中上送); ——命令数据域中的明文或密文数据(如果存在)。* 步骤3:将上述数据块D 分成8 字节长的数据块D1、D2、D3…最后一块数据块的字节长度为1 到8; 步骤4:如果最后一块数据块的长度为8* 字节,后面补8 字节数据块:80 00 00 00 00 00 00 00, 执行步骤5; 如果最后一块数据块的长度小于8* 字节,后面补一个字节80,如果长度到8 字节,执行 步骤5。如果仍然不够8 字节,补00 直到8 字节; 步骤5:用MAC* 过程密钥对数据块进行加密。MAC 过程密钥的生成见C.4; 图C.1 是使用MAC 过程密钥A 和B 生成MAC 的流程图。 步骤6:MAC* 的计算结果为8 字节,从最左边的字节开始取4 字节* * @param pan* @param panSN* @param hexATC* @param mainKey* @param macDataSource* @return* @throws Exception*/public static String generateMAC(String pan, String panSN, String hexATC, String mainKey, String macDataSource) throws Exception{if (null == mainKey || mainKey.length() != 32){throw new IllegalArgumentException("非法的工作主密钥的值");}if (pan == null || pan.equals("") || pan.length() == 0){throw new IllegalArgumentException("卡号不能为空,请检查传入的卡号");}String processKey = generateProcesKey(pan, panSN, hexATC, mainKey);String result = process(processKey, macDataSource);if (result.length() != 16){throw new IllegalArgumentException("返回的mac结果非8字节(16位hex)");}result = result.substring(0, 8);return result;}/*** 计算CVN时使用二个64位的验证密钥,KeyA和KeyB。 a) 计算CVN 的数据源包括:* 主账号(PAN)、卡失效期和服务代码,从左至右顺序编排。 例如19位PAN、4位卡失效期和3位服务代码组成26个字符CVN数据源。 b)* 将上述数据源扩展成128 位二进制数据(不足128 位右补二进制0)。 c) 将128 位二进制数据分成两个64 位的数据块。最左边的64* 位为Block1,最右边的64 位为 Block2。 d) 使用KeyA 对Block1 进行加密。 e) 将Block1* 的加密结果与Block2 进行异或。使用KeyA 对异或结果进行加密。 f) 使用KeyB 对加密结果进行解密。 g) 使用KeyA* 对解密结果进行加密。 h) 从左至右将加密结果中的数字(0-9)抽出,组成一组数字。 i) 从左至右将加密结果中的字符(A-F)抽出,减10* 后将余数组成一组数字,排列在步骤(8) 的数字之后。 j) 步骤(9)的左边第一组三位数即为CVN 值* * @param pan* @param invalidDate* @param serviceCode* @param hexKey:验证密钥* @return* @throws Exception*/public static String generateCVN(String pan, String invalidDate, String serviceCode, String hexKey) throws Exception{if (null == pan || null == invalidDate || null == serviceCode || null == hexKey){throw new IllegalArgumentException("卡号或是失效日期或服务代码或验证密钥为空!");}if (hexKey.length() != 32){throw new IllegalArgumentException("验证密钥长度非32位!");}String keyA = hexKey.substring(0, 16);String keyB = hexKey.substring(16);StringBuffer cvnDataSource = new StringBuffer();cvnDataSource.append(pan).append(invalidDate).append(serviceCode);String cvnDS = StringUtils.padding(cvnDataSource.toString(), "right", "0", 32);String blockA = cvnDS.substring(0, 16);String blockB = cvnDS.substring(16);String keyAEncryptBlockAResult = DESUtils.decEncNoPaddingDES(keyA, blockA, Cipher.ENCRYPT_MODE);String xorBlockBResult = StringUtils.XOR(keyAEncryptBlockAResult, blockB);String result = DESUtils.decEncNoPaddingDES(keyA, xorBlockBResult, Cipher.ENCRYPT_MODE);result = DESUtils.decEncNoPaddingDES(keyB, result, Cipher.DECRYPT_MODE);result = DESUtils.decEncNoPaddingDES(keyA, result, Cipher.ENCRYPT_MODE);String numberData = StringUtils.extract(result, true);String characterData = StringUtils.extract(result, false);characterData = StringUtils.divide(characterData);result = numberData + characterData;if (result.length() < 3){throw new IllegalArgumentException("计算CVN返回的长度小于3位长度不正确");}result = result.substring(0, 3);return result;}/*** 印刷在签名条的右上方处并放在卡号(后4位)后* * 生成CVN2其实就是把服务码变成常数000即可* * @param pan:卡号* @param invalidDate:失效日期* @param serviceCode:服务码* @param hexKey:验证密钥* @return* @throws Exception*/public static String generateCVN2(String pan, String invalidDate, String serviceCode, String hexKey) throws Exception{return generateCVN(pan,invalidDate,"000",hexKey);}/*** 生成过程密钥* * @param pan* @param panSN* @param hexATC* @param mainKey* @return* @throws Exception*/private static String generateProcesKey(String pan, String panSN, String hexATC, String mainKey) throws Exception{int cardNoLength = pan.length();String cardNoRight14 = pan.substring(cardNoLength - 14);// 分散因子String dispersionFactor = cardNoRight14 + panSN;// 对分散因子取反String reversDispersionFactor = StringUtils.reversBytes(dispersionFactor);StringBuffer dispersionBuffer = new StringBuffer();dispersionBuffer.append(dispersionFactor).append(reversDispersionFactor);// 生成子密钥String subKey = DESUtils.encryptDesSede(mainKey, dispersionBuffer.toString());String paddATC = StringUtils.padding(hexATC, "left", "0", 16);String reversATC = StringUtils.reversBytes(hexATC);String paddReversATC = StringUtils.padding(reversATC, "left", "0", 16);String mergerATC = paddATC + paddReversATC;// 生成过程密钥String processKey = DESUtils.encryptDesSede(subKey, mergerATC);return processKey;}/*** 计算MAC处理* * @param processKey*            :过程密钥* @param macDataSource*            :计算MAC的数据源* @return* @throws Exception*/private static String process(String processKey, String macDataSource) throws Exception{if (null == processKey || processKey.equals("") || processKey.length() != 32){throw new IllegalArgumentException("过程密钥不能为空或不够32位!");}String leftKey = processKey.substring(0, 16);String rightKey = processKey.substring(16);// 拆分MAC数据源,每组16位hex(8 byte())String[] ds = splitData(macDataSource);String des = "";for (int i = 0; i < ds.length; i++){if (i == 0){// 第一次只做DES加密des = DESUtils.decEncNoPaddingDES(leftKey, ds[i], Cipher.ENCRYPT_MODE).toUpperCase();} else{// 用上一次 DES加密结果对 第 i 组数据做异或des = StringUtils.XOR(des, ds[i]);// 对异或后的数据做DES加密des = DESUtils.decEncNoPaddingDES(leftKey, des, Cipher.ENCRYPT_MODE).toUpperCase();}}// DES 加密最终结果用processKey后16位解密des = DESUtils.decEncNoPaddingDES(rightKey, des, Cipher.DECRYPT_MODE).toUpperCase();// 解密后 再用processKey前16位加密des = DESUtils.decEncNoPaddingDES(leftKey, des, Cipher.ENCRYPT_MODE).toUpperCase();return des;}/** 将hexMacDataSource进行分组 每 16 字符 8byte 一组*/private static String[] splitData(String hexMacDataSource){int len = 0;int modValue = hexMacDataSource.length() % 16;if (modValue == 0){// 补上80000000000000hexMacDataSource += "80000000000000";len = hexMacDataSource.length() / 16;} else if (modValue == 14){// 补上80hexMacDataSource += "80";len = hexMacDataSource.length() / 16;} else{hexMacDataSource += "80";int hexSrcDataLen = hexMacDataSource.length();int totalLen = hexSrcDataLen + (16 - modValue - 2);hexMacDataSource = StringUtils.padding(hexMacDataSource, "right", "0", totalLen);len = hexMacDataSource.length() / 16;}String[] ds = new String[len];for (int i = 0; i < ds.length; i++){if (hexMacDataSource.length() >= 16){ds[i] = hexMacDataSource.substring(0, 16);hexMacDataSource = hexMacDataSource.substring(16);} else{throw new IllegalArgumentException("填充的数据非法!");}}return ds;}}

测试代码如下:

package com.omini.common.utils;/*** @author sandy* @version $Revision: 1.1 $ 建立日期 2012-9-11*/
public class UnionUtilsTest
{public static void main(String[] args) throws Exception{// UnionUtilsTest.generateMACTest();UnionUtilsTest.generateCVN();}public static void generateARPCTest() throws Exception{String arpc = UnionUtils.generateARPC("32CB95B36D89477C", "6214618888000002074", "00", "0008", "00000000000000000000000000000000", "3030");System.out.println("arpc=" + arpc);}public static void generateMACTest() throws Exception{String result = UnionUtils.generateMAC("6214618888000002074", "00", "0029", "00000000000000000000000000000000", "04DA9F790A00299E99DA1521DAA0A3000000000000");System.out.println("MAC=" + result);}public static void generateARQCTest() throws Exception{String result = UnionUtils.generateARQC("6214610200000004163", "01", "000B", "77777777777777777777777777777777", "00000001000000000000000001568008000800015610052801112233447C00000B03A03000");System.out.println("ARQC=" + result);}public static void generateCVN() throws Exception{String result = UnionUtils.generateCVN("6221234567891234", "0712", "111", "0123456789ABCDEFFEDCBA9876543210");System.out.println("CVN=" + result);}public static void generateCVN2() throws Exception{String result = UnionUtils.generateCVN("6221234567891234", "0712", "000", "0123456789ABCDEFFEDCBA9876543210");System.out.println("CVN2=" + result);}}

银联生成ARQC,ARPC,MAC,CVN生成相关推荐

  1. Vue中使用uuidv1根据时间戳和MAC地址生成唯一标识

    场景 若依前后端分离版手把手教你本地搭建环境并运行项目: 若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客_若依前后端分离搭建 在上面搭建起来Vue项目的基础上,怎样 ...

  2. python生成文件夹并向文件夹写文件_python - 文件练习生成100个MAC地址写入文件

    需求: 生成100个MAC地址并写入文件中,MAC地址前6位(16进制)为01-AF-3B 解题思路: 要求生成这样格式的mac地址:01-AF-3B-xx-xx-xx 首先生成-xx格式,16进制组 ...

  3. android uboot获取mac地址,uboot生成随机的MAC地址

    uboot生成随机的MAC地址 R.wen 由于设备量产需要每台机器的MAC地址都不一样,所以我们给Uboot增加一个环境变量sysready,用来表示如果系统没有ready,就做一些额外的初始化工作 ...

  4. Python实现生成100个MAC地址并写入文件中,MAC地址前6位(16进制)为01-AF-3B

    题目要求: 生成100个MAC地址并写入文件中,MAC地址前6位(16进制)为01-AF-3B 01-AF-3B 01-AF-3B-xx 01-AF-3B-xx-xx 01-AF-3B-xx-xx-x ...

  5. mac序列号生成主板号_如何查找Mac的序列号(即使您没有Mac也是如此)

    mac序列号生成主板号 Your Mac's serial number is a unique identifier that distinguishes your Mac from all oth ...

  6. MAC下 生成安卓签名证书.keystore文件【详细】

    在安卓打包.APK文件的过程中,是需要选择一个. keystore 即安卓数字签名证书.那今天我们就来看看这个,证书是怎么从无到有,一步步的生成的. windows平台生成证书的方法,点击去看看 检查 ...

  7. Android 对apk进行重签名和查看签名(window 和mac)及生成签名

    生成签名文件:其实是有很多工具可以做到,这里不过是想用命令来生成 其命令如下:生成的签名默认在c盘根目录下 keytool -genkey -alias aaaa.keystore -keyalg R ...

  8. VMware为Linux生成新的Mac地址(克隆虚拟机时可以用)

    1.VMware为Linux生成新的Mac地址 2.确保以下三处Mac地址保持一致,且将三处的Mac地址设置为第1步中新生成的Mac地址 解决方法如下图(Centos6版本): 注: Centos7. ...

  9. python随机生成一个地区地址_Python生成随机MAC地址

    利用python代码生成一个随机的MAC地址,使用python网络编程时或可用上,如果使用scapy模块则可直接利用RandMAC()函数来生成MAC. python 复制代码 代码如下: impor ...

最新文章

  1. selenium python (七)层级定位(二次定位)
  2. Swift 十进制二进制转换 (How to convert a decimal number to binary in Swift)
  3. EasyUI中Accordion折叠面板的简单使用
  4. 04_有序清单无序清单
  5. PE文件结构详解(六)重定位
  6. System.Xml名称空间下的支持DOM的类型
  7. 深入理解InnoDB(8)—单表访问
  8. 【转载】MySQL innodb_table_stats表不存在的解决方法
  9. 从 AVFrame 中取出帧(YUV)保存为 Mat 格式
  10. 叫号系统服务器,排队叫号系统设置方法
  11. App Store 受欢迎榜单(美国区)-分析出未来方向
  12. android遥控杆控件,Android自定义滑杆控件SeekBar多功能版本
  13. comsume(comsumer怎么读)
  14. kali字体设置-各种字体图标大小调整总结
  15. Ui自动化概念+Web自动化测试框架介绍
  16. 关于UBNT网桥真实吞吐量
  17. 关于无人机,你需要知道的都在这里了
  18. Premiere动态文本预设 800+文字动画动态展示PR/AE预设模板
  19. Azkaban soloserver
  20. Swift - 第三方图表库Charts使用详解4(折线图3:选中点高亮、十字线样式)

热门文章

  1. jsonp 跨域接收值接不到的解决方法
  2. 企业网络营销推广常见的办法有哪些?
  3. linux版本石器时代,石器时代 架设教程Linux版linux服务器应用 -电脑资料
  4. 按摩椅申请ISO 8191 香烟阴燃测试
  5. c#认证题第一单元 1题 11题.
  6. ST-Link下载程序出错
  7. poc白皮书通证经济体系_区块链中的PoC共识算法是什么意思?
  8. SpringBoot (三) 整合数据库访问 jdbcTemplate、MyBatis
  9. 达内android培训,达内Android培训—EGL 1.0 学习笔记
  10. Bill Gates Centimillionaire and one poor man.