接上一篇密钥交换之后获得银行提供的:银行RSA签名公钥RSA.bank.public.key,接受报文验签使用;银行DES加密私钥DES.bank.private.key,加密报文使用。还有之前自己生成的RSA签名私钥RSA.private.key,报文签名使用。下面以签到为例把报文签名、加密以及接受报文后解密、验签写上:
签到http入口:
/**
* @Auther qijw
* @Date 2020/11/16
* @Description:建行银企直连签到
* @Version 1.0
**/
@ApiOperation(value = "建行银企直连签到", notes = "建行银企直连签到")
@GetMapping("/CCBSignIn")
public ResponseModel<JSONObject> signIn(){
try {
log.info("建行银企直连签到");
RspSign signIn = signServiceImpl.signIn();
JSONObject joo = new JSONObject();
joo.put("signIn", signIn);
return new ResponseModel<>(joo);
} catch (Exception e){
log.error("建行银企直连签到异常",e);
return new ResponseModel<>(HttpStatus.INTERNAL_SERVER_ERROR.value(),HttpStatus.INTERNAL_SERVER_ERROR.name());
}
}
签到构造报文、签名、解析
/**
* @Auther qijw
* @Date 2020/11/16
* @Description:签到构造报文、签名、解析
* @Version 1.0
**/
public RspSign sign(String sysCode) {
try {
log.info("建行银企直连签到sysCode:{}", sysCode);
//构造请求报文
String reqXml = getSign(sysCode);
//签名,加密,发送请求
String resultXml = signAndEncryp(reqXml);
//解析响应
return JaxbUtil.converyToJavaBean(resultXml, RspSign.class);
} catch (Exception e) {
log.error("建行银企直连签到异常", e);
return null;
}
}
构造接口签到的请求报文,使用实体类bean传入P1OPME001、电子银行合约编号、时间等必要字段
/**
* 构造接口签到的请求报文
*/
public String getSign(String sysCode) {
try {
log.info("建行银企直连签到报文");
BankProperties BankProperties = new BankProperties();
CCBSign cCBSign = new CCBSign();
Head head = BankProperties.createHead();
//签到P1OPME001,签退P1OPME002
head.setSysTxCode(sysCode);
cCBSign.setHead(head);
String xmlStr = JaxbUtil.ojbectToXmlWithCDATA(BankConstants.elements, CCBSign.class, cCBSign);
return xmlStr;
} catch (Exception e) {
log.error("建行银企直连签到报文出错,错误信息:" + e.getMessage(), e);
return null;
}
}
签名,加密,发送请求,解析报文,验签
/**
* 签名,加密,发送请求,解析报文,验签
*/
public String signAndEncryp(String reqXml) {
try {
//数字签名算法为:MD5withRSA
String signature = RSASignUtil.sign(reqXml, bankKeyConfig.priRSAKey);
//DESede (3DES):DES 对称密钥由银行生成,用于对交易报文和文件内容的加解密
String data = DESedeUtil.encryptWithDESede(reqXml, bankKeyConfig.Des_KEY);
//向建行银企直连发送请求
String reqs = httpUtil.sendRequest(data, signature, CommonBankConstants.REQUEST_CHARSET_UTF8);
if (null != reqs) {
return reqs;
} else {
log.info("建行银企直连签名,加密,发送请求异常");
}
} catch (Exception e) {
log.error("建行银企直连签名,加密,发送请求错误", e);
return null;
}
return null;
}
MD5withRSA签名
/**
* MD5withRSA签名
*
* @param data 待签名数据
* @return 签名
*/
public static String sign(String data , String private_Key) throws Exception {
byte[] keyBytes = getPrivateKey(private_Key).getEncoded();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(key);
signature.update(data.getBytes());
return new String(Base64.encodeBase64(signature.sign()));
}
DESede加密,还原密钥方法网上有。不知道的自己搜一下
public static final String KEY_ALGORITHM = "DESede";
/**
* 加密/解密算法 /工作模式 /填充方式
* Java 6支持PKCS5PADDING填充方式
* Bouncy Castle支持PKCS7Padding填充方式
*/
public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding";
/**
* DESede/ECB/PKCS5Padding加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static String encrypt(String data, String key) throws Exception{
try {
//还原密钥
Key k = toKey(key);
//加密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return Base64Util.getInstance().base64Encode(cipher.doFinal(data.getBytes("UTF-8")));
} catch (Exception e){
log.error("DESede加密错误",e);
}
return null;
}
发送请求报文,获得返回报文,解密、验签
/**
* 建行银企直连,发送请求报文,获得返回报文,解密、验签
*
* @param data
* @return
*/
public String sendRequest(String data, String signature, String charset) {
log.info("请求建行银企直连请求报文请求参数:{}", data);
HttpClient myclient = null;
PostMethod httppost = null;
String repcontent = "";
try {
myclient = new HttpClient(); // 构建http客户端
httppost = new PostMethod(bankKeyConfig.bankServerUrl); // 加密端口
httppost.addParameter("chanl_cust_no", bankKeyConfig.CHANL_CUST_NO);
httppost.addParameter("xml", data);
httppost.addParameter("signature", signature);
log.info("发送给银企直连服务数据:{}",data);
// 获得http返回码
int returnFlag = myclient.executeMethod(httppost);
InputStream postResult = httppost.getResponseBodyAsStream();
byte[] bytes = inputStream2byte(postResult);
byte[] len_byte = new byte[10];
System.arraycopy(bytes,0,len_byte,0,10);
repcontent = new String(len_byte);
log.info("请求建行银企直连秘钥传输报文返回结果:{}", repcontent);
//成功响应: 数字签名的长度(固定长度 10 个字节) +数字签名数据+加密后的数据。解密时应先根据前 10 位算出数字签名的长度,然后再截取数字签名及报文密文。
if (null != bytes && repcontent.startsWith("00")) {
//数字签名的长度(固定长度 10 个字节) +数字签名数据+加密后的数据
int signLenth = Integer.valueOf(repcontent.substring(0, BankConstants.LENGTH));
byte[] signByte = new byte[signLenth];
System.arraycopy(bytes,10,signByte,0,signLenth);
//返回后的签名
String sign = new String(Base64.encodeBase64(signByte),"UTF-8");
//加密后报文的长度
int srcLength = bytes.length - signLenth - 10;
byte[] scrByte = new byte[srcLength];
System.arraycopy(bytes,signLenth + 10,scrByte,0,srcLength);
//返回的解密后的报文
repcontent = DESedeUtil.decrypt(scrByte, bankKeyConfig.Des_KEY);
//将解密后的报文与签名验签
if (null != repcontent && RSASignUtil.verify(sign, repcontent, bankKeyConfig.RSA_KEY)) {
return repcontent;
} else {
log.error("请求建行银企直连验签出错:{} :{}", sign, repcontent);
}
}else {//失败响应: 6 位错误代码+错误信息,所有信息均不需要进行加密,以字节流返回。
log.error("请求建行银企直连秘钥传输报文出错:",repcontent);
return repcontent;
}
log.info("请求建行银企直连报文返回结果:{}", bytes);
return repcontent;
} catch (Exception e) {
log.error("请求建行银企直连报文出错,错误信息:" + e.getMessage(), e);
} finally {
try {
// 释放http连接
httppost.releaseConnection();
myclient.getHttpConnectionManager().closeIdleConnections(0);
} catch (Exception e2) {
log.error("银企直联释放http连接报错:{}", e2.getMessage());
}
myclient = null;
httppost = null;
}
return null;
}
byte[]数组用的比较少,如果有更好更简洁的方法请联系我,谢谢!
DES解密
/**
* 解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static String decrypt(byte[] data, String key){
try {
//还原密钥
Key k = toKey(key);
//加密
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return new String(cipher.doFinal(data));
} catch (Exception e){
log.error("DESede解密错误",e);
System.out.printf("");
}
return null;
}
RSA验签
/**
* 验签
*
* @param srcData 原始字符串
* @param sign 签名
* @return 是否验签通过
*/
public static boolean verify(String sign, String srcData ,String public_Key) throws Exception {
byte[] keyBytes = getPublicKey(public_Key).getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(key);
signature.update(srcData.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
}
签到签退调试好了之后别的接口也很简单了,有什么不懂的可以给我留言!