调用招商银行接口(手把手教程,附调用接口工具类)

一、下载示例代码

首先到招商银行的文档中心下载示例代码:
https://openbiz.cmbchina.com/developer/UI/Business/CloudDirectConnect/Public/DocumentCenter/DocDetail.aspx?bizkey=DCCT20201215143758097&fabizkey=1&treeID=100034594

在这里插入图片描述
在这里插入图片描述
这个下载下来是一个压缩包,打开压缩包,里面有一个,java文件这个文件就是示例代码了。

二、申请测试环境、或者生产环境加入到示例代码

在这里插入图片描述
这里面的公钥、私钥等环境参数是不能直接用的(红框框住的那些就是),需要企业事先跟招商银行申请测试环境,申请成功后将申请环境中的参数与示例代码的参数一一替换。

三、调用招商银行接口参数拼装

在这里插入图片描述
这个data参数里面的就是招商银行的接口参数,这样看有点费劲,格式一下json
在这里插入图片描述

注意,reqid是有格式要求的

在这里插入图片描述

这个接口的funcode是“NTDMAADD”,在文档中找到对应的接口详情
在这里插入图片描述
会有接口的使用说明,请求和响应的参数介绍,往下拉还会有调用示例和响应示例
在这里插入图片描述

四、调用接口测试

在调用接口前记得找管理网银的同事添加一下本机ip的白名单,否则会出现白名单错误,出现这个错误说明接口传参没问题了,就差白名单了。
在这里插入图片描述
添加了白名单之后,如果响应体中参数“resultcode”的值为“SUC0000”,说明接口调用成功,保存下需要的返回信息即可。

当然也可以用postman测试
在这里插入图片描述
但是“DATA”参数还是需要用示例代码中的方法加密,个人觉得意义不大。

五、最后,贴上我自己改造过的工具类

这个不是调用接口必要的,只是觉得经过我改动后调用接口方便一点

1、这是获取银行环境参数的工具类

import lombok.Data;

/**
 * 银行配置dto
 *
 * @author:user
 * @date: 2022-03-25 10:51
 */
@Data
public class BankConfigDto {
    
    

    //公钥
    private String pubkeyStr;

    //私钥
    private String prikeyStr;

    //AESKEY
    private String AESKEY;

    //用户id
    private String UID;

    //访问地址
    private String URL;

    //=============以下参数并不是所有的银行接口都需要,所以是非必填的

    //付方账号
    private String payacc;

    //模式编号
    private String busmod;

    //网银审批标志    可选;Y 直连经办,客户端审批
    private String grtflg;

    //分行号
    private String bbknbr;

}

2、新增记账子单元 NTDMAADD-参数dto

这只是这个接口的dto,后面要调用其他接口再增加对应的参数dto。


import lombok.Data;

/**
 * 新增记账子单元 NTDMAADD-参数dto
 *
 * @author:user
 * @date: 2022-03-25 18:14
 */
@Data
public class NTDMAADDParam {
    
    

    //账号
    private String accnbr;

    //记账子单元编号
    // 不超过 10 位
    private String dmanbr;

    //记账子单元名称
    private String dmanam;

    //=================以下字段为非必填

    //额度控制标志
    //空:默认 Y,Y:允许透支 N:不允许透支
    private String ovrctl;

    //退票处理方式
    //空:默认 N,Y: 退回原记账子单元 N:退回结算户
    private String bcktyp;

    //余额非零时是否可关闭
    //Y:可关闭, N:不可关闭 空:默认 Y
    private String clstyp;
}

3、这是调用银行接口的工具类,一般就用“commonRequestMethod”方法


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jeecg.common.util.ReflectHelper;
import org.jeecg.modules.cmb.dto.*;
import org.jeecg.modules.cmb.entity.CMBBase64;
import org.jeecg.modules.cmb.entity.CMBRequest;
import org.jeecg.modules.cmb.entity.CMBRestBuilder;
import org.jeecg.modules.cmb.enums.CMBTransAction;

/**
 * 调用银行接口工具类
 *
 * @author:user
 * @date: 2022-03-24 11:50
 */
public class CMBUtils {
    
    

