|
@@ -0,0 +1,380 @@
|
|
|
+package com.hrsk.cloud.eg.domain.api.channel.customized.threedocking.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson2.JSON;
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.hrsk.cloud.eg.client.dto.plan.command.EgLoanApiConfigInfoCmd;
|
|
|
+import com.hrsk.cloud.eg.client.dto.plan.command.PlanCmd;
|
|
|
+import com.hrsk.cloud.eg.client.dto.user.command.UserBaseInfoCmd;
|
|
|
+import com.hrsk.cloud.eg.clinet.vo.DataVo;
|
|
|
+import com.hrsk.cloud.eg.infrastructure.config.client.HessianUtils;
|
|
|
+import com.hrsk.cloud.eg.infrastructure.loanMannager.threedocking.LoanDockingApi;
|
|
|
+import com.hrsk.cloud.eg.infrastructure.service.EgApiService;
|
|
|
+import com.hrsk.cloud.eg.infrastructure.utils.RedisUtil;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.codec.binary.Base64;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.*;
|
|
|
+import java.security.spec.InvalidKeySpecException;
|
|
|
+import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
+import java.security.spec.X509EncodedKeySpec;
|
|
|
+import java.util.Random;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Descrption
|
|
|
+ * @Author: chend
|
|
|
+ * @Date: 2023/2/8
|
|
|
+ * @Version V1.0
|
|
|
+ **/
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class RongshengCreditService implements LoanDockingApi {
|
|
|
+
|
|
|
+ private final static String checkPath = "ad/h5/tripartite/v1/cs2";
|
|
|
+ private final static String applyPath = "ad/h5/tripartite/v1/ps";
|
|
|
+
|
|
|
+ private final static String RedisKey = "Rongsheng:api:%s";
|
|
|
+ @Resource
|
|
|
+ private EgApiService egApiService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private RetryRestTemplate httpRestTemplate;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private RedisUtil redisUtil;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ private static class CheckInfo {
|
|
|
+ private String channelCode;
|
|
|
+ private String bizData;
|
|
|
+ private String miAesKey;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ private static class Result {
|
|
|
+ private Integer code;
|
|
|
+ private String msg;
|
|
|
+ private JSONObject data;
|
|
|
+ private Long timestamp;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public DataVo qualityCheck(UserBaseInfoCmd userInfo, PlanCmd product, EgLoanApiConfigInfoCmd configInfo) {
|
|
|
+ try {
|
|
|
+ log.info("credit RongshengCreditService qualityCheck begin...userId:{}", userInfo.getUserId());
|
|
|
+// XdProductJointConfig xdProductJointConfig = xdProductJointConfigMapper.selectById(product.getJointType());
|
|
|
+
|
|
|
+ byte[] requestConfig = configInfo.getRequestConfig();
|
|
|
+ Object deserialize = HessianUtils.deserialize(requestConfig);
|
|
|
+ JSONObject config = JSONObject.parseObject(deserialize.toString());
|
|
|
+ log.info("荣晟对接参数:{}", JSON.toJSONString(config));
|
|
|
+ String md5Phone = userInfo.getUserMobileMD5();
|
|
|
+ String aesKey = AesUtil.getKey();
|
|
|
+ String miAesKey = RsaUtil.encryptByPublicKey(aesKey, config.getString("publicKey"));
|
|
|
+ String bizData = AesUtil.encrypt(md5Phone, aesKey);
|
|
|
+
|
|
|
+ CheckInfo checkInfo = new CheckInfo();
|
|
|
+ checkInfo.setBizData(bizData);
|
|
|
+ checkInfo.setMiAesKey(miAesKey);
|
|
|
+ checkInfo.setChannelCode(config.getString("channelSource"));
|
|
|
+ String url = config.getString("url") + checkPath;
|
|
|
+ log.info("credit qualityCheck RongshengCreditService request md5Phone:{}, url:{}, CheckInfo:{}", md5Phone, url, checkInfo);
|
|
|
+ Result response = null;
|
|
|
+ try {
|
|
|
+ response = httpRestTemplate.restTemplate().postForObject(url, checkInfo, Result.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("荣晟准入异常,异常信息:{}", e.getMessage(), e);
|
|
|
+ }
|
|
|
+ log.info("credit qualityCheck RongshengCreditService response userId:{}, result:{}", userInfo.getUserId(), response);
|
|
|
+ if (response == null) {
|
|
|
+ return DataVo.timeout(product.getPlanId(), product.getPlanName());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (0 == response.getCode()
|
|
|
+ && StringUtils.isNotBlank(response.getData().getString("applyId"))
|
|
|
+ && StringUtils.isNotBlank(response.getData().getString("key"))) {
|
|
|
+ String applyId = response.getData().getString("applyId");
|
|
|
+ String key = response.getData().getString("key");
|
|
|
+ String userRedisKey = String.format(RedisKey, md5Phone);
|
|
|
+ redisUtil.set(userRedisKey, applyId + "_" + key, 300, TimeUnit.SECONDS);
|
|
|
+ return DataVo.success(0, "荣晟撞库成功", product.getPlanId());
|
|
|
+ }
|
|
|
+
|
|
|
+ return DataVo.fail(response.getMsg(), product.getPlanId());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("荣晟撞库失败", e);
|
|
|
+ return DataVo.fail(1, "荣晟撞库失败,请联系系统管理员", product.getPlanId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ServerCodeEnums getType() {
|
|
|
+ return ServerCodeEnums.RongSheng;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class AesUtil {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加密
|
|
|
+ *
|
|
|
+ * @param sSrc 加密数据
|
|
|
+ * @param sKey aeskey
|
|
|
+ * @return : java.lang.String
|
|
|
+ * @date : 2021/3/30
|
|
|
+ */
|
|
|
+ public static String encrypt(String sSrc, String sKey) throws Exception {
|
|
|
+ if (sKey == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 判断 Key 是否为 16 位
|
|
|
+ if (sKey.length() != 16) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] raw = sKey.getBytes("utf-8");
|
|
|
+ SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
|
|
+ // "算法/模式/补码方式"
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
|
|
|
+ byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
|
|
|
+ // 此处使用 BASE64 做转码功 能,同时能起到 2 次加密的作用。
|
|
|
+ return new Base64().encodeToString(encrypted);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解密
|
|
|
+ *
|
|
|
+ * @param sSrc 加密数据
|
|
|
+ * @param sKey aeskey
|
|
|
+ * @return : java.lang.String
|
|
|
+ * @date : 2021/3/30
|
|
|
+ */
|
|
|
+ public static String decrypt(String sSrc, String sKey) throws Exception {
|
|
|
+ try {
|
|
|
+ // 判断 Key 是否正确
|
|
|
+ if (sKey == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 判断 Key 是否为 16 位
|
|
|
+ if (sKey.length() != 16) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ byte[] raw = sKey.getBytes("utf-8");
|
|
|
+ SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, skeySpec);
|
|
|
+ // 先用 base64 解密
|
|
|
+ byte[] encrypted1 = new Base64().decode(sSrc);
|
|
|
+ try {
|
|
|
+ byte[] original = cipher.doFinal(encrypted1);
|
|
|
+ return new String(original, "utf-8");
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ } catch (Exception ex) {
|
|
|
+ ex.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取key
|
|
|
+ */
|
|
|
+ public static String getKey() {
|
|
|
+ StringBuilder uid = new StringBuilder();
|
|
|
+ //产生16位的强随机数
|
|
|
+ Random rd = new SecureRandom();
|
|
|
+ for (int i = 0; i < 16; i++) {
|
|
|
+ //产生0-2的3位随机数
|
|
|
+ int type = rd.nextInt(3);
|
|
|
+ switch (type) {
|
|
|
+ case 0:
|
|
|
+ //0-9的随机数
|
|
|
+ uid.append(rd.nextInt(10));
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ //ASCII在65-90之间为大写,获取大写随机
|
|
|
+ uid.append((char) (rd.nextInt(25) + 65));
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ //ASCII在97-122之间为小写,获取小写随机
|
|
|
+ uid.append((char) (rd.nextInt(25) + 97));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return uid.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static class RsaUtil {
|
|
|
+
|
|
|
+ private static final String KEY_ALGORITHM = "RSA";
|
|
|
+ public static final String DEFAULT_ALGORITHM = "MD5withRSA";
|
|
|
+ private static final String DEFAULT_ENCODING = "UTF-8";
|
|
|
+
|
|
|
+ public static String sign(String privateKey, String dataSrc) {
|
|
|
+ return sign(privateKey, dataSrc, DEFAULT_ALGORITHM);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 签名
|
|
|
+ *
|
|
|
+ * @param privateKey:私钥
|
|
|
+ * @param dataSrc:源
|
|
|
+ * @param algorithm:签名算法
|
|
|
+ * @return 签名
|
|
|
+ */
|
|
|
+ public static String sign(
|
|
|
+ String privateKey,
|
|
|
+ String dataSrc,
|
|
|
+ String algorithm
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
|
|
|
+ Base64.decodeBase64(privateKey)
|
|
|
+ );
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ PrivateKey myPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
|
|
|
+ Signature signature = Signature.getInstance(algorithm);
|
|
|
+ signature.initSign(myPrivateKey);
|
|
|
+ signature.update(dataSrc.getBytes(DEFAULT_ENCODING));
|
|
|
+ byte[] signed = signature.sign();
|
|
|
+ return Base64.encodeBase64String(signed);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 签名验证
|
|
|
+ *
|
|
|
+ * @param publicKey:公钥
|
|
|
+ * @param dataSrc:源
|
|
|
+ * @param sign:签名结果串
|
|
|
+ * @return 验签结果
|
|
|
+ */
|
|
|
+ public static boolean checkSign(
|
|
|
+ String publicKey,
|
|
|
+ String dataSrc,
|
|
|
+ String sign
|
|
|
+ ) throws Exception {
|
|
|
+ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
|
|
|
+ Base64.decodeBase64(publicKey)
|
|
|
+ );
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ PublicKey myPublicKey = keyFactory.generatePublic(x509EncodedKeySpec);
|
|
|
+ byte[] signed = Base64.decodeBase64(sign);
|
|
|
+ Signature signature = Signature.getInstance(
|
|
|
+ DEFAULT_ALGORITHM
|
|
|
+ );
|
|
|
+ signature.initVerify(myPublicKey);
|
|
|
+ signature.update(dataSrc.getBytes(DEFAULT_ENCODING));
|
|
|
+ return signature.verify(signed);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static PublicKey getPublicKey(String idRsaPub) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
+ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
|
|
|
+ Base64.decodeBase64(idRsaPub)
|
|
|
+ );
|
|
|
+ KeyFactory keyFactory;
|
|
|
+ keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ return keyFactory.generatePublic(x509EncodedKeySpec);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static PrivateKey getPrivateKey(String idRsa) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
|
+ PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
|
|
|
+ Base64.decodeBase64(idRsa)
|
|
|
+ );
|
|
|
+ KeyFactory keyFactory1;
|
|
|
+ keyFactory1 = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ return keyFactory1.generatePrivate(pkcs8EncodedKeySpec);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] decryptByPrivateKey(byte[] encryptedData, String idRsa)
|
|
|
+ throws Exception {
|
|
|
+ Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
|
|
|
+ cipher.init(2, getPrivateKey(idRsa));
|
|
|
+ int inputLen = encryptedData.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+
|
|
|
+ for (int i = 0; inputLen - offSet > 0; offSet = i * 256) {
|
|
|
+ byte[] cache;
|
|
|
+ if (inputLen - offSet > 256) {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, 256);
|
|
|
+ } else {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] decryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return decryptedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String decryptByPrivateKey(String data, String idRsa) throws Exception {
|
|
|
+ byte[] encryptedData = Base64.decodeBase64(data);
|
|
|
+ byte[] result = decryptByPrivateKey(encryptedData, idRsa);
|
|
|
+ if (result == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return new String(result, StandardCharsets.UTF_8);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String encryptByPublicKey(String data, String idRsaPub) throws Exception {
|
|
|
+ byte[] encryptedData = data.getBytes(StandardCharsets.UTF_8);
|
|
|
+ byte[] result = encryptByPublicKey(encryptedData, idRsaPub);
|
|
|
+ if (result == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return Base64.encodeBase64String(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encryptByPublicKey(byte[] data, String idRsaPub)
|
|
|
+ throws Exception {
|
|
|
+ PublicKey publicKey = getPublicKey(idRsaPub);
|
|
|
+ Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
|
|
|
+ cipher.init(1, publicKey);
|
|
|
+ int inputLen = data.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+
|
|
|
+ for (int i = 0; inputLen - offSet > 0; offSet = i * 244) {
|
|
|
+ byte[] cache;
|
|
|
+ if (inputLen - offSet > 244) {
|
|
|
+ cache = cipher.doFinal(data, offSet, 244);
|
|
|
+ } else {
|
|
|
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ byte[] encryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return encryptedData;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|