|
|
|
@ -8,11 +8,15 @@ import sun.misc.BASE64Decoder; |
|
|
|
|
import sun.misc.BASE64Encoder; |
|
|
|
|
|
|
|
|
|
import javax.crypto.Cipher; |
|
|
|
|
import javax.crypto.spec.GCMParameterSpec; |
|
|
|
|
import javax.crypto.spec.IvParameterSpec; |
|
|
|
|
import javax.crypto.spec.SecretKeySpec; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.io.UnsupportedEncodingException; |
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
|
import java.security.MessageDigest; |
|
|
|
|
import java.security.SecureRandom; |
|
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 功能描述 |
|
|
|
@ -20,6 +24,12 @@ import java.security.MessageDigest; |
|
|
|
|
*/ |
|
|
|
|
public class AisinoInvoiceDecryptUtil { |
|
|
|
|
|
|
|
|
|
private static final String ALGORITHM = "AES/GCM/NoPadding"; |
|
|
|
|
private static final int KEY_SIZE = 128; // 密钥长度为128位
|
|
|
|
|
private static final int IV_LENGTH = 12; // 初始化向量长度为12字节(96位)
|
|
|
|
|
private static final int TAG_LENGTH = 16; // GCM模式的认证标签长度为16字节
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 解密api里order请求,同时兼容老版本order里面包了identity的版本 |
|
|
|
|
* |
|
|
|
@ -99,6 +109,93 @@ public class AisinoInvoiceDecryptUtil { |
|
|
|
|
return new BASE64Encoder().encode(temp); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @author kk |
|
|
|
|
* @date 2023/11/21 14:45 |
|
|
|
|
* @param: [xmlStr, mKey] |
|
|
|
|
* @return: 新增AES加密方式,兼容旧版 |
|
|
|
|
*/ |
|
|
|
|
public static String decrypt(String xmlStr, String mKey) throws Exception { |
|
|
|
|
|
|
|
|
|
// base64解码
|
|
|
|
|
BASE64Decoder decoder = new BASE64Decoder(); |
|
|
|
|
byte[] encBuf = null; |
|
|
|
|
try { |
|
|
|
|
if (xmlStr != null) { |
|
|
|
|
xmlStr = xmlStr.replaceAll(" ", "+");//解决URL里加号变空格
|
|
|
|
|
} |
|
|
|
|
encBuf = decoder.decodeBuffer(xmlStr); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 取密钥和偏转向量
|
|
|
|
|
byte[] key = new byte[8]; |
|
|
|
|
byte[] iv = new byte[8]; |
|
|
|
|
getKeyIV(mKey, key, iv); |
|
|
|
|
|
|
|
|
|
SecretKeySpec deskey = new SecretKeySpec(key, "DES"); |
|
|
|
|
IvParameterSpec ivParam = new IvParameterSpec(iv); |
|
|
|
|
|
|
|
|
|
// 使用DES算法解密
|
|
|
|
|
byte[] temp = null; |
|
|
|
|
try { |
|
|
|
|
temp = DES_CBC_Decrypt(encBuf, deskey, ivParam); |
|
|
|
|
|
|
|
|
|
// 进行解密后的md5Hash校验
|
|
|
|
|
byte[] md5Hash = null; |
|
|
|
|
try { |
|
|
|
|
md5Hash = MD5Hash(temp, 16, temp.length - 16); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
e.printStackTrace(); |
|
|
|
|
} |
|
|
|
|
// 进行解密校检
|
|
|
|
|
for (int i = 0; i < md5Hash.length; i++) { |
|
|
|
|
if (md5Hash[i] != temp[i]) { |
|
|
|
|
// System.out.println(md5Hash[i] + "MD5校验错误。" + temp[i]);
|
|
|
|
|
throw new Exception("MD5校验错误。"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// 返回解密后的数组,其中前16位MD5Hash码要除去。
|
|
|
|
|
return new String(temp, 16, temp.length - 16, "utf-8"); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
|
|
temp = AES_CBC_Decrypt(hexToBytes(xmlStr), mKey.getBytes(), ivParam); |
|
|
|
|
return new String(temp); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 新版AES解密方法
|
|
|
|
|
private static byte[] AES_CBC_Decrypt(byte[] data, byte[] key, IvParameterSpec ivSpec) throws Exception { |
|
|
|
|
|
|
|
|
|
byte[] iv = key; |
|
|
|
|
System.arraycopy(data, 0, iv, 0, IV_LENGTH); |
|
|
|
|
|
|
|
|
|
byte[] encryptedData = new byte[data.length - IV_LENGTH]; |
|
|
|
|
System.arraycopy(data, IV_LENGTH, encryptedData, 0, encryptedData.length); |
|
|
|
|
|
|
|
|
|
SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); |
|
|
|
|
Cipher cipher = Cipher.getInstance(ALGORITHM); |
|
|
|
|
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv); |
|
|
|
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); |
|
|
|
|
|
|
|
|
|
return cipher.doFinal(encryptedData); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** 还原方法*/ |
|
|
|
|
public static byte[] hexToBytes(String hexString) { |
|
|
|
|
int len = hexString.length(); |
|
|
|
|
byte[] data = new byte[len / 2]; |
|
|
|
|
for (int i = 0; i < len; i += 2) { |
|
|
|
|
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) |
|
|
|
|
+ Character.digit(hexString.charAt(i+1), 16)); |
|
|
|
|
} |
|
|
|
|
return data; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <li> |
|
|
|
|
* 方法名称:encrypt</li> <li> |
|
|
|
@ -114,7 +211,7 @@ public class AisinoInvoiceDecryptUtil { |
|
|
|
|
* @return 解密后的字符串 |
|
|
|
|
* @throws Exception |
|
|
|
|
*/ |
|
|
|
|
public static String decrypt(String xmlStr, String mKey) throws Exception { |
|
|
|
|
/*public static String decrypt(String xmlStr, String mKey) throws Exception { |
|
|
|
|
// base64解码
|
|
|
|
|
BASE64Decoder decoder = new BASE64Decoder(); |
|
|
|
|
byte[] encBuf = null; |
|
|
|
@ -161,7 +258,7 @@ public class AisinoInvoiceDecryptUtil { |
|
|
|
|
|
|
|
|
|
// 返回解密后的数组,其中前16位MD5Hash码要除去。
|
|
|
|
|
return new String(temp, 16, temp.length - 16, "utf-8"); |
|
|
|
|
} |
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <li> |
|
|
|
|