須要用到的工具類,代碼以下:java
import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * @ClassName: CommonUtils * @Description: 通用工具類 * @since: 0.0.1 * @author: dzy * @date: 2017年2月22日 上午11:46:44 */ public class CommonUtils { /** * @param date 日期 * @param pattern 模式 如:yyyyMMdd等 * @return * @Title: formatDate * @Description: 格式化日期 * @since: 0.0.1 */ public static String formatDate(Date date, String pattern) { SimpleDateFormat formatter = new SimpleDateFormat(pattern); return formatter.format(date); } /** * @param strDate String類型日期 * @param pattern 日期顯示模式 * @return * @Title: parseDate * @Description: 將String日期轉換爲Date類型日期 * @since: 0.0.1 */ public static Date parseDate(String strDate, String pattern) { SimpleDateFormat formatter = null; if (StringUtils.isBlank(strDate)) { return null; } formatter = new SimpleDateFormat(pattern); try { return formatter.parse(strDate); } catch (Exception e) { e.printStackTrace(); return null; } } /** * @param date 操做前的日期 * @param field 日期的部分如:年,月,日 * @param amount 增長或減小的值(負數表示減小) * @return * @Title: dateAdd * @Description: 日期的加減操做 * @since: 0.0.1 */ public static Date dateAdd(Date date, int field, int amount) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(field, amount); return calendar.getTime(); } /** * @param source 源字符串 * @param offset 填充開始的位置, 0-在左邊, source.getBytes().length 在右邊, 若是有中文時需當心位置 * @param c 用於填充的字符 * @param length 最後字符串的字節長度 * @return * @Title: fill * @Description: 填充字符串, 長度是按字節計算, 不是字符 * @since: 0.0.1 */ public static String fill(String source, int offset, char c, int length) throws UnsupportedEncodingException { if (null == source) { source = ""; } if (source.getBytes(CustomConstants.CHARSET_UTF8).length == length) { return source; } byte[] buf = new byte[length]; byte[] src = source.getBytes(CustomConstants.CHARSET_UTF8); if (src.length > length) { System.arraycopy(src, src.length - length, buf, 0, length); return new String(buf, CustomConstants.CHARSET_UTF8); } if (offset > src.length) { offset = src.length; } else if (offset < 0) { offset = 0; } int n = length - src.length; System.arraycopy(src, 0, buf, 0, offset); for (int i = 0; i < n; i++) { buf[i + offset] = (byte) c; } System.arraycopy(src, offset, buf, offset + n, src.length - offset); return new String(buf, CustomConstants.CHARSET_UTF8); } /** * @param original 原字符串 * @param offset 填充開始的位置, 0-在左邊, original.getBytes().length 在右邊, 若是有中文時需當心位置 * @param length 替換的字節數 * @param c 用於替換的字符 * @return * @Title: replace * @Description: 替換字符串, 長度是按字節計算, 不是字符 * @since: 0.0.1 */ public static String replace(String original, int offset, int length, char c) throws UnsupportedEncodingException { if (original == null) { original = ""; } if (original.getBytes(CustomConstants.CHARSET_UTF8).length <= offset) { return original; } if (original.getBytes(CustomConstants.CHARSET_UTF8).length < offset + length) { length = original.getBytes(CustomConstants.CHARSET_UTF8).length - offset; } byte[] buf = new byte[original.length()]; byte[] src = original.getBytes(CustomConstants.CHARSET_UTF8); System.arraycopy(src, 0, buf, 0, offset); for (int i = offset; i < offset + length; i++) { buf[i] = (byte) c; } System.arraycopy(src, offset + length, buf, offset + length, src.length - offset - length); return new String(buf, CustomConstants.CHARSET_UTF8); } /** * @param s 16進制字符串 * @return * @Title: hexToByte * @Description: 16進制字符串轉字節數組 * @since: 0.0.1 */ public static byte[] hexToByte(String s) { byte[] result = null; try { int i = s.length(); // if (i % 2 == 1) { // throw new Exception("字符串長度不是偶數."); // } if (i % 2 != 0) { throw new Exception("字符串長度不是偶數."); } result = new byte[i / 2]; for (int j = 0; j < result.length; j++) { result[j] = (byte) Integer.parseInt(s.substring(j * 2, j * 2 + 2), 16); } } catch (Exception e) { result = null; e.printStackTrace(); // log.error("16進制字符串轉字節數組時出現異常:", e); } return result; } /** * @param bytes 字節數組 * @return * @Title: byte2hexString * @Description: 字節數組轉換爲16進制字符串 //0x33 0xD2 0x00 0x46 轉換爲 "33d20046" 轉換和打印報文用 * @since: 0.0.1 */ public static String byte2hexString(byte[] bytes) { StringBuffer buf = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { if (((int) bytes[i] & 0xff) < 0x10) { buf.append("0"); } buf.append(Long.toString((int) bytes[i] & 0xff, 16)); } return buf.toString().toUpperCase(); } /** * @param hexString 16進制字符串 如:"33d20046" 轉換爲 0x33 0xD2 0x00 0x46 * @return * @Title: hexString2byte * @Description: 16進制字符串轉字節數組 * @since: 0.0.1 */ public static byte[] hexString2byte(String hexString) { if (null == hexString || hexString.length() % 2 != 0 || hexString.contains("null")) { return null; } byte[] bytes = new byte[hexString.length() / 2]; for (int i = 0; i < hexString.length(); i += 2) { bytes[i / 2] = (byte) (Integer.parseInt(hexString.substring(i, i + 2), 16) & 0xff); } return bytes; } /** * @param i 須要轉的int類型數字 * @return * @Title: byte1ToBcd2 * @Description: int類型轉BCD碼 * @since: 0.0.1 */ public static String byte1ToBcd2(int i) { // return (new Integer(i / 16).toString() + (new Integer(i % 16)).toString()); return Integer.toString(i / 16) + Integer.toString(i % 16); } /** * @param b 字節數組 * @return * @Title: byteToHex2 * @Description: 字節數組轉換爲16進制字符串 For example, byte[] {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF} will be changed to String "0123456789ABCDEF" * @since: 0.0.1 */ public static String byteToHex2(byte[] b) { StringBuffer result = new StringBuffer(); String tmp = ""; for (int i = 0; i < b.length; i++) { tmp = Integer.toHexString(b[i] & 0xff); if (tmp.length() == 1) { result.append("0" + tmp); } else { result.append(tmp); } } return result.toString().toUpperCase(); } /** * @param num 數字 * @param len 字節數組長度 * @return * @Title: intToHexBytes * @Description: int類型轉16進制字節數組 */ public static byte[] intToHexBytes(int num, int len) { byte[] bytes = null; String hexString = Integer.toHexString(num); if (len > 0) { int length = len * 2; hexString = CustomStringUtils.leftFill(hexString, '0', length); bytes = CommonUtils.hexString2byte(hexString); } return bytes; } /*public static String byteToHex3(byte[] b) { String result = ""; String tmp = ""; for (int n = 0; n < b.length; n++) { tmp = (java.lang.Integer.toHexString(b[n] & 0XFF)); if (tmp.length() == 1) { result = result + "0" + tmp; } else { result = result + tmp; } if (n < b.length - 1) { result = result + ""; } } return result.toUpperCase(); }*/ /** * @param str 須要轉換編碼的字符串 * @return * @Title: iso2Gbk * @Description: 將ISO-8859-1編碼的字符串轉成GBK編碼的字符串 * @since: 0.0.1 */ public static String iso2Gbk(String str) { if (null == str) { return str; } try { return new String(str.getBytes("ISO-8859-1"), "GBK"); } catch (UnsupportedEncodingException e) { // log.error("不支持的編碼異常:", e); e.printStackTrace(); return str; } } // /** // * @param message // * @return // * @Title: getSubElement // * @Description: 分解各子域到HashMap // * @since: 0.0.1 // */ // public static Map<String, String> getSubElement(byte[] message) { // Map<String, String> map = new HashMap<String, String>(); // String key = null; // String value = null; // int len = 0; // int idx = 0; // while (idx < message.length) { // key = new String(message, idx, 2); // idx += 2; //取了SE id 移2位 // len = Integer.parseInt(new String(message, idx, 2)); // idx += 2; //取了SE id的內容長度 移2位 // value = new String(message, idx, len); // map.put(key, value); // idx += len; // } // return map; // } //byte數組轉成long /** * @param b 將字節數組轉long類型 位置爲小端 * @return */ public static long byteToLong(byte[] b) { long s = 0; long s0 = b[0] & 0xff;// 最低位 long s1 = b[1] & 0xff; long s2 = b[2] & 0xff; long s3 = b[3] & 0xff; long s4 = b[4] & 0xff;// 最低位 long s5 = b[5] & 0xff; long s6 = b[6] & 0xff; long s7 = b[7] & 0xff; // s0不變 s1 <<= 8; s2 <<= 16; s3 <<= 24; s4 <<= 8 * 4; s5 <<= 8 * 5; s6 <<= 8 * 6; s7 <<= 8 * 7; s = s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7; return s; } /** * @param b 將字節數組轉int類型 位置爲小端 * @return */ public static int byteToInt(byte[] b) { int s = 0; int s0 = b[0] & 0xff;// 最低位 int s1 = b[1] & 0xff; int s2 = b[2] & 0xff; int s3 = b[3] & 0xff; // s0不變 s1 <<= 8; s2 <<= 16; s3 <<= 24; s = s0 | s1 | s2 | s3; return s; } /** * int類型轉換小端的byte數組 * @param i * @return */ public static byte[] intToLittleBytes(int i) { ByteBuffer byteBuffer = ByteBuffer.allocate(4); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); byteBuffer.asIntBuffer().put(i); byte[] littleBytes = byteBuffer.array(); return littleBytes; } /** * 將一個字節轉成10進制 * @param b * @return */ public static int byteToInt(byte b) { int value = b & 0xff; return value; } /** * 字節數組合並 * @param bt1 字節數組bt1 * @param bt2 字節數組bt2 * @return */ public static byte[] byteMerger(byte[] bt1, byte[] bt2){ byte[] bt3 = new byte[bt1.length+bt2.length]; System.arraycopy(bt1, 0, bt3, 0, bt1.length); System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length); return bt3; } }
DES算法總結,代碼以下:算法
import java.security.GeneralSecurityException; 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; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public final class DesUtils { private static final byte[] ZERO_IVC = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; /** * 生成祕鑰 * @return 16字節3des祕鑰 * @throws GeneralSecurityException */ public static byte[] create3DESKey() throws GeneralSecurityException { KeyGenerator kg = KeyGenerator.getInstance("DESede"); kg.init(112);//must be equal to 112 or 168 byte[] key24 = kg.generateKey().getEncoded(); byte[] result = new byte[16]; System.arraycopy(key24, 0, result, 0, 16); return result; } /** * 3DES加密cbc模式 * @param content 待加密數據 * @param key 祕鑰 * @param ivb 向量 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptBy3DesCbc(byte[] content, byte[] key, byte[] ivb) throws GeneralSecurityException { byte[] _3deskey = new byte[24]; System.arraycopy(key, 0, _3deskey, 0, 16); System.arraycopy(key, 0, _3deskey, 16, 8); Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); SecretKey secureKey = new SecretKeySpec(_3deskey, "DESede"); IvParameterSpec iv = new IvParameterSpec(ivb); cipher.init(Cipher.ENCRYPT_MODE, secureKey, iv); return cipher.doFinal(content); } /** * 3DES解密cbc模式 * @param content 待解密數據 * @param key 祕鑰 * @param ivb 向量 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptBy3DesCbc(byte[] content, byte[] key, byte[] ivb) throws GeneralSecurityException { byte[] _3deskey = new byte[24]; System.arraycopy(key, 0, _3deskey, 0, 16); System.arraycopy(key, 0, _3deskey, 16, 8); Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); SecretKey secureKey = new SecretKeySpec(_3deskey, "DESede"); IvParameterSpec iv = new IvParameterSpec(ivb); cipher.init(Cipher.DECRYPT_MODE, secureKey, iv); long start = System.currentTimeMillis(); System.out.println(start); return cipher.doFinal(content); } /** * 3DES加密cbc模式,默認向量 * @param content 待加密數據 * @param key 祕鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptBy3DesCbc(byte[] content, byte[] key) throws GeneralSecurityException { return encryptBy3DesCbc(content, key, ZERO_IVC); } /** * 3DES解密cbc模式,默認向量 * @param content 帶解密數據 * @param key 祕鑰 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptBy3DesCbc(byte[] content, byte[] key) throws GeneralSecurityException { return decryptBy3DesCbc(content, key, ZERO_IVC); } /** * 3DES加密Ecb模式 * @param content 待加密數據 * @param key 加密祕鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptBy3DesEcb(byte[] content, byte[] key) throws GeneralSecurityException { byte[] _3deskey = new byte[24]; System.arraycopy(key, 0, _3deskey, 0, 16); System.arraycopy(key, 0, _3deskey, 16, 8); Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); SecretKey secureKey = new SecretKeySpec(_3deskey, "DESede"); cipher.init(Cipher.ENCRYPT_MODE, secureKey); return cipher.doFinal(content); } /** * 3DES解密Ecb模式 * @param content 待解密數據 * @param key 祕鑰 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptBy3DesEcb(byte[] content, byte[] key) throws GeneralSecurityException { byte[] _3deskey = new byte[24]; System.arraycopy(key, 0, _3deskey, 0, 16); System.arraycopy(key, 0, _3deskey, 16, 8); Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); SecretKey secureKey = new SecretKeySpec(_3deskey, "DESede"); cipher.init(Cipher.DECRYPT_MODE, secureKey); return cipher.doFinal(content); } /** * 3DES加密Ecb模式(3倍密鑰長) * @param content 待加密數據 * @param key 加密祕鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptBy3DesEcbThreeThreeTimes(byte[] content, byte[] key) throws GeneralSecurityException { if (key.length != 24) { throw new RuntimeException("密鑰長度不是24."); } Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); SecretKey secureKey = new SecretKeySpec(key, "DESede"); cipher.init(Cipher.ENCRYPT_MODE, secureKey); return cipher.doFinal(content); } /** * 3DES解密Ecb模式((3倍密鑰長)) * @param content 待解密數據 * @param key 祕鑰 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptBy3DesEcbThreeThreeTimes(byte[] content, byte[] key) throws GeneralSecurityException { if (key.length != 24) { throw new RuntimeException("密鑰長度不是24."); } Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding"); SecretKey secureKey = new SecretKeySpec(key, "DESede"); cipher.init(Cipher.DECRYPT_MODE, secureKey); return cipher.doFinal(content); } /** * des的cbc模式加密算法 * @param content 待加密數據 * @param key 密鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptByDesCbc(byte[] content, byte[] key) throws GeneralSecurityException { return encryptByDesCbc(content, key, ZERO_IVC); } /** * des的cbc模式解密算法 * @param content 待解密數據 * @param key 密鑰 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptByDesCbc(byte[] content, byte[] key) throws GeneralSecurityException { return decryptByDesCbc(content, key, ZERO_IVC); } /** * des的cbc模式加密算法 * @param content 待加密數據 * @param key 加密密鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptByDesCbc(byte[] content, byte[] key, byte[] icv) throws GeneralSecurityException { SecureRandom sr = new SecureRandom(); DESKeySpec dks = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(dks); Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding"); IvParameterSpec iv = new IvParameterSpec(icv); cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv, sr); return cipher.doFinal(content); } /** * des的cbc模式解密算法 * @param content 待解密數據 * @param key 密鑰 * @return 解密結果 * @throws GeneralSecurityException */ public static byte[] decryptByDesCbc(byte[] content, byte[] key, byte[] icv) throws GeneralSecurityException { SecureRandom sr = new SecureRandom(); DESKeySpec dks = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(dks); Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding"); IvParameterSpec iv = new IvParameterSpec(icv); cipher.init(Cipher.DECRYPT_MODE, secretKey, iv, sr); return cipher.doFinal(content); } /** * des加密算法,ECB方式,NoPadding模式,數據字節必須是8的整數倍 * @param content 數據字節必須是8的整數倍 * @param key 密鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static byte[] encryptByDesEcb(byte[] content, byte[] key) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(new DESKeySpec(key)); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(content); } /** * des解密算法,ECB方式,NoPadding模式,數據字節必須是8的整數倍 * @param content 數據字節必須是8的整數倍 * @param key 密鑰 * @throws GeneralSecurityException * @return */ public static byte[] decryptByDesEcb(byte[] content, byte[] key) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(new DESKeySpec(key)); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(content); } /** * 本項目用於生成外部設備認證碼和校驗卡認證碼(javacard GP規範 SCP02安全通道)(cardCryptogram) * B.1.2.1 Full Triple DES MAC * The full triple DES MAC is as defined in [ISO 9797-1] as MAC Algorithm 1 with output transformation 3, * without truncation, and with triple DES taking the place of the block cipher. * @param content 待加密數據 * @param key 加密密鑰 * @return 加密結果後8字節 * @throws Exception */ public static byte[] encryptBy3DesCbcLast8Mac(byte[] content, byte[] key) throws GeneralSecurityException { byte[] edata = encryptBy3DesCbc(content, key); byte[] result = new byte[8]; System.arraycopy(edata, edata.length - 8, result, 0, 8); return result; } /** * 將b1和b2作異或,而後返回 * @param b1 * @param b2 * @return 異或結果 */ public static byte[] xOr(byte[] b1, byte[] b2) { byte[] tXor = new byte[Math.min(b1.length, b2.length)]; for (int i = 0; i < tXor.length; i++) tXor[i] = (byte) (b1[i] ^ b2[i]); // 異或(Xor) return tXor; } /** * 整形轉字節 * @param n 整形數值 * @param buf 結果字節數組 * @param offset 填充開始位置 */ public static void int2byte(int n, byte buf[], int offset){ buf[offset] = (byte)(n >> 24); buf[offset + 1] = (byte)(n >> 16); buf[offset + 2] = (byte)(n >> 8); buf[offset + 3] = (byte)n; } /** * 長整形轉字節 * @param n 長整形數值 * @param buf 結果字節數組 * @param offset 填充開始位置 */ public static void long2byte(long n, byte buf[], int offset){ buf[offset] = (byte)(int)(n >> 56); buf[offset + 1] = (byte)(int)(n >> 48); buf[offset + 2] = (byte)(int)(n >> 40); buf[offset + 3] = (byte)(int)(n >> 32); buf[offset + 4] = (byte)(int)(n >> 24); buf[offset + 5] = (byte)(int)(n >> 16); buf[offset + 6] = (byte)(int)(n >> 8); buf[offset + 7] = (byte)(int)n; } /** * @Title: hexString2byte * @Description: 16進制字符串轉字節數組 * @since: 0.0.1 * @param hexString 16進制字符串 如:"33d20046" 轉換爲 0x33 0xD2 0x00 0x46 * @return */ public static byte[] hexString2byte(String hexString) { if (null == hexString || hexString.length() % 2 != 0) { return null; } byte[] bytes = new byte[hexString.length() / 2]; for (int i = 0; i < hexString.length(); i+=2) { bytes[i / 2] = (byte) (Integer.parseInt(hexString.substring(i, i + 2), 16) & 0xff); } return bytes; } /** * @Title: byte2hexString * @Description: 字節數組轉換爲16進制字符串 //0x33 0xD2 0x00 0x46 轉換爲 "33d20046" 轉換和打印報文用 * @since: 0.0.1 * @param bytes 字節數組 * @return */ public static String byte2hexString(byte[] bytes) { StringBuffer buf = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { if (((int)bytes[i] & 0xff) < 0x10) { buf.append("0"); } buf.append(Long.toString((int) bytes[i] & 0xff, 16)); } return buf.toString().toUpperCase(); } /** * 3DES加密Ecb模式 * @param data 待加密數據 * @param key 加密祕鑰 * @return 加密結果 * @throws GeneralSecurityException */ public static String encryptBy3DesEcb(String data, String key) { byte[] content = hexString2byte(data); byte[] deskey = hexString2byte(key); String hex2S = ""; try { byte[] doFinal = encryptBy3DesEcb(content, deskey); hex2S = byte2hexString(doFinal); } catch (GeneralSecurityException e) { e.printStackTrace(); } return hex2S; } /** * 分散算法 * @param data 數據(卡號) * @param key 根密鑰 * @return */ public static String getHashProtectKey(String data, String key) { String tempKey, protect_key; tempKey =byte2hexString(xOr(hexString2byte(data),hexString2byte("FFFFFFFFFFFFFFFF"))); protect_key = encryptBy3DesEcb(data, key); protect_key = protect_key + encryptBy3DesEcb(tempKey, key); return protect_key; } /** * 分散密鑰 * @param data 數據(卡號) * @param key 根密鑰 * @return * @throws GeneralSecurityException */ public static String getHashProtectKey(byte[] data, byte[] key) throws GeneralSecurityException { byte[] tempKeyBytes = xOr(data, hexString2byte("FFFFFFFFFFFFFFFF")); byte[] key1 = encryptBy3DesEcb(data, key); byte[] key2 = encryptBy3DesEcb(tempKeyBytes, key); byte[] disperKeyBytes = CommonUtils.byteMerger(key1, key2); String disperKey = CommonUtils.byte2hexString(disperKeyBytes); return disperKey; } }
MAC算法總結,代碼以下:數組
import java.security.GeneralSecurityException; /** * 計算MAC的工具類 */ public class MacUtils { private static final byte[] ZERO_IVC = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; /** * PBOC-3DES-MAC算法 * * 計算MAC(hex) PBOC_3DES_MAC(符合ISO9797Alg3Mac標準) (16的整數補8000000000000000) * 前n-1組使用單長密鑰DES 使用密鑰是密鑰的左8字節) 最後1組使用雙長密鑰3DES (使用所有16字節密鑰) * * 算法步驟:初始數據爲D,初始向量爲I,3DES祕鑰爲K0,祕鑰低8字節DES祕鑰K1; * 一、數據D分組而且填充:將字節數組D進行分組,每組8個字節,分組編號從0開始,分別爲D0...Dn;最後一個分組不滿8字節的,先填充一個字節80, * 後續所有填充00,滿8字節的,新增一個8字節分組(80000000 00000000); * 二、進行des循環加密:(1)D0和初始向量I進行按位異或獲得結果O0;(2)使用祕鑰K1,DES加密結果O0獲得結果I1, * 將I1和D1按位異或獲得結果D1;(3)循環第二步驟獲得結果Dn; 三、將Dn使用16字節祕鑰K0進行3DES加密,獲得的結果就是咱們要的MAC。 * @param data * @param key * @param icv * @return * @throws Exception */ public static byte[] calculatePboc3desMAC(byte[] data, byte[] key, byte[] icv) throws Exception { if (key == null || data == null) throw new RuntimeException("data or key is null."); if (key.length != 16) throw new RuntimeException("key length is not 16 byte."); byte[] leftKey = new byte[8]; System.arraycopy(key, 0, leftKey, 0, 8); // 拆分數據(8字節塊/Block) final int dataLength = data.length; final int blockCount = dataLength / 8 + 1; final int lastBlockLength = dataLength % 8; byte[][] dataBlock = new byte[blockCount][8]; for (int i = 0; i < blockCount; i++) { int copyLength = i == blockCount - 1 ? lastBlockLength : 8; System.arraycopy(data, i * 8, dataBlock[i], 0, copyLength); } dataBlock[blockCount - 1][lastBlockLength] = (byte) 0x80; byte[] desXor = DesUtils.xOr(dataBlock[0], icv); for (int i = 1; i < blockCount; i++) { byte[] des = DesUtils.encryptByDesCbc(desXor, leftKey); desXor = DesUtils.xOr(dataBlock[i], des); } desXor = DesUtils.encryptBy3DesCbc(desXor, key); return desXor; } /** * PBOC-DES-MAC算法 * @param data * @param key * @param icv * @return * @throws Exception */ public static byte[] calculatePbocdesMAC(byte[] data, byte[] key, byte[] icv) throws Exception { if (key == null || data == null) throw new RuntimeException("data or key is null."); if (key.length != 8) throw new RuntimeException("key length is not 16 byte."); // 拆分數據(8字節塊/Block) final int dataLength = data.length; final int blockCount = dataLength / 8 + 1; final int lastBlockLength = dataLength % 8; byte[][] dataBlock = new byte[blockCount][8]; for (int i = 0; i < blockCount; i++) { int copyLength = i == blockCount - 1 ? lastBlockLength : 8; System.arraycopy(data, i * 8, dataBlock[i], 0, copyLength); } dataBlock[blockCount - 1][lastBlockLength] = (byte) 0x80; byte[] desXor = DesUtils.xOr(dataBlock[0], icv); for (int i = 1; i < blockCount; i++) { byte[] des = DesUtils.encryptByDesCbc(desXor, key); desXor = DesUtils.xOr(dataBlock[i], des); } desXor = DesUtils.encryptByDesCbc(desXor, key); return desXor; } /** * ANSI X9.9MAC算法 <br/> * (1) ANSI X9.9MAC算法只使用單倍長密鑰。 <br/> * (2) MAC數據先按8字節分組,表示爲D0~Dn,若是Dn不足8字節時,尾部以字節00補齊。 <br/> * (3) 用MAC密鑰加密D0,加密結果與D1異或做爲下一次的輸入。 <br/> * (4) 將上一步的加密結果與下一分組異或,而後再用MAC密鑰加密。<br/> * (5) 直至全部分組結束,取最後結果的左半部做爲MAC。<br/> * 採用x9.9算法計算MAC (Count MAC by ANSI-x9.9). * * @param key 8字節密鑰數據 * @param data 待計算的緩衝區 * @throws GeneralSecurityException */ public static byte[] calculateANSIX9_9MAC(byte[] key, byte[] data) throws GeneralSecurityException { final int dataLength = data.length; final int lastLength = dataLength % 8; final int lastBlockLength = lastLength == 0 ? 8 : lastLength; final int blockCount = dataLength / 8 + (lastLength > 0 ? 1 : 0); // 拆分數據(8字節塊/Block) byte[][] dataBlock = new byte[blockCount][8]; for (int i = 0; i < blockCount; i++) { int copyLength = i == blockCount - 1 ? lastBlockLength : 8; System.arraycopy(data, i * 8, dataBlock[i], 0, copyLength); } byte[] desXor = new byte[8]; for (int i = 0; i < blockCount; i++) { byte[] tXor = DesUtils.xOr(desXor, dataBlock[i]); desXor = DesUtils.encryptByDesEcb(tXor, key); // DES加密 } return desXor; } /** * 採用ANSI x9.19算法計算MAC (Count MAC by ANSI-x9.19).<br/> * 將ANSI X9.9的結果作以下計算<br/> * (6) 用MAC密鑰右半部解密(5)的結果。 <br/> * (7) 用MAC密鑰左半部加密(6)的結果。<br/> * (8) 取(7)的結果的左半部做爲MAC。<br/> * @param key 16字節密鑰數據 * @param data 待計算的緩衝區 * @throws GeneralSecurityException */ public static byte[] calculateANSIX9_19MAC(byte[] key, byte[] data) throws GeneralSecurityException { if (key == null || data == null) return null; if (key.length != 16) { throw new RuntimeException("祕鑰長度錯誤."); } byte[] keyLeft = new byte[8]; byte[] keyRight = new byte[8]; System.arraycopy(key, 0, keyLeft, 0, 8); System.arraycopy(key, 8, keyRight, 0, 8); byte[] result99 = calculateANSIX9_9MAC(keyLeft, data); byte[] resultTemp = DesUtils.decryptByDesEcb(result99, keyRight); return DesUtils.encryptByDesEcb(resultTemp, keyLeft); } }
MAC1Utils工具類:安全
/** * 計算MAC1的工具類,是CPU卡在充值過程當中要計算MAC1,而後通卡公司返回MAC2 */ public class Mac1Utils { /** * * @param walletSequence 錢包交易流水(16進制) * @param beforeAmt 充值前金額(10進制) * @param txnAmt 充值金額(10進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param random 僞隨機數 * @param rechargeKey 充值密鑰密文 * @param protectKey 充值密鑰保護密鑰 * @return * @throws Exception */ public static String getMac1(String walletSequence, long beforeAmt, long txnAmt, String tradeType, String psamTerminalCode, String random, String rechargeKey, String protectKey) throws Exception { //獲取充值密鑰明文 byte[] rechargeKeyBytes = DesUtils.hexString2byte(rechargeKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] rechargeKeyClearTextBytes = DesUtils.decryptBy3DesEcb(rechargeKeyBytes, protectKeyBytes); // System.out.println("rechargeKeyClearText:" + DesUtils.byte2hexString(rechargeKeyClearTextBytes)); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); // System.out.println("walletSequence:" + walletSequence); //過程密鑰由 DATA中第一字節即密鑰標識符指定的圈存密鑰對(4 字節隨機數+2 字節電子存摺或電子錢包聯機交易序號+8000)數據加密生成 String calcProcessKeyData = CustomStringUtils.append(random, walletSequence, "8000"); System.out.println("calcProcessKeyData:" + calcProcessKeyData); //獲取過程密鑰 byte[] calcProcessKeyDataBytes = DesUtils.hexString2byte(calcProcessKeyData); byte[] processKeyBytes = DesUtils.encryptBy3DesEcb(calcProcessKeyDataBytes, rechargeKeyClearTextBytes); // System.out.println("processKey:" + DesUtils.byte2hexString(processKeyBytes)); String balance = CustomStringUtils.leftFill(Long.toHexString(beforeAmt), '0', 8); String chargeFee = CustomStringUtils.leftFill(Long.toHexString(txnAmt), '0', 8); String macData = CustomStringUtils.append(balance, chargeFee, tradeType, psamTerminalCode); byte[] macDataBytes = DesUtils.hexString2byte(macData); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] calcPbocDesMacBytes = MacUtils.calculatePbocdesMAC(macDataBytes, processKeyBytes, icv); String calcPbocDesMac = DesUtils.byte2hexString(calcPbocDesMacBytes); String mac1 = calcPbocDesMac.substring(0, 8); return mac1; } /** * * @param walletSequence 錢包交易流水(16進制) * @param beforeBalanceHex 充值前金額(16進制) * @param rechargeFeeHex 充值金額(16進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param random 僞隨機數 * @param rechargeKey 充值密鑰密文 * @param protectKey 充值密鑰保護密鑰 * @return * @throws Exception */ public static String getMac1(String walletSequence, String beforeBalanceHex, String rechargeFeeHex, String tradeType, String psamTerminalCode, String random, String rechargeKey, String protectKey) throws Exception { //獲取充值密鑰明文 byte[] rechargeKeyBytes = DesUtils.hexString2byte(rechargeKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] rechargeKeyClearTextBytes = DesUtils.decryptBy3DesEcb(rechargeKeyBytes, protectKeyBytes); System.out.println("rechargeKeyClearText:" + DesUtils.byte2hexString(rechargeKeyClearTextBytes)); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); // System.out.println("walletSequence:" + walletSequence); //過程密鑰由 DATA中第一字節即密鑰標識符指定的圈存密鑰對(4 字節隨機數+2 字節電子存摺或電子錢包聯機交易序號+8000)數據加密生成 String calcProcessKeyData = CustomStringUtils.append(random, walletSequence, "8000"); //獲取過程密鑰 byte[] calcProcessKeyDataBytes = DesUtils.hexString2byte(calcProcessKeyData); byte[] processKeyBytes = DesUtils.encryptBy3DesEcb(calcProcessKeyDataBytes, rechargeKeyClearTextBytes); System.out.println("processKey:" + DesUtils.byte2hexString(processKeyBytes)); String balance = CustomStringUtils.leftFill(beforeBalanceHex, '0', 8); String chargeFee = CustomStringUtils.leftFill(rechargeFeeHex, '0', 8); String macData = CustomStringUtils.append(balance, chargeFee, tradeType, psamTerminalCode); byte[] macDataBytes = DesUtils.hexString2byte(macData); System.out.println("macData:" + macData); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] calcPbocDesMacBytes = MacUtils.calculatePbocdesMAC(macDataBytes, processKeyBytes, icv); String calcPbocDesMac = DesUtils.byte2hexString(calcPbocDesMacBytes); String mac1 = calcPbocDesMac.substring(0, 8); return mac1; } /** * * @param walletSequence 電子錢包聯機交易序號(16進制) * @param rechargeFee 充值金額(10進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param tradeDateTime 主機交易日期時間 * @param random 隨機數 * @param rechargeKey 充值密鑰密文 * @param protectKey 保護密鑰 * @return * @throws Exception */ public static String getMac2(String walletSequence, long rechargeFee, String tradeType, String psamTerminalCode, String tradeDateTime, String random, String rechargeKey, String protectKey) throws Exception { //獲取充值密鑰明文 byte[] rechargeKeyBytes = DesUtils.hexString2byte(rechargeKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] rechargeKeyClearTextBytes = DesUtils.decryptBy3DesEcb(rechargeKeyBytes, protectKeyBytes); System.out.println("rechargeKeyClearText:" + DesUtils.byte2hexString(rechargeKeyClearTextBytes)); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); //過程密鑰由 DATA中第一字節即密鑰標識符指定的圈存密鑰對(4 字節隨機數+2 字節電子存摺或電子錢包聯機交易序號+8000)數據加密生成 String calcProcessKeyData = CustomStringUtils.append(random, walletSequence, "8000"); //獲取過程密鑰 byte[] calcProcessKeyDataBytes = DesUtils.hexString2byte(calcProcessKeyData); byte[] processKeyBytes = DesUtils.encryptBy3DesEcb(calcProcessKeyDataBytes, rechargeKeyClearTextBytes); //充值金額 String chargeFee = CustomStringUtils.leftFill(Long.toHexString(rechargeFee), '0', 8); String mac2Data = CustomStringUtils.append(chargeFee, tradeType,psamTerminalCode, tradeDateTime); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] mac2DataBytes = DesUtils.hexString2byte(mac2Data); byte[] calcPbocdesMACBytes = MacUtils.calculatePbocdesMAC(mac2DataBytes, processKeyBytes, icv); String calcPbocdesMac = DesUtils.byte2hexString(calcPbocdesMACBytes); String mac2 = calcPbocdesMac.substring(0, 8); return mac2; } /** * * @param walletSequence 電子錢包聯機交易序號(16進制) * @param rechargeFee 充值金額(16進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param tradeDateTime 主機交易日期時間 * @param random 隨機數 * @param rechargeKey 充值密鑰密文 * @param protectKey 保護密鑰 * @return * @throws Exception */ public static String getMac2(String walletSequence, String rechargeFee, String tradeType, String psamTerminalCode, String tradeDateTime, String random, String rechargeKey, String protectKey) throws Exception { //獲取充值密鑰明文 byte[] rechargeKeyBytes = DesUtils.hexString2byte(rechargeKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] rechargeKeyClearTextBytes = DesUtils.decryptBy3DesEcb(rechargeKeyBytes, protectKeyBytes); System.out.println("rechargeKeyClearText:" + DesUtils.byte2hexString(rechargeKeyClearTextBytes)); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); //過程密鑰由 DATA中第一字節即密鑰標識符指定的圈存密鑰對(4 字節隨機數+2 字節電子存摺或電子錢包聯機交易序號+8000)數據加密生成 String calcProcessKeyData = CustomStringUtils.append(random, walletSequence, "8000"); //獲取過程密鑰 byte[] calcProcessKeyDataBytes = DesUtils.hexString2byte(calcProcessKeyData); byte[] processKeyBytes = DesUtils.encryptBy3DesEcb(calcProcessKeyDataBytes, rechargeKeyClearTextBytes); //充值金額 String chargeFee = CustomStringUtils.leftFill(rechargeFee, '0', 8); String mac2Data = CustomStringUtils.append(chargeFee, tradeType,psamTerminalCode, tradeDateTime); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] mac2DataBytes = DesUtils.hexString2byte(mac2Data); byte[] calcPbocdesMACBytes = MacUtils.calculatePbocdesMAC(mac2DataBytes, processKeyBytes, icv); String calcPbocdesMac = DesUtils.byte2hexString(calcPbocdesMACBytes); String mac2 = calcPbocdesMac.substring(0, 8); return mac2; } /** * 計算MAC2 * @param rechargeFee 充值金額 * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param tradeDateTime 交易日期時間 * @param processKey 過程密鑰 * @return * @throws Exception */ public static String getMac2(String rechargeFee, String tradeType, String psamTerminalCode, String tradeDateTime, String processKey) throws Exception { byte[] processKeyBytes = DesUtils.hexString2byte(processKey); //4字節交易金額+1字節交易類型+6字節終端機編號+4字節主機交易日期+3字節主機交易時間 String chargeFee = CustomStringUtils.leftFill(rechargeFee, '0', 8); String mac2Data = CustomStringUtils.append(chargeFee, tradeType,psamTerminalCode, tradeDateTime); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] mac2DataBytes = DesUtils.hexString2byte(mac2Data); byte[] calcPbocdesMACBytes = MacUtils.calculatePbocdesMAC(mac2DataBytes, processKeyBytes, icv); String calcPbocdesMac = DesUtils.byte2hexString(calcPbocdesMACBytes); String mac2 = calcPbocdesMac.substring(0, 8); return mac2; } }
TACUtils工具類:app
public class TacUtils { /** * 計算TAC * @param afterAmt 新餘額(充值後金額)(10進制) * @param walletSequence 錢包交易序號 * @param txnAmt 充值金額(10進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param tradeDateTime 交易日期時間 * @param tacKey tac密鑰 * @param protectKey 保護密鑰 * @return * @throws Exception */ public static String getTac(long afterAmt, String walletSequence, long txnAmt, String tradeType, String psamTerminalCode, String tradeDateTime, String tacKey, String protectKey) throws Exception { //獲取TAC密鑰明文 byte[] tacKeyBytes = DesUtils.hexString2byte(tacKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] tacKeyClearTextBytes = DesUtils.decryptBy3DesEcb(tacKeyBytes, protectKeyBytes); int half = tacKeyClearTextBytes.length / 2; byte[] b1 = new byte[half]; byte[] b2 = new byte[half]; System.arraycopy(tacKeyClearTextBytes, 0, b1, 0, half); System.arraycopy(tacKeyClearTextBytes, half, b2, 0, half); //tac過程密鑰 byte[] tacProcessKeyBytes = DesUtils.xOr(b1, b2); //4字節電子存摺或電子錢包新餘額 String newBalance = CustomStringUtils.leftFill(Long.toHexString(afterAmt), '0', 8); String chargeFee = CustomStringUtils.leftFill(Long.toHexString(txnAmt), '0', 8); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); //4字節電子存摺或電子錢包新餘額 + 2字節電子存摺或電子錢包聯機交易序號(加1前) + 4字節交易金額 + 1字節交易類型標識 + 6字節終端機編號 + 4字節主機交易日期 + 3字節主機交易時間 String tacData = CustomStringUtils.append(newBalance, walletSequence, chargeFee, tradeType, psamTerminalCode, tradeDateTime); System.out.println("tacData:" + tacData); System.out.println("tacProceessKey:" + DesUtils.byte2hexString(tacProcessKeyBytes)); byte[] tacDataBytes = DesUtils.hexString2byte(tacData); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] calculatePbocdesMACBytes = MacUtils.calculatePbocdesMAC(tacDataBytes, tacProcessKeyBytes, icv); String calculatePbocdesMAC = DesUtils.byte2hexString(calculatePbocdesMACBytes); String tac = calculatePbocdesMAC.substring(0, 8); return tac; } /** * 計算TAC * @param rechargeFeeHex 新餘額(充值後金額)(16進制) * @param walletSequence 錢包交易序號 * @param rechargeFeeHex 充值金額(16進制) * @param tradeType 交易類型 * @param psamTerminalCode 終端機編號 * @param tradeDateTime 交易日期時間 * @param tacKey tac密鑰 * @param protectKey 保護密鑰 * @return * @throws Exception */ public static String getTac(String afterBalanceHex, String walletSequence, String rechargeFeeHex, String tradeType, String psamTerminalCode, String tradeDateTime, String tacKey, String protectKey) throws Exception { //獲取TAC密鑰明文 byte[] tacKeyBytes = DesUtils.hexString2byte(tacKey); byte[] protectKeyBytes = DesUtils.hexString2byte(protectKey); byte[] tacKeyClearTextBytes = DesUtils.decryptBy3DesEcb(tacKeyBytes, protectKeyBytes); System.out.println("tacKeyClearText:" + DesUtils.byte2hexString(tacKeyClearTextBytes)); int half = tacKeyClearTextBytes.length / 2; byte[] b1 = new byte[half]; byte[] b2 = new byte[half]; System.arraycopy(tacKeyClearTextBytes, 0, b1, 0, half); System.arraycopy(tacKeyClearTextBytes, half, b2, 0, half); //tac過程密鑰 byte[] tacProcessKeyBytes = DesUtils.xOr(b1, b2); //4字節電子存摺或電子錢包新餘額 String newBalance = CustomStringUtils.leftFill(afterBalanceHex, '0', 8); String chargeFee = CustomStringUtils.leftFill(rechargeFeeHex, '0', 8); // if (walletSequence.length() == 6) { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence.substring(2))); // } else { // walletSequence = Integer.toHexString(Integer.parseInt(walletSequence)); // } // walletSequence = CustomStringUtils.leftFill(walletSequence, '0', 4); //4字節電子存摺或電子錢包新餘額 + 2字節電子存摺或電子錢包聯機交易序號(加1前) + 4字節交易金額 + 1字節交易類型標識 + 6字節終端機編號 + 4字節主機交易日期 + 3字節主機交易時間 String tacData = CustomStringUtils.append(newBalance, walletSequence, chargeFee, tradeType, psamTerminalCode, tradeDateTime); System.out.println("tacData:" + tacData); System.out.println("tacProceessKey:" + DesUtils.byte2hexString(tacProcessKeyBytes)); byte[] tacDataBytes = DesUtils.hexString2byte(tacData); byte[] icv = DesUtils.hexString2byte("0000000000000000"); byte[] calculatePbocdesMACBytes = MacUtils.calculatePbocdesMAC(tacDataBytes, tacProcessKeyBytes, icv); String calculatePbocdesMAC = DesUtils.byte2hexString(calculatePbocdesMACBytes); String tac = calculatePbocdesMAC.substring(0, 8); return tac; } }