手把手教你调用招商银行接口
一、下载示例代码
首先到招商银行的文档中心下载示例代码:
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;
}
}