    static String pubkeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Ec7viMyQC5SShRz1jP0IQRLLVGDQ4f1rgZwtxT4ZOgnWxUoAHquj2yIrgFjNpWVnt/1dJGtXWkpp2UN3jMI5ubjVQkL0OFD+8r0IFXYAARsCLAwVLF0LE487KvVRaQC7A7rPlFfBtE/v++KajzMuDauNlIASYobcFKYdZ89vIfE/xMg/44QJqQ2XBkoMnJ7ul0kMdh4YWOQnO0qqvXD2eK3KPaXMRtxieGsVBgsvtETprw98bTl9tPUBUrneyirrccS8/Z6raV6nioyx2RzrMld8YnjlnV2YTJpNAlG+y/wLoKY55Rkjcvg9wSe8qbI/VtYVQfQz8gfeUzFQTKKCwIDAQAB";
    static String prikeyStr = "MIIEowIBAAKCAQEAwN7xTseqQs1pNA/+gTgXRqcxCYfkxDTsckfqf3O2ndsvJS5T" + "8Fb0oHTyjy0HjrKLASWWUKfhQGXPHbo1FQd+0TyHxSza55+HtXquUq7QsAITHCu3"
            + "U7aslvC7xe6/2E7nhu1TausF1nSyB1o4xVEjZyjrdQpTID0JvG8BtA5Yem9YDBCM" + "ZHBxvarQHVqdBsqo2G3M09qeUDbY3DuBgdiVAw0ApIM8mKGj4jsWmRSnypuxl40B"
            + "jWAr6Xgk44MpSGHndhiFXGvfMRRYEd8Z30w32QlB+Gjk8rQwXnvxG8YCBPYqXVkq" + "wld81bHFFz5zHQ0qekPhD8RrFimPn+RaD9VNfwIDAQABAoIBAQCxUUZQw0hJOkgq"
            + "UToO2t6rWjK/LGyp5m4rcuqoWl3eKxDhAgKxx4AHtPz7nM6B5qvdVg0oAfAZIICW" + "OAdtWgLBowC/yklHAWfm9O8nnQjHiGDBWX+mOx/ZdWFsy98cow5BAhfbCE/Jpme2"
            + "UsA2yC3gPcKbS+64iOVWlEfgnf/CLte/dE0eAMfsp5wXpwv3ygA4wtyd2X2P+y6s" + "+WYBKSvNMS08W4dsxwU9Q3AG3hS0Uab09qIPNS8tEMZ2L1tl0/VvkrAYjayM1CcK"
            + "CrSnwtH6eJVi4WQxL1K3QxyxDKucsOlqSHg++4VMpGZNpvstn3IsY3PyCgfsODvH" + "aoygvDBhAoGBAPxxdcI9stK9bIGSms0FqbVXYj8WmAjE/M9B7ECToWRQg65Gf8MY"
            + "PyUSkY2mbDHwf+yPsUb5Oli+a2GW8BwmJWeXEIy0lQxa1TS2b7CN6XJtZVnjEgiZ" + "d7bXy/j69r/C4CMlfbrRWnUGftKr/U7ynaGs10/bISeW12E7WdLV5+kDAoGBAMOW"
            + "nEzAFMPFzG9p/GtYpWU5jMNSiakzfm6n9Nwr7dFGnLhVGtO6act1bm/WB26NAgIE" + "ArhcitoKrI346nfkoZLXBpzzyJgFx4r31d1RN9Vsrt6AEywlwnLwHk2HXtCwmqre"
            + "hZ4I741S2rHlaT8ifNwLyjW2sbw9QnpC3RL7R3rVAoGAOI/Dbs4cLxO6KB4NCTrn" + "l3YI0VHiprRcYKPIp39sfel8V6P8JF5eZ5QNgMt1GotkXkCj298jr5aawLbs/aGe"
            + "Z+N1FdGwQ6BmfPUTeV+SmszgFI/IDp00MYeQcCzq9HRZfAZ+cUlPF0FpURKwIuxB" + "XWQ4qe/TMeeeQm7l5VOALrkCgYAljLa5LW9PHpxfD3P8j+pBAsl5flEbgN1XFTu3"
            + "QV/I+8t+wCgEWheRjhwDsI2AteWayXZUOsAVmFMEdrNdDTHP5SRJ4auzM/jZPzd5" + "4+vaN6Fi6ifEJAOu2VaX/9M+MYmgIFR6wLBs62k9GhQYoOBjxoetxENfJkuq+UdE"
            + "K6XPeQKBgFvf+SUrg7hFpRRyCq+DehdMQk1TJnEPTNLOalfrA/319KA8LGa0Q+ay" + "5c2mDc9F//yAJEAT1WTEqHnvKBQvjofFAGRntoCT8anAnskSytwwpltKqDcpoKx/"
            + "hVK+eVL47wuFroCBLGj0Zm3I7S+saGGmVllEky4jceE7IMTN7i6W";

