Browse Source

招商银行

master
zhaoyiran 7 months ago
parent
commit
53fecee243
  1. 6
      pom.xml
  2. 32
      src/main/java/com/win/bank/enums/PaymentStatusEnum.java
  3. 21
      src/main/java/com/win/bank/service/BaseBankService.java
  4. 27
      src/main/java/com/win/bank/service/MainService.java
  5. 28
      src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java
  6. 73
      src/main/java/com/win/bank/service/cmb/ApiDemo.java
  7. 211
      src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java
  8. 369
      src/main/java/com/win/bank/service/cmb/DcHelper.java
  9. 39
      src/main/java/com/win/bank/service/cmb/DeviceInfo.java
  10. 54
      src/main/java/com/win/bank/utils/BaseBankServiceUtil.java
  11. 20
      src/main/java/com/win/bank/utils/JsonUtil.java
  12. 8
      src/main/resources/application-dev.yml

6
pom.xml

@ -93,6 +93,12 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId> <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
</dependencies> </dependencies>

32
src/main/java/com/win/bank/enums/PaymentStatusEnum.java

@ -0,0 +1,32 @@
package com.win.bank.enums;
public enum PaymentStatusEnum {
UNPROCESSED("0", "未处理"), PAYING("1", "支付中"), SUCCESS("2", "支付成功"), FAILED("3", "支付失败");
private final String code;
private final String description;
PaymentStatusEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public static PaymentStatusEnum getByCode(String code) {
for (PaymentStatusEnum status : PaymentStatusEnum.values()) {
if (status.getCode().equals(code)) {
return status;
}
}
throw new IllegalArgumentException("Invalid PaymentStatus code: " + code);
}
}

21
src/main/java/com/win/bank/service/BaseBankService.java

@ -0,0 +1,21 @@
package com.win.bank.service;
import com.win.bank.domain.BankDO;
public interface BaseBankService {
/**
* 支付
*
* @param bankDO
* @return
*/
BankDO payment(BankDO bankDO);
/**
* 查询支付结果
*
* @param bankDO
* @return
*/
BankDO queryPaymentResult(BankDO bankDO);
}

27
src/main/java/com/win/bank/service/MainService.java

