Files
emo-grab/src/main/java/quant/rich/emoney/util/EncryptUtils.java
Administrator 6a5e13974c First Commit
2025-05-12 12:04:42 +08:00

310 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package quant.rich.emoney.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.util.encoders.Hex;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
@Slf4j
public class EncryptUtils {
private static final byte[] IV = "aesiv_emapp@2018".getBytes(StandardCharsets.UTF_8);
private static final String PASSWORD_KEY_STRING_BASE64 = "5pxlF/NrBtxlBve0jQwGEw==";
private static final String PASSWORD_CIPHER_INSTANCE = "AES/CBC/PKCS5Padding";
private static final String AES_DECODE_KEY_STRING_BASE64 = "39KLC1etSfUXuiGxRm5ilDDtqapKXm0COuHxUrWSWlc=";
private static final String EM_SIGN_MESS_1 = "23082404151001";
private static final String EM_SIGN_MESS_2 = "994fec3c512f2f7756fd5e4403147f01";
private static final String SLASH = "/";
private static final String COLON = ":";
/**
* 加密用于 Emoney 登录的密码
* @param pwd 明文密码
* @return 加密过的密码Base64 字符串)
*/
public static String encryptAesForEmoneyPassword(String pwd) {
try {
byte[] keyBytes = Base64.getDecoder().decode(PASSWORD_KEY_STRING_BASE64);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV);
Cipher cipher = Cipher.getInstance(PASSWORD_CIPHER_INSTANCE);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// 执行加密
byte[] encryptedBytes = cipher.doFinal(pwd.getBytes(StandardCharsets.UTF_8));
// 将加密结果转换为Base64字符串
String encryptedString = Base64.getEncoder().encodeToString(encryptedBytes);
return encryptedString;
} catch (Exception e) {
log.error("Encrypt aes for emoney-password failed", e);
return null;
}
}
/**
* 解密由 Emoney 进行加密过的密码
* @param pwd 加密过的密码Base64 字符串)
* @return 明文密码
*/
public static String decryptAesForEmoneyPassword(String pwd) {
try {
byte[] keyBytes = Base64.getDecoder().decode(PASSWORD_KEY_STRING_BASE64);
byte[] passBytes = Base64.getDecoder().decode(pwd);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV);
Cipher cipher = Cipher.getInstance(PASSWORD_CIPHER_INSTANCE);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 执行解密
byte[] decryptedBytes = cipher.doFinal(passBytes);
return new String(decryptedBytes, "UTF-8");
} catch (Exception e) {
//log.error("Decrypt aes for emoney-password failed", e);
return null;
}
}
/**
* 使用益盟自带 AES key 对指定字节数组进行解密,一般而言该字节数组通过解码 Base64 字符串得到
* @param bArr
* @return
*/
private static String decryptAesForEmSignMess(byte[] bArr) {
try {
byte[] keyBytes = Base64.getDecoder().decode(AES_DECODE_KEY_STRING_BASE64);
EmoneyBytesResortUtils.d(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return new String(cipher.doFinal(bArr), "UTF-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* 字符串转 32 位小写 md5 字符串
*
* @param str
* @return
*/
public static String toMD5String(String str) {
if (StringUtils.isEmpty(str)) {
return null;
}
try {
return toMD5String(str.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e10) {
e10.printStackTrace();
return null;
}
}
/**
* 字节数组转 32 位小写 md5 字符串
* @param bytes
* @return
*/
public static String toMD5String(byte[] bytes) {
if (bytes == null) {
return null;
}
try {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(bytes);
return bytesToHex(instance.digest());
} catch (NoSuchAlgorithmException e10) {
e10.printStackTrace();
return null;
}
}
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// 将字节转换为无符号整数表示的16进制并且保证每个字节输出两个字符
sb.append(String.format("%02x", b & 0xFF));
}
return sb.toString();
}
/**
* 根据 content、method 和 xProtocolId 生成 EM-Sign随机字符串和时间戳自动生成
* @param content 请求的字节数组
* @param method 请求方法
* @param xProtocolId 请求 X-Protocol-Id
* @return
* @throws IOException
*/
public static String getEMSign(
byte[] content,
String method,
String xProtocolId) throws IOException {
String randomString = TextUtils.randomString(10);
long timestampFixed = Instant.now().toEpochMilli();
return getEMSign(content, method, xProtocolId, randomString, timestampFixed);
}
/**
* 根据 content、method、xProtocolId、指定的随机 10 位字符串、指定的时间戳生成 EM-Sign
* @param content 请求的字节数组
* @param method 请求方法
* @param xProtocolId 请求 X-Protocol-Id
* @return
* @throws IOException
*/
public static String getEMSign(
byte[] content,
String method,
String xProtocolId,
String randomString, long timestampFixed) throws IOException {
// TODO: 空校验、时间戳校验、随机字符串长度校验等防呆操作
StringBuffer plainSignSb = new StringBuffer();
plainSignSb.append(EM_SIGN_MESS_1);
plainSignSb.append(method.toUpperCase());
plainSignSb.append(EM_SIGN_MESS_2);
plainSignSb.append(SLASH);
plainSignSb.append(URLDecoder.decode(xProtocolId, "UTF-8"));
plainSignSb.append(timestampFixed);
plainSignSb.append(randomString);
if (content != null && content.length != 0) {
plainSignSb.append(toMD5String(content));
}
String md5Sign = EncryptUtils.toMD5String(plainSignSb.toString());
StringBuilder emSignSb = new StringBuilder();
emSignSb.append(EM_SIGN_MESS_1);
emSignSb.append(COLON);
emSignSb.append(md5Sign);
emSignSb.append(COLON);
emSignSb.append(randomString);
emSignSb.append(COLON);
emSignSb.append(timestampFixed);
String emSign = emSignSb.toString();
return emSign;
}
public static byte[] sha3(byte[] bytes, int digit) {
SHA3Digest digest = new SHA3Digest(digit);
digest.update(bytes, 0, bytes.length);
byte[] rsData = new byte[digest.getDigestSize()];
digest.doFinal(rsData, 0);
return rsData;
}
public static String sha256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
log.error("SHA-256 Hash failed", e);
return null;
}
}
public static String sha3(String data, int digit) {
return Hex.toHexString(sha3(data.getBytes(), digit));
}
public static String toHexString(byte[] data) {
return Hex.toHexString(data);
}
public static void main(String[] args) throws IOException {
// EM-Sign:
// 23082404151001:356b7041c37848b4f73a8ad5c9416f48:nBZTuInqo1:1721439297485
long timestampFixed = 1721439297485L;
String randomString = "nBZTuInqo1";
byte[] mess = Base64.getDecoder().decode("0JryhAshy7JXgP+5sLmWwaurGM3YHWaFbGaKH/mi7oKN+5mlTnEp5LTeFJVtJTTR");
String f13443a = EncryptUtils.decryptAesForEmSignMess(mess);
String path = "C:\\Users\\Administrator\\Desktop\\Emoney\\login.request.body";
FileInputStream in = new FileInputStream(new File(path));
byte[] requestBody = new byte[in.available()];
in.read(requestBody);
in.close();
String requestBodyStr = "{\"date\":0}";// new String(requestBody, "UTF-8") + '\0';
requestBody = requestBodyStr.getBytes("UTF-8");
RequestBody body = RequestBody.create(requestBodyStr, MediaType.parse("application/json"));
okio.Buffer buffer = new okio.Buffer();
body.writeTo(buffer);
requestBody = buffer.readByteArray();
String requestBodyMd5;
if (requestBody == null || requestBody.length == 0) {
requestBodyMd5 = "";
} else {
requestBodyMd5 = toMD5String(requestBody);
}
String str2 = "23082404151001" + "POST" + f13443a + "/"
+ URLDecoder.decode("SmartInvestment%2FIndex%2FTianYan", "UTF-8") + timestampFixed + randomString
+ requestBodyMd5;
String md5 = EncryptUtils.toMD5String(str2);
log.info("Handmade emsign md5: {}", md5);
String emSign = getEMSign(requestBody, "POST", "SmartInvestment%2FIndex%2FTianYan", randomString, timestampFixed);
log.info("Method emsign: {}", emSign);
// 测试密码加解密
String encryptPass = encryptAesForEmoneyPassword("123456");
log.info("Encrypt pass for 123456: {}", encryptPass);
String decryptPass = decryptAesForEmoneyPassword(encryptPass);
log.info("Decrypt pass for {}: {}", encryptPass, decryptPass);
}
}