    private static PrivateKey privateKey;
    private static PublicKey publicKey;
    static String UID = "N002985759";

    static String URL = "http://99.12.250.6:9080/cdcserver/api/v2";
    static String AESKEY = "YSqdwE8vAQ1BcfYCpESUsnVzOOMA2ZSd";

    public static void main(String[] args) throws Exception {
    
    

        // TODO Auto-generated method stub
        try {
    
    
            BankConfigDto bankConfigDto = new BankConfigDto();
            bankConfigDto.setPubkeyStr(pubkeyStr);
            bankConfigDto.setPrikeyStr(prikeyStr);
            bankConfigDto.setAESKEY(AESKEY);
            bankConfigDto.setUID(UID);
            bankConfigDto.setURL(URL);

//            //============================新增记账子单元 接口测试================================
            NTDMAADDParam ntdmaaddParam = new NTDMAADDParam();
            ntdmaaddParam.setAccnbr("769900019310827");
            ntdmaaddParam.setDmanbr("10101");
            ntdmaaddParam.setDmanam("测试嘿嘿");

            List<NTDMAADDParam> ntdmaaddx = new ArrayList<>();
            ntdmaaddx.add(ntdmaaddParam);

            //拼接银行请求body里面的参数
            Map<String, Object> body = new HashMap<>();
            body.put("ntdmaaddx", ntdmaaddx);

            //接口funcode参数
            String funcode = "NTDMAADD";

            List<String> requestEmptyFiledList = Arrays.asList("ovrctl", "bcktyp", "clstyp");
            JSONObject jsonObject4 = commonRequestMethod(bankConfigDto, funcode, body, requestEmptyFiledList, "参数【body】");
            System.out.println("jsonObject4 " + jsonObject4);

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 银行接口公共请求方法
     * @param bankConfigDto  银行相关配置
     * @param funcode  银行接口的funcode
     * @param body  银行接口的body属性值,目前只有Object和Map<String, Object>两种类型
     * @param emptyFiledList  body属性中可以为空的属性值,没有传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     * @return
     * @throws Exception
     */
    public static <T> JSONObject commonRequestMethod(BankConfigDto bankConfigDto, String funcode, T body, List<String> emptyFiledList, String message) throws Exception {
    
    

        //校验body参数
        checkBody(body, emptyFiledList, message);

        return DoProcessMyself(bankConfigDto, funcode, body);
    }

    //15位真实账号+10位记账子单元编号(长度可小于10位)=25位交易识别账号,可用于对外收款,并实现自动记账功能。(交易识别不支持外币)。
    public static void modifySubAcc( SubAccUpdateDto updateDto) throws Exception {
    
    
        init();
        JSONObject jsonObject = new JSONObject();
        CMBRequest<SubAccUpdateDto> request =  new CMBRestBuilder().of(updateDto).build(CMBTransAction.AccMgt_UPDATE, UID);
        jsonObject.put("request", request);
        DoProcess(JSONObject.parseObject(jsonObject.toJSONString()), privateKey);
    }

    /**
     * 获取主账号15位
     * @param accno25
     * @return
     */
    public static String getMainAcc2Accnbr(String accno25){
    
    
        return accno25.substring(0,15);
    }

    public static String getYurrefNoByReceiptName(String receiptName){
    
    

        return receiptName.substring(receiptName.lastIndexOf("_")+1,receiptName.lastIndexOf("."));

    }

    //etytim 的格式是一个把是时分秒连起来的格式 但是数据库要求有时分秒,所以这里做一下转化
    public static String getDateStr(String dateStr){
    
    
        return dateStr.substring(0,4)+"-"+dateStr.substring(4,6)+"-"+dateStr.substring(6,8);
    }


    public  static String getEtytime(String etytim){
    
    
        return etytim.substring(0,2)+":"+etytim.substring(2,4)+":"+etytim.substring(4,6);
    }

    /**
     * 获取账号编号主账号后半部分数据10位后的数据
     * @param accno25
     * @return
     */
    public static String getSubAccUtil2Manbr(String accno25){
    
    
        return accno25.substring(15);
    }

    /**
     * 判断银行调用结果是否成功
     * @param responseJson 银行响应结果json
     * @return
     */
    public static Boolean isSuccess(JSONObject responseJson) {
    
    
        return "SUC0000".equals(getCmbResponseParam(responseJson, "head", "resultcode"));
    }

    /**
     * 获取调用银行接口的响应参数值,不能通过该方法获取signature参数下的值
     * @param responseJson 银行响应结果json
     * @param param1 要获取的结果参数名 (一级或者二级)
     * @param param2 要获取的结果参数名 (三级,没有可以为空,如果是body下的参数则一定要写正确)
     * @return
     */
    public static String getCmbResponseParam(JSONObject responseJson, String param1, String param2) {
    
    

        JSONObject response = (JSONObject) responseJson.get("response");
        checkParam(response, "返回结果中【response】为空或传入param1有误!");

        JSONObject head = (JSONObject) response.get("head");
        checkParam(head, "返回结果中【head】为空或传入param1有误!");

        JSONObject body = (JSONObject) response.get("body");
        checkParam(body, "返回结果中【body】为空或传入param1有误!");

        //head下的参数
        List<String> param2List = Arrays.asList("bizcode", "funcode", "reqid", "resultcode", "resultmsg", "rspid", "userid");

        JSONObject result = null;
        if ("response".equals(param1)) {
    
    
            result = response;
        } else if ("head".equals(param1)) {
    
    
            result = StringUtils.isNotBlank(param2) && param2List.contains(param2) ? (JSONObject) head.get(param2) : head;
        } else if ("body".equals(param1)) {
    
    
            result = StringUtils.isNotBlank(param2) && param2List.contains(param2) ? (JSONObject) body.get(param2) : body;
        } else {
    
    
            throw new RuntimeException("传入param2时param1不能为空!");
        }

        return result.toString();
    }

    /**
     * 获取请求参数json
     * @param bankConfigDto  银行接口参数,这里用来校验是否为空,这个方法只使用到了UID
     * @param funcode 接口code
     * @param body  请求json中的参数body,泛型类型
     * @return
     */
    public static <T> JSONObject getRequestJson(BankConfigDto bankConfigDto, String funcode, T body) {
    
    
        checkBankConfig(bankConfigDto);

        checkParam(funcode, "funcode不能为空!");

        String data = "{\"request\":{\"body\":" + JSON.toJSONString(body) + ",\"head\":{\"funcode\":\"" + funcode + "\",\"reqid\":\"" + getTimestamp() + "\",\"userid\":\"" + bankConfigDto.getUID() + "\"}}}";

        return JSONObject.parseObject(data);
    }

    /**
     * 校验银行body参数
     * @param body  body参数
     * @param emptyFiledList  可以为空的属性集合,为JavaBean的属性,没有可以为空的传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkBody(T body, List<String> emptyFiledList, String message) {
    
    
        if (body instanceof Map) {
    
    
            Map<String, Object> map = (Map<String, Object>) body;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
    
    
                String key = entry.getKey();
                Object value = entry.getValue();

                List list = null;
                if (value instanceof List) {
    
    
                    list = (List) value;
                } else {
    
    
                    throw new RuntimeException("map集合中key为【" + key + "】的value值非法!应为List!");
                }

                checkList(list, emptyFiledList, message);
            }
        } else {
    
    
            checkBean(body, emptyFiledList, message);
        }
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param emptyFiledList  可以为空的属性集合,为JavaBean的属性,没有可以为空的传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkList(List<T> list, List<String> emptyFiledList, String message) {
    
    
        checkParam(list, (StringUtils.isBlank(message) ? "集合" : message) + "为空!");

        for (int i = 0; i < list.size(); i++) {
    
    
            T t = list.get(i);

            checkBean(t, emptyFiledList, (StringUtils.isBlank(message) ? "集合的" : message + "的") + "第【" + i + "】条记录");
        }
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     */
    public static <T> void checkList(List<T> list) {
    
    
        checkList(list, null, null);
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param emptyFiledList  可以为空的属性集合,没有传null
     */
    public static <T> void checkList(List<T> list, List<String> emptyFiledList) {
    
    
        checkList(list, emptyFiledList, null);
    }

    /**
     * 校验参数集合
     * @param list  参数集合
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static <T> void checkList(List<T> list, String message) {
    
    
        checkList(list, null, message);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param emptyFiledList 可以为空的属性集合,没有传null
     * @param message  在为空的基础上,前面添加的提示信息,没有传null
     */
    public static void checkBean(Object obj, List<String> emptyFiledList, String message) {
    
    
        checkParam(obj, (StringUtils.isBlank(message) ? "对象" : message) + "为空!");

        //传入为空属性集合为null表示当前集合所有属性都不能为空,但为了做校验,emptyFiledList也不能为null
        //在这里做校验可以减少http请求
        emptyFiledList = null != emptyFiledList ? emptyFiledList : new ArrayList<>();

        List<Map> objectInfoList = ReflectHelper.getFiledsInfo(obj);

        for (Map objectInfo : objectInfoList) {
    
    
            String name = (String) objectInfo.get("name"); //对象属性名,属性名都是字符串

            //可以为空的属性不做校验
            if (!emptyFiledList.contains(name)) {
    
    

                //校验对象属性值是否为空
                checkParam(objectInfo.get("value"), (StringUtils.isBlank(message) ? "对象的" : message + "的") + "【" + name + "】属性为空!");
            }
        }
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     */
    public static void checkBean(Object obj) {
    
    
        checkBean(obj, null, null);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param emptyFiledList  可以为空的属性集合
     */
    public static void checkBean(Object obj, List<String> emptyFiledList) {
    
    
        checkBean(obj, emptyFiledList, null);
    }

    /**
     * 校验javabean,传其他类型的对象可能会报错
     * @param obj  校验的javabean
     * @param message  在为空的基础上,前面添加的提示信息
     */
    public static void checkBean(Object obj, String message) {
    
    
        checkBean(obj, null, message);
    }

    /**
     * 校验参数是否为空
     * @param obj 参数
     * @param message 提示信息
     */
    public static void checkParam(Object obj, String message) {
    
    
        if (obj instanceof String) {
    
    
            if (StringUtils.isBlank((String) obj)) throw new RuntimeException(message);
        }
        Optional.ofNullable(obj).orElseThrow(() -> new RuntimeException(message));
    }

    /**
     * 校验银行配置是否为空
     * @param bankConfigDto 银行配置
     */
    public static void checkBankConfig(BankConfigDto bankConfigDto) {
    
    
        checkParam(bankConfigDto, "传入银行配置为空!");

        checkParam(bankConfigDto.getPubkeyStr(), "银行公钥为空!");
        checkParam(bankConfigDto.getPrikeyStr(), "银行私钥为空!");
        checkParam(bankConfigDto.getAESKEY(), "银行AESKEY为空!");
        checkParam(bankConfigDto.getAESKEY(), "银行UID为空!");
        checkParam(bankConfigDto.getURL(), "银行接口路径为空!");

//        Optional.ofNullable(bankConfigDto.getPayacc()).orElseThrow(() -> new RuntimeException("银行付方账号为空!"));
//        Optional.ofNullable(bankConfigDto.getBusmod()).orElseThrow(() -> new RuntimeException("银行模式编号为空!"));
//        Optional.ofNullable(bankConfigDto.getGrtflg()).orElseThrow(() -> new RuntimeException("银行网银审批标志为空!"));
//        Optional.ofNullable(bankConfigDto.getBbknbr()).orElseThrow(() -> new RuntimeException("银行分行号为空!"));
    }

    //这是用来获取时间戳的方法,用来生成唯一的银行接口参数reqid
    public static String getTimestamp() {
    
    
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");//设置日期格式
        String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
        Random random = new Random();
        int rannum = (int) (random.nextDouble() * (99999 - 10000 + 1)) + 10000;
        // System.out.println(rannum);
        String rand = date + rannum;
        System.out.println(rand);

//        SimpleDateFormat df2 = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
//        String date2 = df2.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳
//        System.out.println(date2);

        return rand;
    }








    //============================这里开始是银行的相关类

    public static void init() throws Exception {
    
    
        Security.addProvider(new BouncyCastleProvider());
        publicKey = getPublicKeyFromBytes(pubkeyStr);
        privateKey = getPrivateKeyFromBytes(prikeyStr, "PKCS");
    }

    //这是经过自己加工的DoProcess方法
    public static <T> JSONObject DoProcessMyself(BankConfigDto bankConfigDto, String funcode, T body) throws Exception {
    
    

        //这个方法放在前面是为了校验参数
        JSONObject jObject = getRequestJson(bankConfigDto, funcode, body);

        //要先执行初始化的方法,不然调用银行接口会报错
        init();

        JSONObject object = new JSONObject();
        // 签名
        object.put("sigdat", "__signature_sigdat__");
        object.put("sigtim", GetTime());
        //object.put("sigtim", "20191023165900");
        jObject.put("signature", object);
        String source = serialJsonOrdered(jObject);
        System.out.println(source);
        String data = signRsa2048(source.getBytes(), getPrivateKeyFromBytes(bankConfigDto.getPrikeyStr(), "PKCS"));
        object.put("sigdat", data);
        jObject.put("signature", object);

        // AES加密
        //因为在前面校验过了,这里直接使用
        byte[] AESBytes = bankConfigDto.getAESKEY().getBytes();
        String AesPlainxt = serialJsonOrdered(jObject);
        System.out.println("加密前req:  " + AesPlainxt);
        String req = encryptAES256Str(AesPlainxt, AESBytes);
        System.out.println("加密后req:  " + req);

        //发送请求
        HashMap<String, String> map = new HashMap<>();
        map.put("UID", bankConfigDto.getUID());
        map.put("FUNCODE", funcode);  //银行最近新增的
        map.put("DATA", URLEncoder.encode(req, "utf-8"));
        String res = doPostForm(bankConfigDto.getURL(), map);
        System.out.println("请求结果 res:  " + res);

        //返回结果中包含这个两个字符,可能是没有白名单,也可能用户没有权限,或者其他错误
        if ((res.contains("CDCServer:") && res.contains("ErrMsg:"))) {
    
    
            throw new RuntimeException(res);
        }

        //解密请求
        String resplain = decryptAES256(res, AESBytes, true);
        System.out.println("res decrypt: " + resplain);
        JSONObject object2 =  JSONObject.parseObject(resplain);
        JSONObject object3 = object2.getJSONObject("signature");
        String resSign = object3.getString("sigdat");
        object3.put("sigdat", "__signature_sigdat__");
        object2.put("signature", object3);
        String resSignSource = serialJsonOrdered(object2);
        System.out.println("验签原文: " + resSignSource);
        System.out.println("验签签名值: " +resSign);
        Boolean verify = signRsa2048Verify(resSignSource.getBytes(), CMBBase64.decode(resSign), getPublicKeyFromBytes(bankConfigDto.getPubkeyStr()));
        System.out.println("验签结果: " + verify);
        return JSONObject.parseObject(resSignSource);
    }

    //这是原始的DoProcess
    public static String DoProcess(JSONObject jObject, PrivateKey prikey) throws Exception {
    
    
        JSONObject object = new JSONObject();
        // 签名
        object.put("sigdat", "__signature_sigdat__");
        object.put("sigtim", GetTime());
        //object.put("sigtim", "20191023165900");
        jObject.put("signature", object);
        String source = serialJsonOrdered(jObject);
        System.out.println(source);
        String data = signRsa2048(source.getBytes());
        object.put("sigdat", data);
        jObject.put("signature", object);

        // AES加密
        String AesPlainxt = serialJsonOrdered(jObject);
        System.out.println("加密前req:  " + AesPlainxt);
        String req = encryptAES256Str(AesPlainxt, AESKEY.getBytes());
        System.out.println("加密后req:  " + req);

        //发送请求
        HashMap<String, String> map = new HashMap<>();
        map.put("UID", UID);
        map.put("DATA", URLEncoder.encode(req, "utf-8"));
        String res = doPostForm(URL, map);
        System.out.println("发送请求 res:  " + res);

        //解密请求
        String resplain = decryptAES256(res, AESKEY.getBytes(), true);
        System.out.println("res decrypt: " + resplain);
        JSONObject object2 =  JSONObject.parseObject(resplain);
        JSONObject object3 = object2.getJSONObject("signature");
        String resSign = object3.getString("sigdat");
        object3.put("sigdat", "__signature_sigdat__");
        object2.put("signature", object3);
        String resSignSource = serialJsonOrdered(object2);
        System.out.println("验签原文: " + resSignSource);
        System.out.println("验签签名值: " +resSign);
        Boolean verify = signRsa2048Verify(resSignSource.getBytes(), CMBBase64.decode(resSign), publicKey);
        System.out.println("验签结果: " + verify);
        return resSignSource;
    }

    public static String GetTime() {
    
    
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFormat.format(date);
    }

    public static String GetTime(String datePattern) {
    
    
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
        return dateFormat.format(date);
    }

    public static String encryptAES256Str(String content, byte[] bytePassword) {
    
    
        return CMBBase64.encode(encryptAES256(content, bytePassword));
    }

    public static byte[] encryptAES256(String content, byte[] bytePassword) {
    
    
        try {
    
    
            Cipher cipherInstance = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");
            cipherInstance.init(Cipher.ENCRYPT_MODE, key);
            byte[] byteContent = content.getBytes();
            byte[] cryptograph = cipherInstance.doFinal(byteContent);
            return cryptograph;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        return bytePassword;
    }

    public static String decryptAES256(String content, byte[] bytePassword, boolean logError) {
    
    
        if (content == null || content.length() == 0) {
    
    
            System.out.println("decryptAES256 param content is null or empty");
        }
        byte[] bContent = null;
        try {
    
    
            bContent = CMBBase64.decode(content);
        } catch (Exception e) {
    
    
            System.out.println("decryptAES256  appear error");
            e.printStackTrace();
        }
        try {
    
    
            Cipher cipherInstance = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");
            cipherInstance.init(Cipher.DECRYPT_MODE, key);
            byte[] crypted = cipherInstance.doFinal(bContent);
            return new String(crypted, "utf-8");
        } catch (Exception e) {
    
    
            System.out.println(e.getMessage());
        }
        return content;
    }

    public static String signRsa2048(byte[] baSource) throws Exception {
    
    
        try {
    
    
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(baSource);
            return CMBBase64.encode(signature.sign());
        } catch (Exception e) {
    
    
            System.out.println("signRsa2048 appear error " + e.getMessage());
            throw new Exception("signRsa2048 appear error " + e.getMessage());
        }
    }

    public static String signRsa2048(byte[] baSource, PrivateKey prvKey) throws Exception {
    
    
        try {
    
    
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(prvKey);
            signature.update(baSource);
            return CMBBase64.encode(signature.sign());
        } catch (Exception e) {
    
    
            System.out.println("signRsa2048 appear error" + e.getMessage());
            throw new Exception("signRsa2048 appear error " + e.getMessage());
        }
    }

    public static boolean signRsa2048Verify(byte[] baSource, byte[] baSignature, PublicKey pubKey) throws Exception {
    
    
        try {
    
    
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(pubKey);
            signature.update(baSource);
            return signature.verify(baSignature);
        } catch (Exception e) {
    
    
            System.out.println("验签失败 " + e.getMessage());
            throw new Exception("验签失败 " + e.getMessage());
        }
    }


    public static PrivateKey getPrivateKeyFromBytes(String crtBase64, String type) throws Exception {
    
    
        // type = PKCS,X509
        try {
    
    
            byte[] baKey = CMBBase64.decode(crtBase64);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey prvkey = keyFactory.generatePrivate(type.equals("PKCS") ? new PKCS8EncodedKeySpec(baKey) : new X509EncodedKeySpec(baKey));
            return prvkey;
        } catch (Exception e) {
    
    
            throw new Exception("getPrivateKeyFromBytes error" + e.getMessage());
        }
    }


    public static PublicKey getPublicKeyFromBytes(String crtBase64) throws Exception {
    
    
        try {
    
    
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(CMBBase64.decode(crtBase64));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey key = keyFactory.generatePublic(keySpec);
            return key;
        } catch (Exception e) {
    
    
            throw new Exception("getPublicKeyFromBytes error" + e.getMessage());
        }
    }


    public static String doPostForm(String httpUrl, Map param) {
    
    

        HttpURLConnection connection = null;
        InputStream is = null;
        OutputStream os = null;
        BufferedReader br = null;
        String result = null;
        try {
    
    
            URL url = new URL(httpUrl);
            // trustAllHttpsCertificates();

            SSLContext sslcontext;
            sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
            sslcontext.init(null, new TrustManager[] {
    
     new MyX509TrustManager() }, new java.security.SecureRandom());
            // URL url = new URL("https://xxxx");
            HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
    
    
                public boolean verify(String s, SSLSession sslsession) {
    
    
                    System.out.println("WARNING: Hostname is not matched for cert.");
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());


            connection = (HttpURLConnection) url.openConnection();

            connection.setRequestMethod("POST");

            connection.setConnectTimeout(15000);

            connection.setReadTimeout(60000);
            connection.setInstanceFollowRedirects(true);

            connection.setDoOutput(true);

            connection.setDoInput(true);


            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            os = connection.getOutputStream();

            os.write(createLinkString(param).getBytes());

            if (connection.getResponseCode() != 200) {
    
    
                is = connection.getErrorStream();

                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                StringBuffer sbf = new StringBuffer();
                String temp = null;

                while ((temp = br.readLine()) != null) {
    
    
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            } else {
    
    
                is = connection.getInputStream();

                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                StringBuffer sbf = new StringBuffer();
                String temp = null;

                while ((temp = br.readLine()) != null) {
    
    
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (Exception e) {
    
    
            // TODO: handle exception
            e.printStackTrace();
        } finally {
    
    

            if (null != br) {
    
    
                try {
    
    
                    br.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (null != os) {
    
    
                try {
    
    
                    os.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (null != is) {
    
    
                try {
    
    
                    is.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }

            connection.disconnect();
        }
        return result;
    }

    public static String createLinkString(Map<String, String> params) throws UnsupportedEncodingException {
    
    

        ArrayList<String> keys = new ArrayList<String>(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();
    }


    public static String serialJsonOrdered(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(serialJsonOrdered((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(serialJsonOrdered((JSONObject) obj));
                    } else {
    
    
                        appender.append(obj.toString().replaceAll("\"", "\\\\\""));
                    }
                    isFirstArrEle = false;
                }
                appender.append("]");
            } else {
    
    
                String value = "";
                if (val instanceof String) {
    
    
                    value = "\"" + val.toString().replaceAll("\"", "\\\\\"") + "\"";
                } else {
    
    
                    value = val.toString().replaceAll("\"", "\\\\\"");
                }
                appender.append("\"").append(key).append("\":").append(value);
            }
            isFirstEle = false;
        }
        appender.append("}");
        return appender.toString();
    }
}

class MyX509TrustManager implements X509TrustManager {
    
    

    @Override
    public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
    
    
        // TODO Auto-generated method stub

    }

    @Override
    public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
    
    
        // TODO Auto-generated method stub

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
    
    
        // TODO Auto-generated method stub
        return null;
    }
}

猜你喜欢

转载自blog.csdn.net/studio_1/article/details/125716174