@ -4,11 +4,13 @@ import java.util.List;
import javax.annotation.Resource; import javax.annotation.Resource;
import com.win.bank.service.bank.BankService;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.win.bank.domain.BankDO; import com.win.bank.domain.BankDO;
import com.win.bank.enums.PaymentStatusEnum;
import com.win.bank.service.bank.BankService;
import com.win.bank.utils.BaseBankServiceUtil;
@Component @Component
public class MainService { public class MainService {
@ -16,14 +18,25 @@ public class MainService {
@Resource @Resource
private BankService bankService; private BankService bankService;
@Scheduled(cron = "* * * * * ?") @Scheduled(cron = "0 0/1 * * * ? ")
public void task() { public void payTask() {
System.out.println(System.currentTimeMillis()); List<BankDO> bankDOList = bankService.listByStatus(PaymentStatusEnum.UNPROCESSED.getCode());
for (BankDO bankDO : bankDOList) {
List<BankDO> bankDOList = bankService.listByStatus("0"); BaseBankService baseBankService = BaseBankServiceUtil.get(bankDO.getPayBankCode().toUpperCase());
bankDO = baseBankService.payment(bankDO);
bankService.updateById(bankDO);
}
}
@Scheduled(cron = "30 0/1 * * * ? ")
public void queryPaymentResult() {
List<BankDO> bankDOList = bankService.listByStatus(PaymentStatusEnum.PAYING.getCode());
for (BankDO bankDO : bankDOList) { for (BankDO bankDO : bankDOList) {
System.out.println(bankDO); BaseBankService baseBankService = BaseBankServiceUtil.get(bankDO.getPayBankCode().toUpperCase());
bankDO = baseBankService.queryPaymentResult(bankDO);
if (bankDO != null) {
bankService.updateById(bankDO);
}
} }
} }
} }

28
src/main/java/com/win/bank/service/bocom/BocomServiceImpl.java

@ -0,0 +1,28 @@
package com.win.bank.service.bocom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.win.bank.domain.BankDO;
import com.win.bank.service.BaseBankService;
/**
* 交通银行
*/
@Service
public class BocomServiceImpl implements BaseBankService {
private static final Logger logger = LoggerFactory.getLogger(BocomServiceImpl.class);
@Override
public BankDO payment(BankDO bankDO) {
logger.debug("交通银行 payment");
return bankDO;
}
@Override
public BankDO queryPaymentResult(BankDO bankDO) {
logger.debug("交通银行 queryPaymentResult");
return bankDO;
}
}

73
src/main/java/com/win/bank/service/cmb/ApiDemo.java

@ -1,73 +0,0 @@
package com.win.bank.service.cmb;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* 招商银行银企直联国密免前置/SaaS对接示例本示例仅供参考不保证各种异常场景运行请勿直接使用如有错漏请联系对接人员运行时请使用所获取的测试资源替换 用户编号公私钥对称密钥服务商编号等信息
*
* @author cmb.firmbank
* @date 2023/7/20
*/
public class ApiDemo {
private static final int BOUND_START = 1000000;
private static final int BOUND_END = 9000000;
// 测试地址,生产需要替换
private static String url = "http://192.168.0.142:8080/cdcserver/api/v2";
// private static String url = "http://cdctest.cmburl.cn:80/cdcserver/api/v2";
// 生产地址
// private static String url = "https://cdc.cmbchina.com/cdcserver/api/v2";
// 银行公钥,生产需要替换
private static String publicKey = "BNsIe9U0x8IeSe4h/dxUzVEz9pie0hDSfMRINRXc7s1UIXfkExnYECF4QqJ2SnHxLv3z/99gsfDQrQ6dzN5lZj0=";
// 生产环境银行公钥
// private static String publicKey =
// "BEynMEZOjNpwZIiD9jXtZSGr3Ecpwn7r+m+wtafXHb6VIZTnugfuxhcKASq3hX+KX9JlHODDl9/RDKQv4XLOFak=";
// 客户私钥,生产需要替换
private static String privateKey = "NBtl7WnuUtA2v5FaebEkU0/Jj1IodLGT6lQqwkzmd2E=";
// 对称密钥,生产需要替换
private static String symKey = "VuAzSWQhsoNqzn0K";
// 企业网银用户号,生产需要替换
private static String uid = "N002986522";
private static Random random = new Random();
private ApiDemo() {}
public static void main(String[] args) throws GeneralSecurityException, IOException, CryptoException {
// 装载BC库,必须在应用的启动类中调用此函数
Security.addProvider(new BouncyCastleProvider());
System.setProperty("sun.net.http.retryPost", "false");
// 业务接口名,这里是查询业务模式接口,生产请替换为对应接口名
String funcode = "BB1PAYQR";
// 准备接口数据,生产请替换为具体接口请求报文,包含所需的请求字段
String currentDatetime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String reqid = new SimpleDateFormat("yyyyMMddHHmmssSSSS").format(new Date()) + (BOUND_START + random.nextInt(BOUND_END));
String data =
"{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}],\"bb1payopx1\":[{\"ccyNbr\":\"10\",\"crtAcc\":\"6214831150131511\",\"crtNam\":\"吴极客\",\"dbtAcc\":\"755936045010501\",\"nusAge\":\"用途\",\"bnkFlg\":\"Y\",\"trsAmt\":\"1.05\",\"yurRef\":\"2024041709400601\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}";
// String data =
// "{\"request\":{\"body\":{\"bb1payqrx1\":[{\"busCod\":\"N02030\",\"yurRef\":\"2024041709400602\"}]},\"head\":{\"funcode\":\"BB1PAYQR\",\"userid\":\"N002986522\"}}}";
System.out.println(data);
DcHelper dchelper = new DcHelper(url, uid, privateKey, publicKey, symKey);
// 添加设备信息,请根据文档指引填写真实的信息,参数按顺序分别为:出口IP(无需)、MAC地址、CPU-ID、计算机名、主板ID、主板厂商
// DeviceInfo deviceInfo = new DeviceInfo(null, "9822EFF12C6B", "AABBCCDDEEFFGGHH", "XXX", "XXX", "XXX");
// String response = dchelper.sendRequest(data, funcode);
// process("Api请求1成功,响应报文:\r\n" + response);
// String response2 = dchelper.sendRequest(data, funcode, deviceInfo);
// process("Api请求2成功,响应报文:\r\n" + response2);
}
private static void process(String response) {
}
}

211
src/main/java/com/win/bank/service/cmb/CmbServiceImpl.java

@ -1,17 +1,62 @@
package com.win.bank.service.cmb; package com.win.bank.service.cmb;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.win.bank.domain.BankDO; import com.win.bank.domain.BankDO;
import com.win.bank.enums.PaymentStatusEnum;
import com.win.bank.service.BaseBankService;
import com.win.bank.utils.JsonUtil;
/**
* 招商银行
*/
@Service @Service
public class CmbServiceImpl { public class CmbServiceImpl implements BaseBankService {
// 企业网银用户号
@Value("${cmb.uid}")
private String uid;
@Value("${cmb.url}")
private String url;
// 业务模式编号
@Value("${cmb.busMod}")
private String busMod;
public String payment(BankDO bankDO) { // 算法,固定为国密算法
String data = "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"00002\"}]},\"head\":{\"funcode\":\"BB1PAYOP\",\"userid\":\"N002986522\"}}}"; private static final String alg = "SM";
private static final int CONNECT_TIMEOUT = 15000;
private static final int READ_TIMEOUT = 60000;
private static final int STATUS_OK = 200;
private static final String SUCCESS_CODE = "SUC0000";
private static final Logger logger = LoggerFactory.getLogger(CmbServiceImpl.class);
@Override
public BankDO payment(BankDO bankDO) {
String funCode = "BB1PAYOP";
String data = "{\"request\":{\"body\":{\"bb1paybmx1\":[{\"busCod\":\"N02030\",\"busMod\":\"" + busMod + "\"}]},\"head\":{\"funcode\":\"" + funCode + "\",\"userid\":\"" + uid + "\"}}}";
JsonObject requestJson = new Gson().fromJson(data, JsonObject.class); JsonObject requestJson = new Gson().fromJson(data, JsonObject.class);
JsonArray bb1payopx1 = new JsonArray(); JsonArray bb1payopx1 = new JsonArray();
JsonObject info = new JsonObject(); JsonObject info = new JsonObject();
@ -21,11 +66,167 @@ public class CmbServiceImpl {
info.addProperty("ccyNbr", "10"); info.addProperty("ccyNbr", "10");
info.addProperty("trsAmt", bankDO.getAmount().toString()); info.addProperty("trsAmt", bankDO.getAmount().toString());
info.addProperty("nusAge", bankDO.getPurpose()); info.addProperty("nusAge", bankDO.getPurpose());
info.addProperty("bnkFlg", "Y"); String bnkFlg = "Y";
if (!StringUtils.isEmpty(bankDO.getRcvBankName()) && !bankDO.getRcvBankName().contains("招商")) {
bnkFlg = "N";
}
info.addProperty("bnkFlg", bnkFlg);
info.addProperty("yurRef", bankDO.getId().toString()); info.addProperty("yurRef", bankDO.getId().toString());
bb1payopx1.add(info); bb1payopx1.add(info);
requestJson.getAsJsonObject("request").getAsJsonObject("body").add("bb1payopx1", bb1payopx1); requestJson.getAsJsonObject("request").getAsJsonObject("body").add("bb1payopx1", bb1payopx1);
System.out.println(requestJson); logger.debug("发送给招商银行的数据:" + JsonUtil.formatJson(requestJson.toString()));
String response;
try {
response = sendRequest(requestJson.toString(), funCode);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.debug("收到招商银行的数据:" + JsonUtil.formatJson(response));
JsonObject responseJson = new Gson().fromJson(response, JsonObject.class);
JsonObject headJson = responseJson.getAsJsonObject("response").getAsJsonObject("head");
if (SUCCESS_CODE.equals(headJson.get("resultcode").getAsString())) {
JsonObject resultJson = responseJson.getAsJsonObject("response").getAsJsonObject("body").getAsJsonArray("bb1payopz1").get(0).getAsJsonObject();
String errCode = resultJson.get("errCod").getAsString();
if (SUCCESS_CODE.equals(errCode)) {
bankDO.setStatus(PaymentStatusEnum.PAYING.getCode());
} else {
bankDO.setStatus(PaymentStatusEnum.FAILED.getCode());
bankDO.setMessage(resultJson.get("errTxt").getAsString());
}
} else {
bankDO.setStatus(PaymentStatusEnum.FAILED.getCode());
bankDO.setMessage(headJson.get("resultmsg").getAsString());
}
return bankDO;
}
@Override
public BankDO queryPaymentResult(BankDO bankDO) {
String funCode = "BB1PAYQR";
String yurRef = bankDO.getId().toString();
String data = "{\"request\":{\"body\":{\"bb1payqrx1\":[{\"busCod\":\"N02030\",\"yurRef\":\"" + yurRef + "\"}]},\"head\":{\"funcode\":\"" + funCode + "\",\"userid\":\"" + uid + "\"}}}";
logger.debug("发送给招商银行的数据" + JsonUtil.formatJson(data));
String response;
try {
response = sendRequest(data, funCode);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.debug("收到招商银行的数据:" + JsonUtil.formatJson(response));
JsonObject responseJson = new Gson().fromJson(response, JsonObject.class);
JsonObject headJson = responseJson.getAsJsonObject("response").getAsJsonObject("head");
if (SUCCESS_CODE.equals(headJson.get("resultcode").getAsString())) {
JsonObject resultJson = responseJson.getAsJsonObject("response").getAsJsonObject("body").getAsJsonArray("bb1payqrz1").get(0).getAsJsonObject();
String reqSts = resultJson.get("reqSts").getAsString();
if ("FIN".equals(reqSts)) {
if ("S".equals(resultJson.get("rtnFlg").getAsString())) {
bankDO.setStatus(PaymentStatusEnum.SUCCESS.getCode());
bankDO.setSerialNumber(resultJson.get("trxSeq").getAsString());
} else {
bankDO.setStatus(PaymentStatusEnum.FAILED.getCode());
bankDO.setMessage(resultJson.get("rtnNar").getAsString());
}
} else {
return null; return null;
} }
} else {
bankDO.setMessage(headJson.get("resultmsg").getAsString());
}
return bankDO;
}
public String sendRequest(String data, String funcode) throws Exception {
HashMap<String, String> map = new HashMap<>();
map.put("UID", uid);
map.put("ALG", alg);
map.put("DATA", URLEncoder.encode(data, StandardCharsets.UTF_8.displayName()));
map.put("FUNCODE", funcode);
String response = httpPost(url, map);
if (response.startsWith("CDCServer:")) {
throw new IOException("访问目标地址 " + url + " 失败:" + response);
}
return response;
}
private static String httpPost(String httpUrl, Map<String, String> param) throws IOException, GeneralSecurityException {
HttpURLConnection connection = null;
String result;
try {
URL url = new URL(httpUrl);
SSLContext sslcontext;
sslcontext = SSLContext.getInstance("SSL");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null);
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager)tm;
break;
}
}
sslcontext.init(null, new TrustManager[] {defaultTm}, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setConnectTimeout(CONNECT_TIMEOUT);
connection.setReadTimeout(READ_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try (OutputStream os = connection.getOutputStream()) {
os.write(createLinkString(param).getBytes());
if (connection.getResponseCode() != STATUS_OK) {
InputStream is = connection.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder sbf = new StringBuilder();
String temp;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
br.close();
is.close();
} else {
InputStream is = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder sbf = new StringBuilder();
String temp;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
}
result = sbf.toString();
br.close();
is.close();
}
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
return result;
}
private static String createLinkString(Map<String, String> params) {
ArrayList<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder prestr = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {
prestr.append(key).append("=").append(value);
} else {
prestr.append(key).append("=").append(value).append("&");
}
}
return prestr.toString();
}
} }

369
src/main/java/com/win/bank/service/cmb/DcHelper.java

@ -1,369 +0,0 @@
package com.win.bank.service.cmb;
import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.*;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import org.bouncycastle.asn1.*;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* 招商银行银企直联国密免前置/SaaS对接示例本示例仅供参考不保证各种异常场景运行请勿直接使用如有错漏请联系对接人员运行时请使用所获取的测试资源替换 用户编号公私钥对称密钥服务商编号等信息
*
* @author cmb.firmbank
* @date 2023/7/20
*/
public class DcHelper {
private static final int LENGTH_32 = 32;
private static final int USERID_LEN = 16;
private static final int CONNECT_TIMEOUT = 15000;
private static final int READ_TIMEOUT = 60000;
private static final int STATUS_OK = 200;
private static Base64.Encoder encoder = Base64.getEncoder();
private static Base64.Decoder decoder = Base64.getDecoder();
// 请求URL
private final String url;
// 企业网银用户号
private final String uid;
// 国密算法向量,根据用户号生成
private final byte[] userId;
// 算法,固定为国密算法
private final String alg;
// 客户私钥
private final byte[] privateKey;
// 银行公钥
private final byte[] publicKey;
// 协商的对称密钥
private final byte[] symKey;
private static final Logger logger = LoggerFactory.getLogger(DcHelper.class);
public DcHelper(String url, String uid, String privateKey, String publicKey, String symKey) {
this.url = url;
this.uid = uid;
this.userId = getUserId(uid);
this.alg = "SM";
this.privateKey = decoder.decode(privateKey);
this.publicKey = decoder.decode(publicKey);
this.symKey = symKey.getBytes(StandardCharsets.UTF_8);
}
public String sendRequest(String data, String funcode) throws IOException, CryptoException, GeneralSecurityException {
// 增加设备信息
JsonObject requestJson = new Gson().fromJson(data, JsonObject.class);
// requestJson.getAsJsonObject("request").add("exthdr", deviceInfo.toJson());
// 对请求报文做排序
String source = recursiveKeySort(requestJson);
// 生成签名
// byte[] signature = cmbSM2SignWithSM3(userId, privateKey, source.getBytes(StandardCharsets.UTF_8));
// 替换签名字段
// requestJson.getAsJsonObject("signature").addProperty("sigdat", new String(encoder.encode(signature),
// StandardCharsets.UTF_8));
// 对数据进行对称加密
// String request = requestJson.toString();
// byte[] encryptRequest = cmbSM4Crypt(symKey, userId, request.getBytes(StandardCharsets.UTF_8), 1);
// String encryptedRequest = new String(encoder.encode(encryptRequest), StandardCharsets.UTF_8);
logger.debug(data);
// 发送请求
HashMap<String, String> map = new HashMap<>();
map.put("UID", uid);
map.put("ALG", alg);
map.put("DATA", URLEncoder.encode(data, StandardCharsets.UTF_8.displayName()));
// map.put("DATA", URLEncoder.encode(encryptedRequest, StandardCharsets.UTF_8.displayName()));
map.put("FUNCODE", funcode);
String response = httpPost(url, map);
// logger.debug(response);
printJson(data);
if (response.startsWith("CDCServer:")) {
throw new IOException("访问目标地址 " + url + " 失败:" + response);
}
// 返回结果解密
// response = new String((cmbSM4Crypt(symKey, userId, decoder.decode(response), 2)), StandardCharsets.UTF_8);
// 验证签名是否正确
// JsonObject responseJson = new Gson().fromJson(response, JsonObject.class);
// JsonObject signatureJson = responseJson.getAsJsonObject("signature");
// String responseSignature = signatureJson.get("sigdat").getAsString();
// signatureJson.addProperty("sigdat", "__signature_sigdat__");
// responseJson.add("signature", signatureJson);
// String responseSorted = recursiveKeySort(responseJson);
// boolean verify = cmbSM2VerifyWithSM3(userId, publicKey, responseSorted.getBytes(StandardCharsets.UTF_8),
// decoder.decode(responseSignature));
// if (!verify) {
// throw new IOException("响应报文的签名无效");
// }
printJson(response);
return response;
}
private static String recursiveKeySort(JsonObject json) {
StringBuilder appender = new StringBuilder();
appender.append("{");
Iterator<String> keys = new TreeSet<>(json.keySet()).iterator();
boolean isFirstEle = true;
while (keys.hasNext()) {
if (!isFirstEle) {
appender.append(",");
}
String key = keys.next();
Object val = json.get(key);
if (val instanceof JsonObject) {
appender.append("\"").append(key).append("\":");
appender.append(recursiveKeySort((JsonObject)val));
} else if (val instanceof JsonArray) {
JsonArray jarray = (JsonArray)val;
appender.append("\"").append(key).append("\":[");
boolean isFirstArrEle = true;
for (int i = 0; i < jarray.size(); i++) {
if (!isFirstArrEle) {
appender.append(",");
}
Object obj = jarray.get(i);
if (obj instanceof JsonObject) {
appender.append(recursiveKeySort((JsonObject)obj));
} else {
appender.append(obj.toString());
}
isFirstArrEle = false;
}
appender.append("]");
} else {
String value = val.toString();
appender.append("\"").append(key).append("\":").append(value);
}
isFirstEle = false;
}
appender.append("}");
return appender.toString();
}
private static byte[] getUserId(String uid) {
return (uid + "0000000000000000").substring(0, USERID_LEN).getBytes();
}
private static String httpPost(String httpUrl, Map<String, String> param) throws IOException, GeneralSecurityException {
HttpURLConnection connection = null;
String result;
try {
URL url = new URL(httpUrl);
SSLContext sslcontext;
sslcontext = SSLContext.getInstance("SSL");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null);
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager)tm;
break;
}
}
sslcontext.init(null, new TrustManager[] {defaultTm}, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setConnectTimeout(CONNECT_TIMEOUT);
connection.setReadTimeout(READ_TIMEOUT);
connection.setInstanceFollowRedirects(true);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try (OutputStream os = connection.getOutputStream()) {
os.write(createLinkString(param).getBytes());
if (connection.getResponseCode() != STATUS_OK) {
InputStream is = connection.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder sbf = new StringBuilder();
String temp;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
br.close();
is.close();
} else {
InputStream is = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder sbf = new StringBuilder();
String temp;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
}
result = sbf.toString();
br.close();
is.close();
}
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
return result;
}
private static String createLinkString(Map<String, String> params) {
ArrayList<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder prestr = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {
prestr.append(key).append("=").append(value);
} else {
prestr.append(key).append("=").append(value).append("&");
}
}
return prestr.toString();
}
// 以下是加解密相关的函数
private static byte[] cmbSM2SignWithSM3(byte[] id, byte[] privkey, byte[] msg) throws IOException, CryptoException {
if (privkey == null || msg == null) {
throw new CryptoException("CMBSM2SignWithSM3 input error");
}
ECPrivateKeyParameters privateKey = encodePrivateKey(privkey);
SM2Signer signer = new SM2Signer();
ParametersWithID parameters = new ParametersWithID(privateKey, id);
signer.init(true, parameters);
signer.update(msg, 0, msg.length);
return decodeDERSignature(signer.generateSignature());
}
private static ECPrivateKeyParameters encodePrivateKey(byte[] value) {
BigInteger d = new BigInteger(1, value);
ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed());
return new ECPrivateKeyParameters(d, ecParameters);
}
private static byte[] decodeDERSignature(byte[] signature) throws IOException {
ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(signature));
ASN1Sequence primitive = (ASN1Sequence)stream.readObject();
Enumeration<ASN1Integer> enumeration = primitive.getObjects();
BigInteger intR = enumeration.nextElement().getValue();
BigInteger intS = enumeration.nextElement().getValue();
byte[] bytes = new byte[LENGTH_32 * 2];
byte[] r = format(intR.toByteArray());
byte[] s = format(intS.toByteArray());
System.arraycopy(r, 0, bytes, 0, LENGTH_32);
System.arraycopy(s, 0, bytes, LENGTH_32, LENGTH_32);
return bytes;
}
private static byte[] format(byte[] value) {
if (value.length == LENGTH_32) {
return value;
} else {
byte[] bytes = new byte[LENGTH_32];
if (value.length > LENGTH_32) {
System.arraycopy(value, value.length - LENGTH_32, bytes, 0, LENGTH_32);
} else {
System.arraycopy(value, 0, bytes, LENGTH_32 - value.length, value.length);
}
return bytes;
}
}
private static byte[] cmbSM4Crypt(byte[] key, byte[] iv, byte[] input, int mode) throws GeneralSecurityException {
SecretKeySpec spec = new SecretKeySpec(key, "SM4");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(mode, spec, ivParameterSpec);
return cipher.doFinal(input);
}
private static boolean cmbSM2VerifyWithSM3(byte[] id, byte[] pubkey, byte[] msg, byte[] signature) throws IOException {
if (pubkey == null || msg == null || signature == null) {
throw new IllegalArgumentException("CMBSM2VerifyWithSM3 input error");
}
ECPublicKeyParameters publicKey = encodePublicKey(pubkey);
SM2Signer signer = new SM2Signer();
ParametersWithID parameters = new ParametersWithID(publicKey, id);
signer.init(false, parameters);
signer.update(msg, 0, msg.length);
return signer.verifySignature(encodeDERSignature(signature));
}
private static ECPublicKeyParameters encodePublicKey(byte[] value) {
byte[] x = new byte[LENGTH_32];
byte[] y = new byte[LENGTH_32];
System.arraycopy(value, 1, x, 0, LENGTH_32);
System.arraycopy(value, LENGTH_32 + 1, y, 0, LENGTH_32);
BigInteger intX = new BigInteger(1, x);
BigInteger intY = new BigInteger(1, y);
ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
ECPoint intQ = spec.getCurve().createPoint(intX, intY);
ECDomainParameters ecParameters = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed());
return new ECPublicKeyParameters(intQ, ecParameters);
}
private static byte[] encodeDERSignature(byte[] signature) throws IOException {
byte[] r = new byte[LENGTH_32];
byte[] s = new byte[LENGTH_32];
System.arraycopy(signature, 0, r, 0, LENGTH_32);
System.arraycopy(signature, LENGTH_32, s, 0, LENGTH_32);
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new ASN1Integer(new BigInteger(1, r)));
vector.add(new ASN1Integer(new BigInteger(1, s)));
return (new DERSequence(vector)).getEncoded();
}
private void printJson(String jsonStr) {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try {
// 原始的JSON字符串
// String jsonStr = "{\"name\": \"Alice\", \"age\": 30, \"city\": \"Beijing\"}";
// 格式化打印JSON字符串
Object json = mapper.readValue(jsonStr, Object.class);
String formattedJsonStr = mapper.writeValueAsString(json);
// System.out.println(formattedJsonStr);
logger.debug(formattedJsonStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}

39
src/main/java/com/win/bank/service/cmb/DeviceInfo.java

@ -1,39 +0,0 @@
package com.win.bank.service.cmb;
import com.google.gson.JsonObject;
/**
* 设备信息
*/
public class DeviceInfo {
private String ipVal;
private String macVal;
private String cpuInfo;
private String computerName;
private String mainBoardId;
private String mainBoardName;
public DeviceInfo(String ipVal, String macVal, String cpuInfo, String computerName, String mainBoardId, String mainBoardName) {
this.ipVal = ipVal;
this.macVal = macVal;
this.cpuInfo = cpuInfo;
this.computerName = computerName;
this.mainBoardId = mainBoardId;
this.mainBoardName = mainBoardName;
}
public JsonObject toJson() {
JsonObject deviceInfo = new JsonObject();
if (ipVal != null) {
deviceInfo.addProperty("ipType", ipVal.indexOf(':') != -1 ? "6" : "4");
deviceInfo.addProperty("ipVal", ipVal);
}
deviceInfo.addProperty("macVal", macVal);
deviceInfo.addProperty("cpuInfo", cpuInfo);
deviceInfo.addProperty("cmpName", computerName);
deviceInfo.addProperty("mbId", mainBoardId);
deviceInfo.addProperty("mbMf", mainBoardName);
return deviceInfo;
}
}

54
src/main/java/com/win/bank/utils/BaseBankServiceUtil.java

@ -0,0 +1,54 @@
package com.win.bank.utils;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.reflections.Reflections;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.win.bank.service.BaseBankService;
@Component
public class BaseBankServiceUtil implements ApplicationContextAware {
ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
private static Map<String, BaseBankService> map = new HashMap<>();
public static BaseBankService get(String code) {
return map.get(code);
}
@PostConstruct
public void init() {
String packageName = "com.win.bank.service";
Reflections reflections = new Reflections(packageName);
Set<Class<? extends BaseBankService>> subTypes = reflections.getSubTypesOf(BaseBankService.class);
for (Class<?> clazz : subTypes) {
if (!Modifier.isAbstract(clazz.getModifiers()) && !clazz.isInterface()) {
String key = clazz.getPackageName().substring(clazz.getPackageName().lastIndexOf(".") + 1).toUpperCase();
BaseBankService instance = null;
try {
instance = (BaseBankService)context.getBean(Class.forName(clazz.getName()));
map.put(key, instance);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
}

20
src/main/java/com/win/bank/utils/JsonUtil.java

@ -0,0 +1,20 @@
package com.win.bank.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
public class JsonUtil {
public static String formatJson(String jsonStr) {
try {
ObjectMapper mapper = new ObjectMapper();
Object json = mapper.readValue(jsonStr, Object.class);
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
String prettyJson = '\n' + writer.writeValueAsString(json);
return prettyJson;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

8
src/main/resources/application-dev.yml

@ -31,3 +31,11 @@ logging:
level: level:
com.win: debug com.win: debug
org.springframework: warn org.springframework: warn
###################### 以下是银行配置 ######################
# 招商银行
cmb:
uid: N002986522
busMod: 00002
url: http://192.168.0.142:8080/cdcserver/api/v2
Loading…
Cancel
Save