调用CFCA金信反欺诈服务相关接口,很详细

编程入门 行业动态 更新时间:2024-10-12 05:54:50

调用CFCA金信反欺诈服务相关<a href=https://www.elefans.com/category/jswz/34/1771365.html style=接口,很详细"/>

调用CFCA金信反欺诈服务相关接口,很详细

调用CFCA金信反欺诈服务相关接口,很详细

  • 一、准备
  • 二、调用接口
    • 1、查询接口文档
    • 2、查看代码示例
    • 3、测试调用接口
  • 三、工具类
    • 1、CFCA金信反欺诈服务接口码枚举类
    • 2、CFCA金信反欺诈服务的公共参数配置
    • 3、加密解密工具类
    • 4、请求参数dto
    • 5、调用接口工具类(关键是这个)

一、准备

之前对接过CFCA安心签相关的接口,以为这次对接也会很麻烦,现实是这次比想象中的要简单一点,起码加白名单就很快。

老规矩,先找CFCA对接的技术人员要相关资料,并让他们帮你的服务器加上白名单。

这是金信反欺诈服务的产品,放在Excel表中,看需要使用哪一个产品

找到目标产品后,找对接的技术人员要相关的文档和demo。

二、调用接口

我这次对接的是运营商风险识别 (三要素详版),也就是三要素核验接口,以下称为三要素核验

1、查询接口文档

这是三要素核验对应的技术文档,是一个PDF文件

先看请求参数

对应请求体示例:

{"transcode": "209","sign": "...","version": "0100","ordersn": "...","dsorderid": "...","merchno": "...","timestamp": "1643170610550","data": {"username": "用户名","idcard": "身份证","mobile": "手机号","idtype": "01","sceneCode": "0x","sCustomerName": "xx-xx-xx","scUsePurpose": "xxxxx","protocolVerNm": "xxxxxxx","serialNm": "xxxxxxxxx","reqIp": "","remark": ""}
}

然后是响应参数

每个接口成功的响应码相同,失败的响应可能不一样,这是三要素核验的响应码


2、查看代码示例

三要素核验的代码示例在一个压缩包里,解压之后就可以找到对应的代码示例,所有的代码示例都在这里

解压之后可以看到所有的示例

不知道代码示例在那可以先看这里

基本上列举了所有产品接口

这是三要素核验的接口码

我要找的三要素核验示例代码在这

对应三要素核验的示例代码

package com.jinxin;import com.jinxin.utils.BaseTest;
import com.jinxin.utils.TransCodeEnum;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** author: weidong* create: 2021-06-13**/
public class Test209 extends BaseTest {public static void main(String[] args) throws Exception {// 获取枚举类中指定业务的地址后缀部分,前缀需要在【商户信息.properties】填写String suffix = TransCodeEnum.TRANS_CODE_209.getUrl();// 请求最外层参数---------------------------------------------------------------------------------------Map<String, Object> reqMap = new HashMap<>();reqMap.put("transcode", TransCodeEnum.TRANS_CODE_209.getTranscode()); // 接口业务编码,具体业务请参考接口文档reqMap.put("merchno", BaseTest.merchNo); // 商户号reqMap.put("ordersn", UUID.randomUUID().toString().replaceAll("-", ""));    //商户流水号reqMap.put("dsorderid", UUID.randomUUID().toString().replaceAll("-", ""));  //商户订单号reqMap.put("version", "0100"); //api版本号reqMap.put("timestamp", System.currentTimeMillis() + ""); // 时间戳// 请求内层 data 参数====================================================================================Map<String, Object> data = new HashMap<>();data.put("username", "请输入姓名");data.put("mobile", "请输入手机号");data.put("idcard", "请输入身份证");data.put("idtype", "01");data.put("sCustomerName", ""); // 二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法data.put("sceneCode", "");   //真实场景 见接口文档枚举data.put("scUsePurpose", ""); // 预留字段data.put("protocolVerNm", ""); // 预留字段data.put("serialNm", ""); // 预留字段data.put("reqIp", "");  // 请求ipdata.put("remark", ""); // 备注request(reqMap, data, suffix); // 调用此方法设置拼接请求地址、生成签名信息、对数据进行加密,后发起请求、解密响应数据。}}

示例里面只有请求参数,没有响应参数,需要查看接口文档

调用接口还需要对应的证书文件,在这里

“jks”文件夹下来的所有文件都需要。

3、测试调用接口

调用接口不需要导入jar包,直接将demo中的代码拿过来并且正确传入参数,那应该就可以调用成功了,但调用的过程中感觉demo中的代码繁琐了点,就做了一些简化,代码如下:

public static void main(String[] args) {CFCAJINXINConfig config = new CFCAJINXINConfig();config.setHost("请求地址前缀部分");config.setKeystorePath("客户通信证书路径(证书需要申请)");config.setKeystorePassword("客户通信证书密码");config.setTruststorePath("金信通信证书信任库路径(证书由金信提供)");config.setTruststorePassword("金信通信证书信任库密码");config.setMerchSM2PrivateKey("商户私钥");config.setJinXinSM2PublicKey("金信平台公钥");config.setMerchNo("商户号");config.setVersion("api版本号");config.setTranscode(CFCAJINXINCodeEnum.CODE_209.getCode());CFCAJINXINCode209ReqDto params = new CFCAJINXINCode209ReqDto();params.setUsername("你的名字");params.setMobile("你的手机号码");params.setIdcard("你的身份证号码");params.setIdtype("01");try {sendRequest(config, params);} catch (Exception e) {e.printStackTrace();}
}

这里只是测试调用的代码,详情可以查看工具类那里,所有代码都放在那了。

调用成功后控制台打印是这样的:

返回结果格式化后是这样的:

三、工具类

1、CFCA金信反欺诈服务接口码枚举类

/*** CFCA金信反欺诈服务接口码枚举类** @author:gan* @date: 2023-09-21 09:41*/
public enum CFCAJINXINCodeEnum {CODE_105("105", "银行卡风险识别(二要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),CODE_106("106", "银行卡风险识别(三要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),CODE_107("107", "银行卡风险识别(四要素简版))", "/jxdata/api/auth/jm/execute2.do", ""),CODE_108("108", "运营商风险识别(二要素))", "/jxdata/api/auth/jm/execute2.do", ""),CODE_109("109", "运营商风险识别(三要素)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_116("116", "银行卡风险识别(三要素详版)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_117("117", "银行卡风险识别(四要素详版-非身份证)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_118("118", "身份信息识别(国民二要素)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_209("209", "运营商风险识别(三要素详版)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_307("307", "身份信息识别(国民四要素)", "/jxdata/api/auth/jm/execute2.do", ""),// 313 业务有两个url,请查看 Test313代码中的路径CODE_313("313", "H5活体识别+人像比对", "", ""),// 314 业务有两个url,请查看 Test314代码中的路径CODE_314("314", "H5活体识别", "", ""),CODE_405("405", "银行卡账户风险协查", "/jxdata/api/auth/jm/execute2.do", ""),CODE_407("407", "账户单卡认证", "/jxdata/api/auth/jm/execute2.do", ""),CODE_408("408", "账户三要素认证", "/jxdata/api/auth/jm/execute2.do", ""),CODE_412("412", "银行卡账户风险协查(反欺诈识别)", "/jxdata/api/auth/jm/execute2.do", ""),CODE_610("610", "不良银联卡识别", "/jxdata/api/auth/jm/execute2.do", ""),CODE_611("611", "银联不良持卡人识别", "/jxdata/api/auth/jm/execute2.do", ""),CODE_612("612", "银联风险电话识别", "/jxdata/api/auth/jm/execute2.do", ""),CODE_901("901", "企业信息三要素认证", "/jxdata/api/auth/jm/execute2.do", ""),CODE_902("902", "对公账户二要素", "/jxdata/api/auth/jm/execute2.do", ""),CODE_906("906", "企业信息认证四要素", "/jxdata/api/auth/jm/execute2.do", ""),CODE_121("121", "身份信息识别(实名人像认证)", "/jxdata/api/auth/living/execute2.do", ""),CODE_124("124", "身份信息识别(身份证照片比对)", "/jxdata/api/auth/living/execute2.do", ""),CODE_311("311", "静默活体认证", "/jxdata/api/auth/living/execute2.do", ""),// 309 业务有两个url,请查看 Test309 代码中的路径CODE_309("309", "读数活体认证", "", ""),CODE_903("903", "企业工商信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_907("907", "LEI编码査企业信息", "/jxdata/api/auth/company/execute2.do", ""),CODE_908("908", "企业四要素信息认证详版", "/jxdata/api/auth/company/execute2.do", ""),CODE_910("910", "企业司法风险报告", "/jxdata/api/auth/company/execute2.do", ""),CODE_911("911", "企业经营异常核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_912("912", "企业股权穿透识别", "/jxdata/api/auth/company/execute2.do", ""),CODE_913("913", "企业经纬度查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_914("914", "税务登记号核验", "/jxdata/api/auth/company/execute2.do", ""),CODE_915("915", "企业深度查验报告", "/jxdata/api/auth/company/execute2.do", ""),CODE_916("916", "一址多照核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_917("917", "企业最终受益人查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_919("919", "纸质营业执照二维码查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_922("922", "严重违法核查", "/jxdata/api/auth/company/execute2.do", ""),// 923 业务有两个url,请查看 Test923 代码中的路径CODE_923_9231("923", "小额打款", "/jxdata/api/auth/company/execute2.do", ""),CODE_925("925", "发票要素核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_926("926", "企业工商详细信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_927("927", "诉讼信息核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_928("928", "企业知识产权核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_929("929", "税务信用列表核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_930("930", "进出口信用列表核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_9301("9301", "电子营业执照", "/jxdata/api/auth/company/execute2.do", ""),//    TRANS_CODE_903("926", "企业工商信息详细查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_932("932", "企业经营状况报告", "/jxdata/api/auth/company/execute2.do", ""),CODE_933("933", "证书状态查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_934("934", "证书所属机构查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_935("935", "企业证书信息验证", "/jxdata/api/auth/company/execute2.do", ""),CODE_936("936", "企业多维智能反欺诈评分", "/jxdata/api/auth/company/execute2.do", ""),CODE_937("937", "企业综合信用报告", "/jxdata/api/auth/company/execute2.do", ""),CODE_938("938", "企业信用决策评分", "/jxdata/api/auth/company/execute2.do", ""),CODE_939("939", "个人严重违法核查", "/risk/api/execute2.do", ""),CODE_940("940", "电信诈骗识别(含账户涉诈/客户涉诈", "/risk/api/execute2.do", ""),CODE_941("941", "涉金融风险黑名单", "/risk/api/execute2.do", ""),CODE_942("942", "涉诉信息核查(详版)", "/risk/api/execute2.do", ""),CODE_945("945", "企业经营风险核查", "/risk/api/execute2.do", ""),CODE_946("946", "企业综合风险评分", "/risk/api/execute2.do", ""),CODE_947("947", "企业经营信息", "/risk/api/execute2.do", ""),CODE_948("948", "企业红名单", "/risk/api/execute2.do", ""),CODE_949("949", "企业评价信息", "/risk/api/execute2.do", ""),CODE_943("943", "企业失信核查", "/risk/api/execute2.do", ""),CODE_944("944", "企业处罚核查", "/risk/api/execute2.do", ""),CODE_957("957", "企业法人信息核验", "/jxdata/api/auth/jm/execute2.do", ""),CODE_960("960", "企业信息核验", "/jxdata/api/auth/jm/execute2.do", ""),CODE_953("953", "小微企业查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_951("951", "网站备案信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_955("955", "税务评级查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_954("954", "组织机构信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_950("950", "企业族谱查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_962("962", "企业名称模糊查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_961("961", "附近公司信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_956("956", "商圈企业信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_958("958", "企业信息查询(照面)", "/jxdata/api/auth/company/execute2.do", ""),CODE_965("965", "企业信息验证(三要素)", "/jxdata/api/auth/company/execute2.do", ""),CODE_966("966", "企业法人信息验证(四要素)", "/jxdata/api/auth/company/execute2.do", ""),CODE_959("959", "企业信息查询(增值)", "/jxdata/api/auth/company/execute2.do", ""),CODE_952("952", "企业人员信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_971("971", "企业人员信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_973("973", "IP风险核查", "/jxdata/api/auth/company/execute2.do", ""),CODE_974("974", "IP归属地核查-IPv4版", "/jxdata/api/auth/company/execute2.do", ""),CODE_975("975", "IP归属地核查-IPv6版", "/jxdata/api/auth/company/execute2.do", ""),CODE_970("970", "工商风险核查报告", "/jxdata/api/auth/company/execute2.do", ""),CODE_977("977", "电信诈骗识别-商户版", "/risk/api/execute2.do", ""),CODE_978("978", "涉金融风险黑名单-商户版", "/risk/api/execute2.do", ""),CODE_979("979", "电子营业执照申请授权二维码", "/jxdata/api/auth/company/execute2.do", ""),CODE_980("980", "电子营业执照验证授权二维码", "/jxdata/api/auth/company/execute2.do", ""),CODE_981("981", "电子营业执照执照信息查询", "/jxdata/api/auth/company/execute2.do", ""),CODE_982("982", "电子营业执照执照有效性验证", "/jxdata/api/auth/company/execute2.do", ""),CODE_983("983", "电子营业执照企业人员身份验证", "/jxdata/api/auth/company/execute2.do", ""),CODE_984("984", "电子营业执照持照人身份验证", "/jxdata/api/auth/company/execute2.do", ""),;private final String code;private final String desc;private final String url;private final String params; // 备用属性,暂时无用CFCAJINXINCodeEnum(String code, String desc, String url, String params) {this.code = code;this.desc = desc;this.url = url;this.params = params;}public String getCode() {return code;}public String getUrl() {return url;}public static String getUrl(String code) {for (CFCAJINXINCodeEnum codeEnum : values()) {if (codeEnum.getCode().equals(code)) {return codeEnum.url;}}throw new RuntimeException("未知CFCA金信接口码:" + code);}public String getDesc() {return desc;}public static String getDesc(String code) {for (CFCAJINXINCodeEnum codeEnum : values()) {if (codeEnum.getCode().equals(code)) {return codeEnum.desc;}}throw new RuntimeException("未知CFCA金信接口码:" + code);}public String getParams() {return params;}public static CFCAJINXINCodeEnum getByCode(String code) {if (code == null || "".equals(code)) {throw new NullPointerException("v 为空");}for (CFCAJINXINCodeEnum codeEnum : values()) {if (codeEnum.getCode().equals(code)) {return codeEnum;}}throw new NullPointerException("没有此业务");}
}

2、CFCA金信反欺诈服务的公共参数配置

import lombok.Data;import java.io.Serializable;/*** CFCA 金信反欺诈服务的公共参数配置dto** @author:gan* @date: 2023-09-21 10:10*/
@Data
public class CFCAJINXINConfig implements Serializable {//请求地址前缀部分,格式:http://192.168.1.223:8091private String host;//商户号private String merchNo;//金信平台公钥private String jinXinSM2PublicKey;//商户私钥private String merchSM2PrivateKey;//客户通信证书路径(证书需要申请)private String keystorePath;//客户通信证书密码private String keystorePassword;//金信通信证书信任库路径(证书由金信提供)private String truststorePath;//金信通信证书信任库密码private String truststorePassword;//接口业务编码private String transcode;//商户流水号private String ordersn;//商户订单号private String dsorderid;//api版本号private String version;//时间戳private String timestamp;//二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法private String sCustomerName;//真实场景 见接口文档枚举private String sceneCode;//预留字段private String scUsePurpose;//预留字段private String protocolVerNm;//预留字段private String serialNm;//请求ipprivate String reqIp;//备注private String remark;
}

3、加密解密工具类

这是demo里面的

import cfca.sadk.bouncycastle.util.encoders.Hex;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Base64;
//import org.bouncycastle.util.encoders.Hex;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;/*** bcprov-jdk15on 版本适用(1.61-1.68)*/
public class BC_SM2 {private BouncyCastleProvider provider;private X9ECParameters parameters;private ECParameterSpec ecParameterSpec;private KeyFactory keyFactory;static {if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {System.out.println("security provider BC not found");// 注意:注册BouncyCastle是通过下面的语句实现的。注册只需要在启动时进行一次,后续就可以使用BouncyCastle提供的所有哈希算法和加密算法。Security.addProvider(new BouncyCastleProvider());}}public BC_SM2(){try {provider = new BouncyCastleProvider();parameters = GMNamedCurves.getByName("sm2p256v1");ecParameterSpec = new ECParameterSpec(parameters.getCurve(),parameters.getG(), parameters.getN(), parameters.getH());keyFactory = KeyFactory.getInstance("EC", provider);} catch (Exception e) {e.printStackTrace();}}/*** 生成密钥对*/public KeyPair generateSm2KeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");// 获取一个椭圆曲线类型的密钥对生成器final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);SecureRandom random = new SecureRandom();// 使用SM2的算法区域初始化密钥生成器kpg.initialize(sm2Spec, random);// 获取密钥对KeyPair keyPair = kpg.generateKeyPair();return keyPair;}/*** 加密** @param input  待加密文本* @param pubKey 公钥*/public String encode(String input, String pubKey)throws NoSuchPaddingException, NoSuchAlgorithmException,BadPaddingException, IllegalBlockSizeException,InvalidKeySpecException, InvalidKeyException {// 获取SM2相关参数X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1");// 椭圆曲线参数规格ECParameterSpec ecParameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH());// 将公钥HEX字符串转换为椭圆曲线对应的点ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey));// 获取椭圆曲线KEY生成器KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec));// 获取SM2加密器Cipher cipher = Cipher.getInstance("SM2", provider);// 初始化为加密模式cipher.init(Cipher.ENCRYPT_MODE, key);// 加密并编码为base64格式return cfca.sadk.bouncycastle.util.encoders.Base64.toBase64String(cipher.doFinal(input.getBytes()));}/*** 解密** @param input  待解密文本* @param prvKey 私钥*/public String decoder(String input, String prvKey) throws NoSuchPaddingException, NoSuchAlgorithmException,InvalidKeySpecException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {// 获取SM2加密器Cipher cipher = Cipher.getInstance("SM2", provider);// 将私钥HEX字符串转换为X值BigInteger bigInteger = new BigInteger(prvKey, 16);BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger,ecParameterSpec));// 初始化为解密模式cipher.init(Cipher.DECRYPT_MODE, privateKey);// 解密//return cipher.doFinal(Base64.decode(input));return new String(cipher.doFinal(org.bouncycastle.util.encoders.Base64.decode(input)));}/*** 签名** @param plainText 待签名文本* @param prvKey    私钥*/public String sign(String plainText, String prvKey) throws NoSuchAlgorithmException, InvalidKeySpecException,InvalidKeyException, SignatureException {// 创建签名对象Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);// 将私钥HEX字符串转换为X值BigInteger bigInteger = new BigInteger(prvKey, 16);BCECPrivateKey privateKey = (BCECPrivateKey) keyFactory.generatePrivate(new ECPrivateKeySpec(bigInteger,ecParameterSpec));// 初始化为签名状态signature.initSign(privateKey);// 传入签名字节signature.update(plainText.getBytes());// 签名return org.bouncycastle.util.encoders.Hex.toHexString(signature.sign());}/*** 根据 map 的 value、key排序拼接,生成待签名字符串,并签名*/public String sign(Map<String, Object> map, String prvKey) throws NoSuchAlgorithmException, InvalidKeySpecException,InvalidKeyException, SignatureException {return sign(getSortStr(map), prvKey);}public String getSortStr(Map<String, Object> sortedParams) {StringBuilder signSrc = new StringBuilder();List<String> keys = new ArrayList<String>(sortedParams.keySet());Collections.sort(keys);for (String key : keys) {Object value = sortedParams.get(key);if (key != null && !"".equals(key) && value != null && !"sign".equals(key)) {signSrc.append(key).append("=").append(value);}}return signSrc.toString();}/*** 验证签名*/public boolean verify(String plainText, String signatureValue, String pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException,InvalidKeyException, SignatureException {Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), provider);ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(pubKey));BCECPublicKey key = (BCECPublicKey) keyFactory.generatePublic(new ECPublicKeySpec(ecPoint, ecParameterSpec));signature.initVerify(key);signature.update(plainText.getBytes());return signature.verify(Hex.decode(signatureValue));}public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {System.out.println("----------------------jdk版本信息----------------------------------------------------------");System.out.println(System.clearProperty("java.version"));System.out.println(System.clearProperty("os.name"));System.out.println("----------------------测试 开始----------------------------------------------------------");String str = "test encode";BC_SM2 sm2 = new BC_SM2();KeyPair keyPair = sm2.generateSm2KeyPair();BCECPrivateKey privateKey = (BCECPrivateKey) keyPair.getPrivate();BCECPublicKey publicKey = (BCECPublicKey) keyPair.getPublic();// 拿到密钥String pubKey =  "04" + (publicKey.getQ().getXCoord() + "" + publicKey.getQ().getYCoord());String prvKey = privateKey.getD().toString(16);System.out.println("Private Key: " + prvKey);System.out.println("Public Key: " + pubKey);// 加解密测试try {System.out.println("加密前:" + str);String encode = sm2.encode(str, pubKey);System.out.println("加密后:" + encode);String decoder = new String(sm2.decoder(encode, prvKey));System.out.println("解密后:" + decoder);} catch (Exception e) {e.printStackTrace();System.out.println("加解密错误");}// 签名和验签测试try {System.out.println("签名源数据:" + str);String signStr = sm2.sign(str, prvKey);System.out.println("签名后数据:" + signStr);boolean verify = sm2.verify(str, signStr, pubKey);System.out.println("签名验证结果:" + verify);} catch (Exception e) {System.out.println("签名和验签错误");}}}

4、请求参数dto

调哪个接口就创建对应的请求参数的dto,我这个是三要素核验的dto。因为在工具类中用反射动态设置参数,属性名称必须与请求参数名称保持一致。

package org.jeecg.modules.cfca.dto;import lombok.Data;/*** 运营商风险识别dto** 验证用户手机号码与姓名、证件号码与运营商实名预留信息是否一致,不一致的情况下返回具体要素不符** @author:gan* @date: 2023-09-21 10:17*/
@Data
public class CFCAJINXINCode209ReqDto {//姓名private String username;//手机号码private String mobile;//身份证号private String idcard;//证件类型,只支持身份证,传 01private String idtype;
}

5、调用接口工具类(关键是这个)

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jeecgmon.util.VerifyUtil;
import org.jeecg.modules.cfca.dto.CFCAJINXINCode209ReqDto;
import org.jeecg.modules.cfca.dto.CFCAJINXINConfig;
import org.jeecg.modules.cfca.enums.CFCAJINXINCodeEnum;import javax.ssl.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.HttpURLConnection;
import java.URL;
import java.URLConnection;
import java.security.KeyStore;
import java.util.*;/*** CFCA金信反欺诈服务工具类** @author:gan* @date: 2023-09-21 09:53*/
public class CFCAJINXINUtils {private static Logger log = LogManager.getLogger();private static BC_SM2 bcSm2 = new BC_SM2();  //加密解密需要用到private final static String GET_STR = "get";  //get方法字符private final static String GET_CLASS_STR = "getClass";  //getClass方法字符private static StringBuilder host; // 请求地址前缀部分,格式:http://192.168.1.223:8091private static String jinXinSM2PublicKey; // 金信平台公钥private static String merchSM2PrivateKey; // 商户私钥private static String keystorePath; // 客户通信证书路径(证书需要申请)private static String keystorePassword; // 客户通信证书密码private static String truststorePath; // 金信通信证书信任库路径(证书由金信提供)private static String truststorePassword; // 金信通信证书信任库密码private static String fullUrl; // 当前接口业务完整地址/*** 初始化CFCA金信反欺诈服务需要的配置* @param config*/private static void init(CFCAJINXINConfig config) {VerifyUtil.checkParam(config, "CFCA金信反欺诈服务请求参数为空!");List<String> noEmptyFieldList = Arrays.asList("host", "merchNo", "jinXinSM2PublicKey", "merchSM2PrivateKey", "keystorePath", "keystorePassword", "truststorePath", "truststorePassword", "transcode", "version");VerifyUtil.checkBeanByNonEmptyFiledList(config, noEmptyFieldList, "CFCA金信反欺诈服务请求参数");host = new StringBuilder();host.append(config.getHost());int length = host.length();if (host.indexOf("/", length - 1) > 0) {  //最后一个字符为“/”,则去掉host = host.delete(length - 1, length);}fullUrl = host.append(CFCAJINXINCodeEnum.getUrl(config.getTranscode())).toString();jinXinSM2PublicKey = config.getJinXinSM2PublicKey();merchSM2PrivateKey = config.getMerchSM2PrivateKey();keystorePath = config.getKeystorePath();keystorePassword = config.getKeystorePassword();truststorePath = config.getTruststorePath();truststorePassword = config.getTruststorePassword();}/*** 发送CFCA金信反欺诈服务请求* @param config* @param params*/public static JSONObject sendRequest(CFCAJINXINConfig config, Object params) throws Exception {init(config);// 请求最外层参数,所有接口公共参数Map<String, Object> reqMap = new HashMap<>();reqMap.put("transcode", config.getTranscode());  // 接口业务编码,具体业务请参考接口文档reqMap.put("merchno", config.getMerchNo());  // 商户号reqMap.put("ordersn", UUID.randomUUID().toString().replaceAll("-", ""));  //商户流水号reqMap.put("dsorderid", UUID.randomUUID().toString().replaceAll("-", ""));  //商户订单号reqMap.put("version", config.getVersion());  //api版本号reqMap.put("timestamp", System.currentTimeMillis());  // 时间戳// 请求内层 data 参数,所有接口公共参数Map<String, Object> data = new HashMap<>();data.put("sCustomerName", config.getSCustomerName()); // 二级商户名称,例如:xxx商户名-yyy产品名称-zzz使用方法data.put("sceneCode", config.getSceneCode());   //真实场景 见接口文档枚举data.put("scUsePurpose", config.getScUsePurpose()); // 预留字段data.put("protocolVerNm", config.getProtocolVerNm()); // 预留字段data.put("serialNm", config.getSerialNm()); // 预留字段data.put("reqIp", config.getReqIp());  // 请求ipdata.put("remark", config.getRemark()); // 备注//这里设置的是不同接口独有的参数,也是data中的参数setInterfaceSpecificParams(params, data);return request(reqMap, data);}/*** 设置不同接口特定参数* @param params  传入请求参数* @param data   调用接口请求参数*/private static void setInterfaceSpecificParams(Object params, Map<String, Object> data) {Method[] methods = params.getClass().getMethods();Method currentMethod = null;  //当前方法String currentMethodName = null;  //当前方法名称StringBuilder currentPropertyName = new StringBuilder();  //当前属性名称for (int i = 0; i < methods.length; i++) {currentMethod = methods[i];currentMethodName = currentMethod.getName();if (GET_CLASS_STR.equals(currentMethodName)) {  //getClass方法不为请求参数continue;}if (currentMethodName.startsWith(GET_STR) && currentMethodName.length() > 3) {currentPropertyName.append(currentMethodName.substring(3));  //截取currentMethodName字符串的中索引第四到最后的字符,也就是去掉“get”字符currentPropertyName.replace(0, 1, currentPropertyName.substring(0, 1).toLowerCase());  //将首字符的大写改为小写try {data.put(currentPropertyName.toString(), currentMethod.invoke(params));currentPropertyName.delete(0, currentPropertyName.length());  //设置完参数就清除StringBuilder中的数据,给下一个参数设置时用} catch (IllegalAccessException e) {e.printStackTrace();throw new IllegalArgumentException(e);} catch (InvocationTargetException e) {e.printStackTrace();throw new RuntimeException(e.getMessage());}}}}/*** 处理请求参数并发送请求* @param reqMap  外层请求参数* @param data   data请求参数* @throws Exception*/private static JSONObject request(Map<String, Object> reqMap, Map<String, Object> data) throws Exception {//reqMap的值转化为字符String reqMapToStr = JSONObject.toJSONString(reqMap, SerializerFeature.WriteNonStringValueAsString);//data的值转化为字符String dataToStr = JSONObject.toJSONString(data, SerializerFeature.WriteNonStringValueAsString);//获取【金信平台】SM2 公钥,对 data 使用 SM2 进行加密reqMap.put("data", bcSm2.encode(dataToStr, jinXinSM2PublicKey));// 对请求报文生成签名信息reqMap.put("sign", getSign(reqMap, merchSM2PrivateKey));log.info("请求地址:{}" , fullUrl);log.info("请求参数:{}" , dataToStr);log.info("完整的请求参数:{}" , reqMapToStr);String result = post(fullUrl, JSONObject.toJSONBytes(reqMap), keystorePath, keystorePassword, truststorePath, truststorePassword); // 发起请求return response(result);}/*** 处理响应* @param response  响应结果字符* @throws Exception*/private static JSONObject response(String response) throws Exception {log.info("响应参数:{}" , response);JSONObject respJson = JSONObject.parseObject(response);String sortStr = bcSm2.getSortStr(respJson);Object respSign = respJson.get("sign");Object respData = respJson.get("data");JSONObject decryptDataJsonObj = null;  //转化为JSONObject的data数据if (VerifyUtil.isNotEmpty(respSign) && bcSm2.verify(sortStr, String.valueOf(respSign), jinXinSM2PublicKey)) {if (VerifyUtil.isNotEmpty(respData)) {String decryptData = bcSm2.decoder(respData.toString(), merchSM2PrivateKey); // 解密decryptDataJsonObj = JSONObject.parseObject(decryptData);respJson.put("data", decryptDataJsonObj); // 后续可根据实际情况替换Object.classlog.info("解密响应参数:{}" , JSONObject.toJSONString(respJson, SerializerFeature.WriteNonStringValueAsString));log.info("响应参数中的data:{}" , decryptData);} else {log.info("【data】接口没有返回或为空");}} else {log.info("验签失败");}return respJson;}/*** 判断是否请求成功* @param respJson*/public static boolean isSuccess(JSONObject respJson) {return "001000000".equals(respJson.getString("platformCode"));  //001000000代表接口调用成功,因为只有这里用到,每个接口的返回参数又不一样,不再用枚举类}/*** 接口调用失败时抛出错误信息* @param respJson*/public static void getErrorInfo(JSONObject respJson) {if (!isSuccess(respJson)) {throw new RuntimeException(respJson.getString("platformDesc"));}}/*** 发送HTTP请求分类判断* @param url  请求路径* @param request  转化为byte数组后的请求参数* @param keystorePath  通信证书路径* @param keystorePassword  通信证书密码* @param truststorePath  金信通信证书信任库路径* @param truststorePassword  金信通信证书信任库密码* @return* @throws Exception*/private static String post(String url, byte[] request, String keystorePath, String keystorePassword, String truststorePath, String truststorePassword) throws Exception {if (url.startsWith("https://")) {return doPostHttps(url, request, keystorePath, keystorePassword, truststorePath, truststorePassword);} else {return doPostHttp(url, request);}}/*** 发送HTTP请求,主要就是这个* @param reqUrl  请求路径* @param reqData  转化为byte数组后的请求参数* @param keystorePath  通信证书路径* @param keystorePassword  通信证书密码* @param truststorePath  金信通信证书信任库路径* @param truststorePassword  金信通信证书信任库密码* @return* @throws Exception*/private static String doPostHttps(String reqUrl, byte[] reqData, String keystorePath, String keystorePassword, String truststorePath, String truststorePassword) throws Exception {OutputStream outputStream = null;BufferedReader bufferedReader = null;HttpsURLConnection httpsURLConnection = null;try {// 初始化密钥库KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");KeyStore keyStore = getKeyStore(keystorePath, keystorePassword);keyManagerFactory.init(keyStore, keystorePassword.toCharArray());// 初始化信任库TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");KeyStore trustKeyStore = getKeyStore(truststorePath, truststorePassword);trustManagerFactory.init(trustKeyStore);// 初始化SSL上下文SSLContext ctx = SSLContext.getInstance("SSL");ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);SSLSocketFactory sf = ctx.getSocketFactory();URL url = new URL(reqUrl);httpsURLConnection = (HttpsURLConnection) url.openConnection();httpsURLConnection.setSSLSocketFactory(sf);//设置链接超时时间httpsURLConnection.setConnectTimeout(60000);//设置读取超时时间httpsURLConnection.setReadTimeout(60000);// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在// http正文内,因此需要设为true, 默认情况下是false;httpsURLConnection.setDoOutput(true);// 设置是否从httpUrlConnection读入,默认情况下是true;httpsURLConnection.setDoInput(true);// Post 请求不能使用缓存httpsURLConnection.setUseCaches(false);// 设定传送的内容类型是json UTF-8httpsURLConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");// 设定请求的方法为"POST",默认是GEThttpsURLConnection.setRequestMethod("POST");// 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,// 所以在开发中不调用上述的connect()也可以)。outputStream = httpsURLConnection.getOutputStream();// 向对象输出流写出数据,这些数据将存到内存缓冲区中outputStream.write(reqData);// 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)outputStream.flush();// 调用HttpURLConnection连接对象的getInputStream()函数,// 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。// httpsURLConnection.getInputStream() 注意,实际发送请求的代码段就在这里bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream(), "UTF-8"));StringBuilder jsonString = new StringBuilder();String line = "";while ((line = bufferedReader.readLine()) != null) {jsonString.append(line);}return jsonString.toString();} catch (Exception e) {e.printStackTrace();log.error(e.getMessage(), e);return null;} finally {if (outputStream != null) {outputStream.close();}if (bufferedReader != null) {bufferedReader.close();}if(httpsURLConnection != null) {httpsURLConnection.disconnect();}}}// 备用方法private static String doPostHttp(String urlStr, byte[] request) throws Exception {DataOutputStream objOutputStrm = null;OutputStream outStrm = null;InputStream inStrm = null;try {URL url = new URL(urlStr);// 此处的urlConnection对象实际上是根据URL的 // 请求协议(此处是http)生成的URLConnection类 // 的子类HttpURLConnection,故此处最好将其转化 // 为HttpURLConnection类型的对象,以便用到 // HttpURLConnection更多的API.如下:URLConnection rulConnection = url.openConnection();HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;//设置链接超时时间httpUrlConnection.setConnectTimeout(6000000);//设置读取超时时间httpUrlConnection.setReadTimeout(6000000);// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在// http正文内,因此需要设为true, 默认情况下是false;httpUrlConnection.setDoOutput(true);// 设置是否从httpUrlConnection读入,默认情况下是true;httpUrlConnection.setDoInput(true);// Post 请求不能使用缓存httpUrlConnection.setUseCaches(false);// 设定传送的内容类型是可序列化的java对象// (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)httpUrlConnection.setRequestProperty("Content-type", "application/json");// 设定请求的方法为"POST",默认是GEThttpUrlConnection.setRequestMethod("POST");// 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,// 所以在开发中不调用上述的connect()也可以)。outStrm = httpUrlConnection.getOutputStream();// 现在通过输出流对象构建对象输出流对象,以实现输出可序列化的对象。objOutputStrm = new DataOutputStream(outStrm);// 向对象输出流写出数据,这些数据将存到内存缓冲区中objOutputStrm.write(request);// 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)objOutputStrm.flush();// 调用HttpURLConnection连接对象的getInputStream()函数,// 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。inStrm = httpUrlConnection.getInputStream(); // <===注意,实际发送请求的代码段就在这里BufferedReader read = new BufferedReader(new InputStreamReader(inStrm, "UTF-8"));StringBuffer jsonString = new StringBuffer();String line = "";while ((line = read.readLine()) != null) {jsonString.append(line);}String json = jsonString.toString();return json;} catch (Exception e) {e.printStackTrace();log.error(e.getMessage(), e);return null;} finally {if (objOutputStrm != null)objOutputStrm.close();if (outStrm != null) {outStrm.close();}if (inStrm != null)inStrm.close();}}/*** 获得KeyStore*/private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception {FileInputStream is = new FileInputStream(keyStorePath);KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());ks.load(is, password.toCharArray());is.close();return ks;}/*** 使用私钥加签* @param reqParams 待加签的map集合,会对此map集合的key做排序,遍历拼接* @param merchSM2PrivateKey 商户私钥*/private static String getSign(Map<String, Object> reqParams, String merchSM2PrivateKey) throws Exception {StringBuilder signSrc = new StringBuilder();List<String> keys = new ArrayList<>(reqParams.keySet());Collections.sort(keys);for (String key : keys) {Object value = reqParams.get(key);if (key != null && !"".equals(key) && value != null && !"sign".equals(key)) {signSrc.append(key).append("=").append(value);}}String sign = bcSm2.sign(signSrc.toString(), merchSM2PrivateKey);return sign;}public static void main(String[] args) {CFCAJINXINConfig config = new CFCAJINXINConfig();config.setHost("请求地址前缀部分");config.setKeystorePath("客户通信证书路径(证书需要申请)");config.setKeystorePassword("客户通信证书密码");config.setTruststorePath("金信通信证书信任库路径(证书由金信提供)");config.setTruststorePassword("金信通信证书信任库密码");config.setMerchSM2PrivateKey("商户私钥");config.setJinXinSM2PublicKey("金信平台公钥");config.setMerchNo("商户号");config.setVersion("api版本号");config.setTranscode(CFCAJINXINCodeEnum.CODE_209.getCode());CFCAJINXINCode209ReqDto params = new CFCAJINXINCode209ReqDto();params.setUsername("你的名字");params.setMobile("你的手机号码");params.setIdcard("你的身份证号码");params.setIdtype("01");try {sendRequest(config, params);} catch (Exception e) {e.printStackTrace();}}
}

其中VerifyUtil是之前写的一个判空的工具类,感兴趣可以前往查看,也可以用自己的方式判空。

更多推荐

调用CFCA金信反欺诈服务相关接口,很详细

本文发布于:2024-02-25 14:54:13,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1699345.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:接口   详细   CFCA   金信反

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!