记得上下班打卡 | git大法好,push需谨慎

Commit 70db7894 authored by anjiabin's avatar anjiabin

提交unionpay代码

parent a3ef7eb2
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.security.PublicKey;
import java.util.Map;
/**
* @ClassName Acp6Service
* @Description 全渠道6.0接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2020/03
*/
@Slf4j
public class Acp6Service {
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> sign(Map<String, String> reqData,String encoding) {
return signByCertInfo(reqData, SDKConfig.getConfig().getSignCertPath(), SDKConfig.getConfig().getSignCertPwd(), encoding);
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
try {
data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名<br>
* @param data 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public static boolean validate(Map<String, String> data, String encoding) {
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String certId = data.get(SDKConstants.param_certId);
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
PublicKey verifyKey = CertUtil.getValidatePublicKey(certId);
if(verifyKey == null) {
log.error("未找到此序列号证书。");
return false;
}
try {
boolean result = SDKUtil.verifyRsa2(data, verifyKey, encoding);
log.info("验签" + (result ? "成功" : "失败") + "。");
return result;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1<br>
* @return
*/
public static int updateEncryptCert(String strCert, String certType) {
return SDKUtil.updateEncryptCert(strCert, certType);
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public static String encryptPin(String accNo, String pin, String encoding) {
byte[] pinblock = SecureUtil.pinblock(accNo, pin);
return Base64.encodeBase64String(SecureUtil.encrypt(CertUtil.getPinEncryptCert().pubKey, pinblock));
}
// /**
// * 密码加密并做base64<br>
// * @param accNo 卡号<br>
// * @param pin 密码<br>
// * @param encoding<br>
// * @return 加密的内容<br>
// */
// public static String encryptPin(String pin, String encoding) {
// byte[] pinblock = SecureUtil.pinblock(pin);
// return Base64.encodeBase64String(SecureUtil.encrypt(CertUtil.getPinEncryptCert().pubKey, pinblock));
// }
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public static String encryptData(String data, String encoding) {
return AcpService.encryptData(data, encoding);
}
/**
* @param data 明文<br>
* @return 加密的密文<br>
*/
public static String encryptData(byte[] data) {
try {
return Base64.encodeBase64String(SecureUtil.encrypt(CertUtil.getEncryptCert().pubKey, data));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* @param data 明文<br>
* @return 加密的密文<br>
*/
public static String tripleDesEncryptECBPKCS5Padding(byte[] key, byte[] data) {
try {
return Base64.encodeBase64String(SecureUtil.tripleDesEncryptECBPKCS5Padding(key, SecureUtil.rightPadZero(data, 8)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public static String decryptData(String base64EncryptedInfo, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, encoding);
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public static String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, certPath, certPwd, encoding);
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public static String getEncryptCertId(){
return CertUtil.getEncryptCert().certId;
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public static String getPinEncryptCertId(){
return CertUtil.getPinEncryptCert().certId;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public static Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) {
if(reqData == null || reqUrl == null) {
log.error("null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !SDKConfig.getConfig().isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
Map<String,String> result = SDKUtil.parseQString(new String(respBytes, encoding), encoding);
log.info("应答参数:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public static String postNotice(Map<String, String> reqData, String reqUrl,String encoding) {
if(reqData == null || reqUrl == null) {
log.error("null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !SDKConfig.getConfig().isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
String result = new String(respBytes, encoding);
log.info("应答体:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr, String encoding){
return AcpService.base64Encode(rawStr, encoding);
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str, String encoding){
return AcpService.base64Decode(base64Str, encoding);
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import java.io.*;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
/**
* @ClassName AcpService
* @Description acpsdk接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2016-7-22 下午2:44:37
*/
@Slf4j
public class AcpService {
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> sign(Map<String, String> reqData, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(param_signMethod);
String version = data.get(param_version);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
try{
if (SIGNMETHOD_RSA.equals(signMethod)) {
return signByCertInfo(data, SDKConfig.getConfig().getSignCertPath(), SDKConfig.getConfig().getSignCertPwd(), encoding);
} else if (SIGNMETHOD_SHA256.equals(signMethod)) {
return signBySecureKey(data, SDKConfig.getConfig().getSecureKey(), encoding);
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
return signBySecureKey(data, SDKConfig.getConfig().getSecureKey(), encoding);
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
String signMethod = data.get(param_signMethod);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_0_1.equals(version) || VERSION_5_0_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa(data, certPath, certPwd, encoding));
return data;
}
} else if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 多密钥签名(通过传入密钥签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param secureKey 签名对称密钥<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> signBySecureKey(Map<String, String> reqData, String secureKey, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(secureKey)) {
log.error("secureKey is empty");
return data;
}
String signMethod = data.get(param_signMethod);
String version = data.get(param_version);
try{
if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_SHA256.equals(signMethod)) {
data.put(SDKConstants.param_signature, SDKUtil.signSha256(data, secureKey, encoding));
return data;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
data.put(SDKConstants.param_signature, SDKUtil.signSm3(data, secureKey, encoding));
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名(SHA-1摘要算法)<br>
* @param data 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public static boolean validate(Map<String, String> data, String encoding) {
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(SDKConstants.param_signMethod);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_1_0.equals(version)){
if( SIGNMETHOD_SHA256.equals(signMethod))
return SDKUtil.verifySha256(data, SDKConfig.getConfig().getSecureKey(), encoding);
else if(SIGNMETHOD_SM3.equals(signMethod))
return SDKUtil.verifySm3(data, SDKConfig.getConfig().getSecureKey(), encoding);
}
if(SIGNMETHOD_RSA.equals(signMethod)) {
String strCert = data.get(SDKConstants.param_signPubKeyCert);
String certId = data.get(SDKConstants.param_certId);
PublicKey verifyKey = null;
if(!SDKUtil.isEmpty(strCert))
verifyKey = com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.verifyAndGetVerifyPubKey(strCert);
else if(!SDKUtil.isEmpty(certId)){
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
verifyKey = com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getValidatePublicKey(certId);
}
if(verifyKey == null) {
log.error("未成功获取验签公钥,验签失败。");
return false;
}
if(VERSION_5_0_0.equals(version) || VERSION_5_0_1.equals(version) ) {
boolean result = SDKUtil.verifyRsa(data, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if(VERSION_5_1_0.equals(version)) {
boolean result = SDKUtil.verifyRsa2(data, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
}
}
log.error("无法判断验签方法,验签失败。version="+version+", signMethod="+signMethod);
return false;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 多密钥验签(通过传入密钥签名)<br>
* @param data 返回报文数据<br>
* @param secureKey 密钥<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public static boolean validateBySecureKey(Map<String, String> data, String secureKey, String encoding) {
if(secureKey == null || "".equals(secureKey)) {
log.error("secureKey为空,验签失败。");
return false;
}
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(SDKConstants.param_signMethod);
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_SHA256.equals(signMethod)) {
boolean result = SDKUtil.verifySha256(data, secureKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
boolean result = SDKUtil.verifySm3(data, secureKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
}
}
log.error("无法判断验签方法,验签失败。version="+version+", signMethod="+signMethod);
return false;
} catch (Exception e) {
log.error("validateBySecureKey Error", e);
return false;
}
}
/**
* @deprecated 5.1.0开发包已删除此方法,请直接参考5.1.0开发包中的VerifyAppData.java验签。
* 对控件支付成功返回的结果信息中data域进行验签(控件端获取的应答信息)<br>
* @param jsonData json格式数据,例如:{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==", "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}
* @return 是否成功
*/
public static boolean validateAppResponse(String jsonData, String encoding) {
log.info("控件应答信息验签处理开始:[" + jsonData + "]");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
Pattern p = Pattern.compile("\\s*\"sign\"\\s*:\\s*\"([^\"]*)\"\\s*");
Matcher m = p.matcher(jsonData);
if(!m.find()) return false;
String sign = m.group(1);
p = Pattern.compile("\\s*\"data\"\\s*:\\s*\"([^\"]*)\"\\s*");
m = p.matcher(jsonData);
if(!m.find()) return false;
String data = m.group(1);
p = Pattern.compile("cert_id=(\\d*)");
m = p.matcher(jsonData);
if(!m.find()) return false;
String certId = m.group(1);
try {
// 验证签名需要用银联发给商户的公钥证书.
return com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.verifySignature(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil
.getValidatePublicKey(certId), Base64.decodeBase64(sign),
SDKUtil.byteArrayToHexString(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.sha1(data.getBytes(encoding))).getBytes(encoding));
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return false;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public static Map<String,String> post(
Map<String, String> reqData, String reqUrl, String encoding) {
if(reqData == null || reqUrl == null) {
log.error("post err: null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !SDKConfig.getConfig().isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
Map<String,String> result = SDKUtil.parseRespString(new String(respBytes, encoding));
log.info("应答参数:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:http Get方法 便民缴费产品中使用<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return
*/
public static String get(String reqUrl, String encoding) {
if(reqUrl == null) {
log.error("null input");
return null;
}
log.info("get请求银联地址:" + reqUrl);
if(!SDKConfig.getConfig().isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.get(reqUrl);
if(respBytes == null) {
log.error("post失败");
return null;
}
String result = new String(respBytes, encoding);
log.info("应答:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:前台交易构造HTTP POST自动提交表单<br>
* @param reqUrl 表单提交地址<br>
* @param hiddens 以MAP形式存储的表单键值<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 构造好的HTTP POST交易表单<br>
*/
public static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens,String encoding) {
StringBuffer sf = new StringBuffer();
sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset="+encoding+"\"/></head><body>");
sf.append("<form id = \"pay_form\" action=\"" + reqUrl
+ "\" method=\"post\">");
if (null != hiddens && 0 != hiddens.size()) {
Set<Entry<String, String>> set = hiddens.entrySet();
Iterator<Entry<String, String>> it = set.iterator();
while (it.hasNext()) {
Entry<String, String> ey = it.next();
String key = ey.getKey();
String value = ey.getValue();
sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\""
+ key + "\" value=\"" + value + "\"/>");
}
}
sf.append("</form>");
sf.append("</body>");
sf.append("<script type=\"text/javascript\">");
sf.append("document.all.pay_form.submit();");
sf.append("</script>");
sf.append("</html>");
String html = sf.toString();
log.info("打印请求HTML,此为请求报文,为联调排查问题的依据:"+html);
return html;
}
/**
* 功能:将批量文件内容使用DEFLATE压缩算法压缩,Base64编码生成字符串并返回<br>
* 适用到的交易:批量代付,批量代收,批量退货<br>
* @param filePath 批量文件-全路径文件名<br>
* @return
*/
public static String enCodeFileContent(String filePath, String encoding){
String baseFileContent = "";
File file = new File(filePath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
InputStream in = null;
try {
in = new FileInputStream(file);
int fl = in.available();
if (null != in) {
byte[] s = new byte[fl];
in.read(s, 0, fl);
// 压缩编码.
baseFileContent = Base64.encodeBase64String(SDKUtil.deflater(s));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
return baseFileContent;
}
/**
* 功能:解析交易返回的fileContent字符串并落地 ( 解base64,解DEFLATE压缩并落地)<br>
* 适用到的交易:对账文件下载,批量交易状态查询<br>
* @param data 返回报文map<br>
* @param fileDirectory 落地的文件目录(绝对路径)
* @param encoding 上送请求报文域encoding字段的值<br>
*/
public static String deCodeFileContent(Map<String, String> data,String fileDirectory,String encoding) {
// 解析返回文件
String filePath = null;
String fileContent = data.get(SDKConstants.param_fileContent);
if (null != fileContent && !"".equals(fileContent)) {
FileOutputStream out = null;
try {
byte[] fileArray = SDKUtil.inflater(Base64.decodeBase64(fileContent));
if (SDKUtil.isEmpty(data.get("fileName"))) {
filePath = fileDirectory + File.separator + data.get("merId")
+ "_" + data.get("batchNo") + "_"
+ data.get("txnTime") + ".txt";
} else {
filePath = fileDirectory + File.separator + data.get("fileName");
}
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
file.createNewFile();
out = new FileOutputStream(file);
out.write(fileArray, 0, fileArray.length);
out.flush();
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}finally{
try {
out.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
return filePath;
}
/**
* 功能:将结果文件内容 转换成明文字符串:解base64,解压缩<br>
* 适用到的交易:批量交易状态查询<br>
* @param fileContent 批量交易状态查询返回的文件内容<br>
* @return 内容明文<br>
*/
public static String getFileContent(String fileContent, String encoding){
String fc = "";
try {
fc = new String(SDKUtil.inflater(Base64.decodeBase64(fileContent)),encoding);
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return fc;
}
/**
* 功能:持卡人信息域customerInfo构造<br>
* 说明:不勾选对敏感信息加密权限使用旧的构造customerInfo域方式,不对敏感信息进行加密(对 phoneNo,cvn2, expired不加密),但如果送pin的话则加密<br>
* @param customerInfoMap 信息域请求参数 key送域名value送值,必送<br>
* 例如:customerInfoMap.put("certifTp", "01"); //证件类型<br>
customerInfoMap.put("certifId", "341126197709218366"); //证件号码<br>
customerInfoMap.put("customerNm", "互联网"); //姓名<br>
customerInfoMap.put("phoneNo", "13552535506"); //手机号<br>
customerInfoMap.put("smsCode", "123456"); //短信验证码<br>
customerInfoMap.put("pin", "111111"); //密码(加密)<br>
customerInfoMap.put("cvn2", "123"); //卡背面的cvn2三位数字(不加密)<br>
customerInfoMap.put("expired", "2311"); //有效期 年在前月在后(不加密)<br>
* @param accNo customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码pin,此字段可以不送<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return base64后的持卡人信息域字段<br>
*/
public static String getCustomerInfo(Map<String,String> customerInfoMap,String accNo,String encoding) {
if(customerInfoMap.isEmpty())
return "{}";
StringBuffer sf = new StringBuffer("{");
for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){
String key = it.next();
String value = customerInfoMap.get(key);
if(key.equals("pin")){
if(null == accNo || "".equals(accNo.trim())){
log.info("送了密码(PIN),必须在getCustomerInfo参数中上传卡号");
throw new RuntimeException("加密PIN没送卡号无法后续处理");
}else{
value = encryptPin(accNo,value,encoding);
}
}
sf.append(key).append(SDKConstants.EQUAL).append(value);
if(it.hasNext())
sf.append(SDKConstants.AMPERSAND);
}
String customerInfo = sf.append("}").toString();
log.info("组装的customerInfo明文:"+customerInfo);
try {
return Base64.encodeBase64String(sf.toString().getBytes(encoding));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfo;
}
/**
* 功能:持卡人信息域customerInfo构造,勾选对敏感信息加密权限 适用新加密规范,对pin和phoneNo,cvn2,expired加密 <br>
* 适用到的交易: <br>
* @param customerInfoMap 信息域请求参数 key送域名value送值,必送 <br>
* 例如:customerInfoMap.put("certifTp", "01"); //证件类型 <br>
customerInfoMap.put("certifId", "341126197709218366"); //证件号码 <br>
customerInfoMap.put("customerNm", "互联网"); //姓名 <br>
customerInfoMap.put("smsCode", "123456"); //短信验证码 <br>
customerInfoMap.put("pin", "111111"); //密码(加密) <br>
customerInfoMap.put("phoneNo", "13552535506"); //手机号(加密) <br>
customerInfoMap.put("cvn2", "123"); //卡背面的cvn2三位数字(加密) <br>
customerInfoMap.put("expired", "2311"); //有效期 年在前月在后(加密) <br>
* @param accNo customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码PIN,此字段可以不送<br>
* @param encoding 上送请求报文域encoding字段的值
* @return base64后的持卡人信息域字段 <br>
*/
public static String getCustomerInfoWithEncrypt(Map<String,String> customerInfoMap,String accNo,String encoding) {
if(customerInfoMap.isEmpty())
return "{}";
StringBuffer sf = new StringBuffer("{");
//敏感信息加密域
StringBuffer encryptedInfoSb = new StringBuffer("");
for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){
String key = it.next();
String value = customerInfoMap.get(key);
if(key.equals("phoneNo") || key.equals("cvn2") || key.equals("expired")){
encryptedInfoSb.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);
}else{
if(key.equals("pin")){
if(null == accNo || "".equals(accNo.trim())){
log.info("送了密码(PIN),必须在getCustomerInfoWithEncrypt参数中上传卡号");
throw new RuntimeException("加密PIN没送卡号无法后续处理");
}else{
value = encryptPin(accNo,value,encoding);
}
}
sf.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);
}
}
if(!encryptedInfoSb.toString().equals("")){
encryptedInfoSb.setLength(encryptedInfoSb.length()-1);//去掉最后一个&符号
log.info("组装的customerInfo encryptedInfo明文:"+ encryptedInfoSb.toString());
sf.append("encryptedInfo").append(SDKConstants.EQUAL).append(encryptData(encryptedInfoSb.toString(), encoding));
}else{
sf.setLength(sf.length()-1);
}
String customerInfo = sf.append("}").toString();
log.info("组装的customerInfo明文:"+customerInfo);
try {
return Base64.encodeBase64String(sf.toString().getBytes(encoding));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfo;
}
/**
* 解析返回报文(后台通知)中的customerInfo域:<br>
* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
* @param customerInfo<br>
* @param encoding<br>
* @return
*/
public static Map<String, String> parseCustomerInfo(String customerInfo, String encoding) {
Map<String, String> customerInfoMap = null;
try {
byte[] b = Base64.decodeBase64(customerInfo);
String customerInfoNoBase64 = new String(b, encoding);
log.info("解base64后===>" + customerInfoNoBase64);
//去掉前后的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
customerInfoMap = SDKUtil.parseRespString(customerInfoNoBase64);
if (customerInfoMap.containsKey("encryptedInfo")) {
String encInfoStr = customerInfoMap.get("encryptedInfo");
customerInfoMap.remove("encryptedInfo");
String encryptedInfoStr = decryptData(encInfoStr, encoding);
Map<String, String> encryptedInfoMap = SDKUtil.parseRespString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfoMap;
}
/**
* 解析返回报文(后台通知)中的customerInfo域:<br>
* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
* @param customerInfo<br>
* @param encoding<br>
* @return
*/
public static Map<String,String> parseCustomerInfo(String customerInfo, String certPath,
String certPwd, String encoding){
Map<String,String> customerInfoMap = null;
try {
byte[] b = Base64.decodeBase64(customerInfo);
String customerInfoNoBase64 = new String(b, encoding);
log.info("解base64后===>" + customerInfoNoBase64);
//去掉前后的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
customerInfoMap = SDKUtil.parseRespString(customerInfoNoBase64);
if (customerInfoMap.containsKey("encryptedInfo")) {
String encInfoStr = customerInfoMap.get("encryptedInfo");
customerInfoMap.remove("encryptedInfo");
String encryptedInfoStr = decryptData(encInfoStr, certPath, certPwd, encoding);
Map<String, String> encryptedInfoMap = SDKUtil.parseRespString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfoMap;
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public static String encryptPin(String accNo, String pin, String encoding) {
byte[] pinblock = com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.pinblock(accNo, pin);
return Base64.encodeBase64String(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.encrypt(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getEncryptCert().pubKey, pinblock));
}
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public static String encryptData(String data, String encoding) {
try {
return Base64.encodeBase64String(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.encrypt(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getEncryptCert().pubKey, data.getBytes(encoding)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public static String decryptData(String base64EncryptedInfo, String encoding) {
return new String(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.decrypt(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getSignCertPrivateKey(), Base64.decodeBase64(base64EncryptedInfo)), Charset.forName(encoding));
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public static String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return new String(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.decrypt(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd),
Base64.decodeBase64(base64EncryptedInfo)), Charset.forName(encoding));
}
/**
* 5.0.0加密磁道信息,5.1.0接口请勿使用<br>
* @param trackData 待加密磁道数据<br>
* @param encoding 编码格式<br>
* @return 加密的密文<br>
* @deprecated
*/
public static String encryptTrack(String trackData, String encoding) {
try {
return Base64.encodeBase64String(com.liquidnet.service.dragon.channel.unionpay.sdk.SecureUtil.encrypt(com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getEncryptTrackPublicKey(), trackData.getBytes(encoding)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public static String getEncryptCertId(){
return com.liquidnet.service.dragon.channel.unionpay.sdk.CertUtil.getEncryptCert().certId;
}
/**
*
* 有卡交易信息域(cardTransData)构造<br>
* 所有子域需用“{}”包含,子域间以“&”符号链接。格式如下:{子域名1=值&子域名2=值&子域名3=值}<br>
* 说明:本示例仅供参考,开发时请根据接口文档中的报文要素组装<br>
*
* @param cardTransDataMap cardTransData的数据<br>
* @param requestData 必须包含merId、orderId、txnTime、txnAmt,磁道加密时需要使用<br>
* @param encoding 编码<br>
* @return
*/
public static String getCardTransData(Map<String, String> cardTransDataMap,
Map<String, String> requestData,
String encoding) { {
StringBuffer cardTransDataBuffer = new StringBuffer();
if(cardTransDataMap.containsKey("track2Data")){
StringBuffer track2Buffer = new StringBuffer();
track2Buffer.append(requestData.get("merId"))
.append(SDKConstants.COLON).append(requestData.get("orderId"))
.append(SDKConstants.COLON).append(requestData.get("txnTime"))
.append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt"))
.append(SDKConstants.COLON).append(cardTransDataMap.get("track2Data"));
cardTransDataMap.put("track2Data",
AcpService.encryptData(track2Buffer.toString(), encoding));
}
if(cardTransDataMap.containsKey("track3Data")){
StringBuffer track3Buffer = new StringBuffer();
track3Buffer.append(requestData.get("merId"))
.append(SDKConstants.COLON).append(requestData.get("orderId"))
.append(SDKConstants.COLON).append(requestData.get("txnTime"))
.append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt"))
.append(SDKConstants.COLON).append(cardTransDataMap.get("track3Data"));
cardTransDataMap.put("track3Data",
AcpService.encryptData(track3Buffer.toString(), encoding));
}
return cardTransDataBuffer.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(cardTransDataMap, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
}
}
/**
* 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1<br>
* @return
*/
public static int updateEncryptCert(Map<String, String> data) {
return SDKUtil.updateEncryptCert(data.get(SDKConstants.param_encryptPubKeyCert), data.get(SDKConstants.param_certType));
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr, String encoding){
try {
return Base64.encodeBase64String(rawStr.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("不认识这个编码?" + encoding, e);
}
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str, String encoding){
try {
return new String(Base64.decodeBase64(base64Str), encoding);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("不认识这个编码?" + encoding, e);
}
}
/**
* 组成{a=b&c=d}字符串
* @param map
* @return
*/
public static String getKVField(Map<String, String> map){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, null))
.append(SDKConstants.RIGHT_BRACE).toString();
return info;
}
/**
* 解析{a=b&c=d}字符串
* @param data
* @return
*/
public static Map<String, String> parseKVField(String data){
if(data.length() <= 2) return null;
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 证书工具类.
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKUtil.isEmpty;
/**
* @ClassName: CertUtil
* @Description: acpsdk证书工具类,主要用于对证书的加载和使用
* @date 2016-7-22 下午2:46:20
*/
@Slf4j
public class CertUtil {
/** 验签中级证书 */
private static X509Certificate middleCert = null;
/** 验签根证书 */
private static X509Certificate rootCert = null;
/** 5.0磁道加密公钥 */
private static PublicKey encryptTrackKey = null;
/** 签名私钥map:证书路径,私钥 */
private final static Map<String, Cert> signCerts = new ConcurrentHashMap<String, Cert>();
/** 5.0接口验签证书Map:certId,公钥 */
private static Map<String, PublicKey> verifyCerts = new ConcurrentHashMap<String, PublicKey>();
/** 加密证书 */
private static Cert encryptCert = null;
/** 5.1接口验签证书Map:证书完整字符串,公钥 */
private static Map<String, PublicKey> verifyCerts510 = new ConcurrentHashMap<String, PublicKey>();
/** 6.0统一支付加密pin用,其他接口请勿使用 */
private static Cert pinEncryptCert = null;
protected static class Cert {
protected String certId;
protected PublicKey pubKey;
protected PrivateKey priKey;
}
static {
addProvider();//向系统添加BC provider
init();
}
/**
* 初始化所有证书.
*/
public static void init() {
try {
initSignCert();//初始化签名私钥证书
initMiddleCert();//初始化验签证书的中级证书
initRootCert();//初始化验签证书的根证书
initEncryptCert();//初始化加密公钥
initPinEncryptCert();//初始化pin加密公钥
initTrackKey();//构建磁道加密公钥
initValidateCertFromDir();//初始化所有的验签证书
} catch (Exception e) {
log.error("init失败。", e);
}
}
/**
* 添加签名,验签,加密算法提供者
*/
private static void addProvider(){
if (Security.getProvider("BC") == null) {
log.debug("add BC provider");
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} else {
Security.removeProvider("BC"); //解决eclipse调试时tomcat自动重新加载时,BC存在不明原因异常的问题。
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
log.debug("re-add BC provider");
}
printSysInfo();
}
/**
*
* @param path
* @param pwd
* @return
*/
private static Cert addSignCert(String path, String pwd) {
if (isEmpty(path) || isEmpty(pwd)) {
log.warn("签名证书路径或证书密码为空。 停止加载签名私钥证书。");
return null;
}
final String type = "PKCS12"; //实际BC只支持PKCS12,不支持JKS,就不去管JKS了……
log.info("加载签名私钥证书==>" + path);
FileInputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type, "BC");
log.debug("Load RSA CertPath=[" + path + "],Pwd=["+ pwd + "]");
fis = new FileInputStream(path);
char[] nPassword = null;
nPassword = null == pwd || "".equals(pwd.trim()) ? null: pwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
Enumeration<String> aliasenum = null;
aliasenum = ks.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
X509Certificate cert = (X509Certificate) ks.getCertificate(keyAlias);
Cert c = new Cert();
c.certId = cert.getSerialNumber().toString(10);
c.priKey = (PrivateKey) ks.getKey(keyAlias, pwd.toCharArray());
c.pubKey = cert.getPublicKey();
signCerts.put(path, c);
log.info("addSignCert Successful. CertId=[" + c.certId + "]");
return c;
} catch (Exception e) {
log.error("addSignCert Error", e);
} finally {
if(null!=fis)
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 指定路径读个x509证书
* @param path
* @return
*/
public static X509Certificate readX509Cert(String path) {
X509Certificate cert = null;
CertificateFactory cf = null;
FileInputStream in = null;
try {
cf = CertificateFactory.getInstance("X.509", "BC");
in = new FileInputStream(path);
cert = (X509Certificate) cf.generateCertificate(in);
} catch (FileNotFoundException e) {
log.error("readX509Cert Error File Not Found: " + path, e);
} catch (Exception e) {
log.error("readX509Cert Error", e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
}
}
}
return cert;
}
/**
* 用配置文件acp_sdk.properties中配置的私钥路径和密码 加载签名证书,会清空重新加载,一般仅第一次加载时调用即可
*/
private static void initSignCert() {
signCerts.clear();
String path = SDKConfig.getConfig().getSignCertPath();
String pwd = SDKConfig.getConfig().getSignCertPwd();
if(isEmpty(path) || isEmpty(pwd)) {
log.warn(SDKConfig.SDK_SIGNCERT_PATH + " or " + SDKConfig.SDK_SIGNCERT_PWD + " is empty");
return;
}
Cert cert = addSignCert(path, pwd);
log.info("读取配置文件默认签名证书==>" + path + (cert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载5.1验签证书中级证书
*/
private static void initMiddleCert() {
String path = SDKConfig.getConfig().getMiddleCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_MIDDLECERT_PATH + " is empty");
return;
}
middleCert = readX509Cert(path);
log.info("加载中级证书==>" + path + (middleCert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载5.1验签证书根证书
*/
private static void initRootCert() {
String path = SDKConfig.getConfig().getRootCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_ROOTCERT_PATH + " is empty");
return;
}
rootCert = readX509Cert(path);
log.info("加载根证书==>" + path + (rootCert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载磁道公钥
*/
private static void initTrackKey() {
String modulus = SDKConfig.getConfig().getEncryptTrackKeyModulus();
String exponent = SDKConfig.getConfig().getEncryptTrackKeyExponent();
if(isEmpty(modulus) || isEmpty(exponent)){
log.warn(SDKConfig.SDK_ENCRYPTTRACKKEY_MODULUS + " or " + SDKConfig.SDK_ENCRYPTTRACKKEY_EXPONENT + " is empty");
return;
}
encryptTrackKey = getPublicKey(modulus, exponent);
log.info("加载5.0磁道公钥==>" + (encryptTrackKey != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载验签证书
*/
private static void initValidateCertFromDir() {
verifyCerts.clear();
String dir = SDKConfig.getConfig().getValidateCertDir();
if (isEmpty(dir)) {
log.error("WARN: acpsdk.validateCert.dir is empty");
return;
}
log.info("加载验证签名证书目录==>" + dir +" 注:如果请求报文中version=5.1.0那么此验签证书目录使用不到,可以不需要设置(version=5.0.0必须设置)。");
File fileDir = new File(dir);
File[] files = fileDir.listFiles(new CerFilter());
for (int i = 0; i < files.length; i++) {
File file = files[i];
try {
X509Certificate verifyCert = readX509Cert(file.getAbsolutePath());
if(verifyCert == null) {
continue;
}
String certId = verifyCert.getSerialNumber().toString(10);
verifyCerts.put(certId, verifyCert.getPublicKey());
log.info("[" + file.getAbsolutePath() + "][CertId=" + certId + "]");
} catch (Exception e) {
log.error("Load verify cert error, " + file.getAbsolutePath(), e);
}
}
log.info("LoadVerifyCert Finish");
}
/**
* 用配置文件acp_sdk.properties配置路径 加载敏感信息加密证书
*/
private static void initEncryptCert() {
String path = SDKConfig.getConfig().getEncryptCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_ENCRYPTCERT_PATH + " is empty");
return;
}
X509Certificate encryptCert = readX509Cert(path);
log.info("加载敏感信息加密证书==>" + path + (encryptCert != null ?"成功":"失败"));
if(encryptCert != null) {
Cert c = new Cert();
c.certId = encryptCert.getSerialNumber().toString(10);
c.pubKey = encryptCert.getPublicKey();
CertUtil.encryptCert = c;
}
}
/**
* 用配置文件acp_sdk.properties配置路径 加载6.0统一支付产品pin加密证书
*/
private static void initPinEncryptCert() {
String path = SDKConfig.getConfig().getPinEncryptCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_PINENCRYPTCERT_PATH + " is empty");
return;
}
X509Certificate encryptCert = readX509Cert(path);
log.info("加载6.0统一支付产品pin加密证书==>" + path + (encryptCert != null ?"成功":"失败"));
if(encryptCert != null) {
Cert c = new Cert();
c.certId = encryptCert.getSerialNumber().toString(10);
c.pubKey = encryptCert.getPublicKey();
CertUtil.pinEncryptCert = c;
}
}
/**
*
*/
private static Cert getSignCert() {
String path = SDKConfig.getConfig().getSignCertPath();
String pwd = SDKConfig.getConfig().getSignCertPwd();
if(isEmpty(path) || isEmpty(pwd)) {
log.error("未配置默认签名证书时无法调用此方法。");
return null;
}
return getSignCert(path, pwd);
}
/**
*
* @param path
* @param pwd
* @return
*/
private static Cert getSignCert(String path, String pwd) {
if(isEmpty(path) || isEmpty(pwd)) {
log.error("传入的签名路径或密码为空。");
return null;
}
if(!signCerts.containsKey(path)){
addSignCert(path, pwd);
}
Cert c = signCerts.get(path);
if(c == null) {
log.error("未成功获取签名证书。");
return null;
}
return c;
}
/**
* 获取敏感信息加密证书PublicKey
*
* @return
*/
protected static Cert getEncryptCert() {
if(CertUtil.encryptCert == null) {
initEncryptCert();
}
return CertUtil.encryptCert;
}
/**
* 获取敏感信息加密证书PublicKey
*
* @return
*/
protected static Cert getPinEncryptCert() {
if(CertUtil.pinEncryptCert == null) {
initPinEncryptCert();
}
return CertUtil.pinEncryptCert;
}
/**
* 重置敏感信息加密证书公钥。
*/
public static int resetEncryptCertPublicKey(String strCert) {
if (isEmpty(strCert)) {
log.error("传入证书信息为空。");
return -1;
}
X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
// 没换,不需要更新
if (CertUtil.getEncryptCert().certId.equals(
x509Cert.getSerialNumber().toString(10))) {
log.info("返回证书和原证书一样,不用更新。");
return 0;
}
final String localCertPath = SDKConfig.getConfig().getEncryptCertPath();
if(isEmpty(localCertPath)){
log.error("未配置加密证书路径,无法执行此方法。");
return -1;
}
File f = new File(localCertPath);
if(!f.exists()) {
log.warn("原加密证书不存在:" + localCertPath);
} else {
// 将本地证书进行备份存储
int i = localCertPath.lastIndexOf(POINT);
String leftFileName = localCertPath.substring(0, i);
String rightFileName = localCertPath.substring(i + 1);
String newFileName = leftFileName + "_backup" + POINT + rightFileName;
try {
FileUtils.copyFile(f, new File(newFileName));
log.info("原加密证书备份成功。");
} catch (IOException e) {
log.error("原加密证书备份失败,停止改证书。", e);
return -1;
}
}
// 备份成功,进行新证书的存储
try {
FileUtils.writeByteArrayToFile(f, strCert.getBytes(), false);
log.info("加密证书更新成功。");
initEncryptCert();
return 1;
} catch (IOException e) {
log.error("加密证书更新失败。", e);
return -1;
}
}
/**
* 重置pin敏感信息加密证书公钥。
*/
public static int resetPinEncryptCertPublicKey(String strCert) {
if (isEmpty(strCert)) {
log.error("传入证书信息为空。");
return -1;
}
X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
// 没换,不需要更新
if (CertUtil.getPinEncryptCert().certId.equals(
x509Cert.getSerialNumber().toString(10))) {
log.info("返回证书和原证书一样,不用更新。");
return 0;
}
final String localCertPath = SDKConfig.getConfig().getPinEncryptCertPath();
if(isEmpty(localCertPath)){
log.error("未配置加密证书路径,无法执行此方法。");
return -1;
}
File f = new File(localCertPath);
if(!f.exists()) {
log.warn("原加密证书不存在:" + localCertPath);
} else {
// 将本地证书进行备份存储
int i = localCertPath.lastIndexOf(POINT);
String leftFileName = localCertPath.substring(0, i);
String rightFileName = localCertPath.substring(i + 1);
String newFileName = leftFileName + "_backup" + POINT + rightFileName;
try {
FileUtils.copyFile(f, new File(newFileName));
log.info("原加密证书备份成功。");
} catch (IOException e) {
log.error("原加密证书备份失败,停止改证书。", e);
return -1;
}
}
// 备份成功,进行新证书的存储
try {
FileUtils.writeByteArrayToFile(f, strCert.getBytes(), false);
log.info("加密证书更新成功。");
initPinEncryptCert();
return 1;
} catch (IOException e) {
log.error("加密证书更新失败。", e);
return -1;
}
}
/**
* 获取磁道加密证书PublicKey
*
* @return
*/
public static PublicKey getEncryptTrackPublicKey() {
if (null == encryptTrackKey) {
initTrackKey();
}
return encryptTrackKey;
}
/**
* 通过certId获取验签证书Map中对应证书PublicKey
*
* @param certId 证书物理序号
* @return 通过证书编号获取到的公钥
*/
public static PublicKey getValidatePublicKey(String certId) {
if(certId == null) {
log.error("没有传入certId.");
return null;
}
if (!verifyCerts.containsKey(certId)) {
initValidateCertFromDir();
}
PublicKey result = verifyCerts.get(certId);
if(result == null) {
log.error("缺少certId=[" + certId + "]对应的验签证书.");
return null;
}
return result;
}
/**
* 获取配置文件acp_sdk.properties中配置的签名私钥证书certId
*
* @return 证书的物理编号
*/
public static String getSignCertId() {
Cert c = getSignCert();
if(c == null) return null;
return c.certId;
}
/**
* 获取配置文件acp_sdk.properties中配置的签名私钥证书私钥
*
* @return 证书的物理编号
*/
public static PrivateKey getSignCertPrivateKey() {
Cert c = getSignCert();
if(c == null) return null;
return c.priKey;
}
/**
*
* @param path
* @param pwd
* @return
*/
public static String getCertIdByKeyStoreMap(String path, String pwd) {
Cert c = getSignCert(path, pwd);
if(c == null) return null;
return c.certId;
}
/**
*
* @param path
* @param pwd
* @return
*/
public static PrivateKey getSignCertPrivateKeyByStoreMap(String path, String pwd) {
Cert c = getSignCert(path, pwd);
if(c == null) return null;
return c.priKey;
}
// /**
// * 获取敏感信息加密证书的certId
// *
// * @return
// */
// public static String getEncryptCertId() {
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.certId;
// }
//
//
// public static PublicKey getEncryptCertPublicKey(){
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.pubKey;
// }
//
// /**
// * 获取6.0统一支付产品pin加密证书的certId
// *
// * @return
// */
// public static String getPinEncryptCertId() {
// Cert c = getPinEncryptCert();
// if(c == null) return null;
// return c.certId;
// }
//
//
// public static PublicKey getPinEncryptCertPublicKey(){
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.pubKey;
// }
/**
* 使用模和指数生成RSA公钥 注意:此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
private static PublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
log.error("构造RSA公钥失败:" + e);
return null;
}
}
/**
* 将字符串转换为X509Certificate对象.
*
* @param x509CertString
* @return
*/
public static X509Certificate genCertificateByStr(String x509CertString) {
X509Certificate x509Cert = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
InputStream tIn = new ByteArrayInputStream(
x509CertString.getBytes("ISO-8859-1"));
x509Cert = (X509Certificate) cf.generateCertificate(tIn);
} catch (Exception e) {
log.error("gen certificate error", e);
}
return x509Cert;
}
/**
* 从配置文件acp_sdk.properties中获取验签公钥使用的中级证书
* @return
*/
private static X509Certificate getMiddleCert() {
String path = SDKConfig.getConfig().getMiddleCertPath();
if (isEmpty(path)) {
log.error("未配置中级证书时无法调用此方法。");
return null;
}
if(middleCert == null) {
initMiddleCert();
}
return middleCert;
}
/**
* 从配置文件acp_sdk.properties中获取验签公钥使用的根证书
* @return
*/
private static X509Certificate getRootCert() {
String path = SDKConfig.getConfig().getRootCertPath();
if (isEmpty(path)) {
log.error("未配置根证书时无法调用此方法。");
return null;
}
if(rootCert == null) {
initRootCert();
}
return rootCert;
}
/**
* 获取证书的CN
* @param aCert
* @return
*/
public static String getIdentitiesFromCertficate(X509Certificate aCert) {
String tDN = aCert.getSubjectDN().toString();
String tPart = "";
if ((tDN != null)) {
String tSplitStr[] = tDN.substring(tDN.indexOf("CN=")).split("@");
if (tSplitStr != null && tSplitStr.length > 2
&& tSplitStr[2] != null)
tPart = tSplitStr[2];
}
return tPart;
}
/**
* 验证书链。
* @param cert
* @return
*/
public static boolean verifyCertificateChain(X509Certificate cert, X509Certificate middleCert, X509Certificate rootCert){
if (null == cert) {
log.error("cert must Not null");
return false;
}
if (null == middleCert) {
log.error("middleCert must Not null");
return false;
}
if (null == rootCert) {
log.error("rootCert or cert must Not null");
return false;
}
try {
X509CertSelector selector = new X509CertSelector();
selector.setCertificate(cert);
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
trustAnchors.add(new TrustAnchor(rootCert, null));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(
trustAnchors, selector);
Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
intermediateCerts.add(rootCert);
intermediateCerts.add(middleCert);
intermediateCerts.add(cert);
pkixParams.setRevocationEnabled(false);
CertStore intermediateCertStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCerts), "BC");
pkixParams.addCertStore(intermediateCertStore);
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
@SuppressWarnings("unused")
PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder
.build(pkixParams);
log.info("verify certificate chain succeed.");
return true;
} catch (java.security.cert.CertPathBuilderException e){
log.error("verify certificate chain fail.", e);
} catch (Exception e) {
log.error("verify certificate chain exception: ", e);
}
return false;
}
public static PublicKey verifyAndGetVerifyPubKey(String x509CertString){
if(isEmpty(x509CertString)) {
log.error("验签公钥证书传了空。");
return null;
}
if(verifyCerts510.containsKey(x509CertString))
return verifyCerts510.get(x509CertString);
log.debug("验签公钥证书:["+x509CertString+"]");
X509Certificate x509Cert = CertUtil.genCertificateByStr(x509CertString);
if (x509Cert == null) {
log.error("convert signPubKeyCert failed");
return null;
}
// 验证证书链
if (!CertUtil.verifyCertificate(x509Cert)) {
log.error("验证公钥证书失败,证书信息:[" + x509CertString + "]");
return null;
}
log.info("验证公钥验证成功:[" + x509Cert.getSerialNumber().toString(10) + "]");
PublicKey publicKey = x509Cert.getPublicKey();
verifyCerts510.put(x509CertString, publicKey);
return publicKey;
}
/**
*
* @param cert
* @return
*/
private static boolean verifyCertificate(X509Certificate cert) {
if ( null == cert) {
log.error("cert must Not null");
return false;
}
try {
cert.checkValidity();//验证有效期
if(!verifyCertificateChain(cert, CertUtil.getMiddleCert(), CertUtil.getRootCert())){
return false;
}
} catch (Exception e) {
log.error("verifyCertificate fail", e);
return false;
}
if(SDKConfig.getConfig().isIfValidateCNName()){
// 验证公钥是否属于银联
if(!UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))) {
log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));
return false;
}
} else {
// 验证公钥是否属于银联
if(!UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))
&& !"00040000:SIGN".equals(CertUtil.getIdentitiesFromCertficate(cert))) {
log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));
return false;
}
}
return true;
}
/**
* 打印系统环境信息
*/
private static void printSysInfo() {
log.info("================= SYS INFO begin====================");
log.info("os_name:" + System.getProperty("os.name"));
log.info("os_arch:" + System.getProperty("os.arch"));
log.info("os_version:" + System.getProperty("os.version"));
log.info("java_vm_specification_version:"
+ System.getProperty("java.vm.specification.version"));
log.info("java_vm_specification_vendor:"
+ System.getProperty("java.vm.specification.vendor"));
log.info("java_vm_specification_name:"
+ System.getProperty("java.vm.specification.name"));
log.info("java_vm_version:"
+ System.getProperty("java.vm.version"));
log.info("java_vm_name:" + System.getProperty("java.vm.name"));
log.info("java.version:" + System.getProperty("java.version"));
log.info("java.vm.vendor=[" + System.getProperty("java.vm.vendor") + "]");
log.info("java.version=[" + System.getProperty("java.version") + "]");
printProviders();
log.info("================= SYS INFO end=====================");
}
/**
* 打jre中印算法提供者列表
*/
private static void printProviders() {
log.info("Providers List:");
Provider[] providers = Security.getProviders();
for (int i = 0; i < providers.length; i++) {
log.info(i + 1 + "." + providers[i].getName());
}
}
/**
* 证书文件过滤器
*
*/
static class CerFilter implements FilenameFilter {
public boolean isCer(String name) {
return name.toLowerCase().endsWith(".cer");
}
public boolean accept(File dir, String name) {
return isCer(name);
}
}
public static Collection<PublicKey> getVerifySignPubKeys(){
return verifyCerts.values();
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/**
* http:
* 直接用get和post方法。
*
* 单向https用默认信任库验证证书:
* 直接用get和post方法。
*
* 单向https不验证证书:
* 用get和post方法时地址前面加个u,比如uhttps://101.231.204.80:5000/xxxx。
*
* 双向https:
* 先调addSslConf加客户端证书和信任库。
* 用get和post方法时地址前面加个自己写的tag替换掉https,比如cloudpos://101.231.204.80:5000/xxxx。
*/
@Slf4j
public class HttpsUtil {
// static {//商户如果通过代理访问需要的改动点
// System.setProperty("proxyType", "4");
// System.setProperty("proxyPort", "8080");
// System.setProperty("proxyHost", "172.16.1.245");
// System.setProperty("proxySet", "true");
// }
public static byte[] send(String urlStr, byte[] data, Map<String, String> reqHeader, String requestMethod){
String tag = null;
if (!urlStr.startsWith("https://") && !urlStr.startsWith("http://")) {
int idx = urlStr.indexOf("://");
if(idx <= 0) {
log.error("errurl [" + urlStr + "]");
}
tag = urlStr.substring(0, idx); //取tag
urlStr = "https" + urlStr.substring(idx); //地址转回https
}
if(data != null)
log.debug(requestMethod + " to [" + urlStr + "]: " + new String(data));
else
log.debug(requestMethod + " to [" + urlStr + "]");
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(urlStr);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(15000);// 连接超时时间
httpURLConnection.setReadTimeout(90000);// 读取结果超时时间
httpURLConnection.setDoInput(true); // 可读
httpURLConnection.setDoOutput(true); // 可写
httpURLConnection.setUseCaches(false);// 取消缓存
for(Entry<String, String> kv : reqHeader.entrySet()){
httpURLConnection.setRequestProperty(kv.getKey(), kv.getValue());
}
httpURLConnection.setRequestMethod(requestMethod);
if (tag != null) {
HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;
if(ssLSocketFactoryMap.containsKey(tag)) {
husn.setSSLSocketFactory(ssLSocketFactoryMap.get(tag));
}
if(ifVerifyHostnameMap.get(tag) == false) {
husn.setHostnameVerifier(trustAllHostnameVerifier);
}
}
httpURLConnection.connect();
if(data != null) {
httpURLConnection.getOutputStream().write(data);
httpURLConnection.getOutputStream().flush();
}
int httpCode = httpURLConnection.getResponseCode();
String encoding = httpURLConnection.getContentEncoding();
if(encoding == null) encoding = "UTF-8";
byte[] respBody;
if(httpCode != HttpURLConnection.HTTP_OK){
respBody = read(httpURLConnection.getErrorStream());
log.error("HTTP RESP[" + httpCode + "]: " + new String(respBody, encoding));
} else {
respBody = read(httpURLConnection.getInputStream());
log.debug("HTTP RESP[" + httpCode + "]: " + new String(respBody, encoding));
}
return respBody;
} catch (Exception e) {
log.error("[" + urlStr + "] " + e.getMessage() + " <- " + e.getClass().getName(), e);
} finally {
if(httpURLConnection != null)
httpURLConnection.disconnect();
}
return null;
}
public static byte[] post(String url, byte[] data, Map<String, String> reqHeader){
return send(url, data, reqHeader, "POST");
}
public static byte[] post(String url, byte[] data){
return post(url, data, new TreeMap<String, String>(){
private static final long serialVersionUID = -2761292859751696874L;
{
put("Content-Type", "application/x-www-form-urlencoded");
}
});
}
public static byte[] get(String url, Map<String, String> reqHeader){
return send(url, null, reqHeader, "GET");
}
public static byte[] get(String url){
return send(url, null, new TreeMap<String, String>(){
private static final long serialVersionUID = -7225134807859941048L;
{
put("Content-Type", "application/x-www-form-urlencoded");
}
}, "GET");
}
private static byte[] read(InputStream in) throws IOException {
byte[] buf = new byte[1024];
int length = 0;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
while ((length = in.read(buf, 0, buf.length)) > 0) {
bout.write(buf, 0, length);
}
bout.flush();
return bout.toByteArray();
}
private static Map<String, SSLSocketFactory> ssLSocketFactoryMap = new TreeMap<String, SSLSocketFactory>();
private static Map<String, Boolean> ifVerifyHostnameMap = new TreeMap<String, Boolean>();
private static final X509TrustManager trustAllX509TrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
private static final HostnameVerifier trustAllHostnameVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
/**
*
* @param tag
* @param keyStoreJksPath 客户端证书库jks。设空时效果等同为单向https。
* @param keyStoreJksPwd
* @param trustStoreJksPath 信任库jks。设空时效果等同都信任。
* @param trustStoreJksPwd
* @param verifyHostname 是否需要验证hostname。
* @return
*/
public static boolean addSslConf(String tag,
String keyStoreJksPath, String keyStoreJksPwd,
String trustStoreJksPath, String trustStoreJksPwd,
boolean verifyHostname) {
if(ssLSocketFactoryMap.containsKey(tag)) {
log.warn("addSslConf err: [" + tag + "] has been added");
return false;
}
try {
KeyManagerFactory kmf = null;
if(keyStoreJksPath != null) {
KeyStore keyStore = loadKeyStore(keyStoreJksPath, keyStoreJksPwd, "JKS");
if(keyStore == null) {
return false;
}
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStoreJksPwd.toCharArray());
}
TrustManagerFactory tmf = null;
if(trustStoreJksPath != null) {
KeyStore trustStore = loadKeyStore(trustStoreJksPath, trustStoreJksPwd, "JKS");
if(trustStore == null) {
return false;
}
tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
}
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = kmf == null ? null : kmf.getKeyManagers();
TrustManager[] trustManagers = tmf == null ? new TrustManager[]{trustAllX509TrustManager} : tmf.getTrustManagers(); //不知道为啥双向不验服务器证书的时候TrustManager必须设置,但单向的直接null也可以不验。
sslContext.init(keyManagers, trustManagers, SecureRandom.getInstance("SHA1PRNG"));
ssLSocketFactoryMap.put(tag, sslContext.getSocketFactory());
ifVerifyHostnameMap.put(tag, verifyHostname);
log.info("addSslConf succeed: [" + tag + "]" + (tmf == null ? ",但没验服务器证书哦" : "") + "。");
return true;
} catch (Exception e) {
log.error("addSslConf fail. ", e);
return false;
}
}
static {
addSslConf("uhttps", null, null, null, null, false);
}
private static KeyStore loadKeyStore(String path, String pwd, String type) {
FileInputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type);
fis = new FileInputStream(path);
char[] nPassword = null;
nPassword = null == pwd || "".equals(pwd.trim()) ? null : pwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
return ks;
} catch (Exception e) {
log.error("loadKeyStore Error", e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// e.printStackTrace();
}
}
}
return null;
}
public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
// HttpsUtil.addSslConf("cloudpos", "d:/certs/cloudpos/01003320-------001-----.jks", "000000", "d:/certs/cloudpos/trust.jks", "000000", false);
// for(int i=0;i<10000;i++){
// post("uhttps://gateway.test.95516.com/gateway/api/appTransReq.do", "accessType=0&bizType=000000&txnSubType=00&signature=ff3e9d82a5528ad51c5b3bde5fd2d62a8e016a68d28abb584140ad2d2baa2c2a&orderId=2019112215532700000154&reqReserved=%7Btestcase%3DTestCase_90_DF%7D&txnTime=20191122155327&txnType=00&merId=777290058110018&encoding=UTF-8&version=5.1.0&signMethod=12".getBytes());
// post("cloudpos://180.169.111.145:10009/CloudPosPayment/InsTransServlet", "{\"TrxIndCd\":\"SPK00461694051982042817\",\"TrxTp\":\"SPK004\",\"Version\":\"100\",\"FirmTermId\":\"52079000\",\"CerVer\":\"02\",\"FirmMchntId\":\"001332060120003\",\"EncryptData\":\"e8r/n/VkA1nQ+eOVRL5iHBwvgw0q/KYEyeARox+dtPFAKFsXmY064x6UU/pTBX3OLcA9PjEy3ATUmnxMwCb0GTRyn4Qh3OvotOh2YytbNt4g9gii5movpX04+eKCcbU2eIH5D4Bx+TTOv0t94LvMgf4FMN5djZ5XOjfnx9mpajrMKQEKBdwp97hOTv49QZJqW/AzMaagfErxBKqEeC+yG1pyvg23Xg+Scq/qheJ/w2XvCLKFoeZjKWN5bevpVkHS7KZdAqCx3T4wHEmZd0wo5YF3WdHwBT53LHZ6l6m0Ja6HpTxyaGccGULgcyhxxttWyqzoVKQCFU3ZlfX3b5Yxdg==\"}".getBytes());
// }
// addSslConf("test", null, null, null, null, true);
// post("test://gateway.test.95516.com/gateway/api/appTransReq.do", "accessType=0&bizType=000000&txnSubType=00&signature=ff3e9d82a5528ad51c5b3bde5fd2d62a8e016a68d28abb584140ad2d2baa2c2a&orderId=2019112215532700000154&reqReserved=%7Btestcase%3DTestCase_90_DF%7D&txnTime=20191122155327&txnType=00&merId=777290058110018&encoding=UTF-8&version=5.1.0&signMethod=12".getBytes());
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.Map;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
/**
* @ClassName QrcService
* @Description sdk接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2018-12-10 下午2:44:37
*/
@Slf4j
public class QrcService {
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> sign(Map<String, String> reqData,String encoding) {
return signByCertInfo(reqData, SDKConfig.getConfig().getSignCertPath(), SDKConfig.getConfig().getSignCertPwd(), encoding);
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
String signType = data.get(SDKConstants.param_signType);
String version = data.get(SDKConstants.param_version);
String reqType = data.get(SDKConstants.param_reqType);
if (SDKUtil.isEmpty(signType)) {
signType = QRC_SIGNTYPE_SHA1WITHRSA;
}
try {
if (VERSION_1_0_0.equals(version)) {
//被扫脱机码两个接口无视配置固定按sha256withrsa处理。下面两个ifelse别改变顺序。
if (QRC_SIGNTYPE_SHA256WITHRSA.equals(signType)
|| "0420000903".equals(reqType)
|| "0410000903".equals(reqType)) {
data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
} else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) {
data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, SDKUtil.signRsa(data, certPath, certPwd, encoding));
return data;
} else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) {
log.error("国密算法按要求必须通过加密机签名,本sdk不提供。");
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signType=" + signType);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名<br>
* @param resData 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public static boolean validate(Map<String, String> resData, String encoding) {
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String certId = resData.get(SDKConstants.param_certId);
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
PublicKey verifyKey = CertUtil.getValidatePublicKey(certId);
if(verifyKey == null) {
log.error("未找到此序列号证书。");
return false;
}
String signType = resData.get(SDKConstants.param_signType);
String version = resData.get(SDKConstants.param_version);
String reqType = resData.get(SDKConstants.param_reqType);
if (SDKUtil.isEmpty(signType)) {
signType = QRC_SIGNTYPE_SHA1WITHRSA;
}
try {
if (VERSION_1_0_0.equals(version)) {
//被扫脱机码两个接口无视配置固定按sha256withrsa处理。下面两个ifelse别改变顺序。
if (QRC_SIGNTYPE_SHA256WITHRSA.equals(signType)
|| "0420000903".equals(reqType)
|| "0410000903".equals(reqType)) {
boolean result = SDKUtil.verifyRsa2(resData, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) {
boolean result = SDKUtil.verifyRsa(resData, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) {
log.error("国密算法按要求必须通过加密机签名,本sdk不提供。");
return false;
}
}
log.error("未实现验签方法, version=" + version + ", signType=" + signType);
return false;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return false;
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public static String encryptPin(String accNo, String pin, String encoding) {
return AcpService.encryptPin(accNo, pin, encoding);
}
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public static String encryptData(String data, String encoding) {
return AcpService.encryptData(data, encoding);
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public static String decryptData(String base64EncryptedInfo, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, encoding);
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public static String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return AcpService.decryptData(base64EncryptedInfo, certPath, certPwd, encoding);
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public static String getEncryptCertId(){
return CertUtil.getEncryptCert().certId;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public static Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) {
return AcpService.post(reqData, reqUrl, encoding);
}
/**
* base64({a=b&c=d})
* @param map
* @param encoding
* @return
*/
public static String getKVBase64Field(Map<String, String> map, String encoding){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
return Base64.encodeBase64String(info.getBytes(Charset.forName(encoding)));
}
/**
* base64(rsa({a=b&c=d}))
* @param map
* @param encoding
* @return
*/
public static String getKVEncBase64Field(Map<String, String> map,String encoding){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
return QrcService.encryptData(info, encoding);
}
/**
* base64({a=b&c=d})
* 解析返回报文的payerInfo域,敏感信息不加密时使用:<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public static Map<String, String> parseKVBase64Field(String base64data, String encoding){
String data = new String(Base64.decodeBase64(base64data), Charset.forName(encoding));
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* base64(rsa({a=b&c=d}))
* 解析返回报文的payerInfo域,敏感信息加密时使用:<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public static Map<String, String> parseKVEncBase64Field(String base64data, String encoding){
String data = QrcService.decryptData(base64data, encoding);
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* base64(rsa({a=b&c=d}))
* 解析返回报文中的payerInfo域,敏感信息加密时使用,多证书方式。<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public static Map<String, String> parseKVEncBase64Field(String base64data, String certPath,
String certPwd, String encoding){
String data = QrcService.decryptData(base64data, certPath, certPwd, encoding);
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr, String encoding){
return AcpService.base64Encode(rawStr, encoding);
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str, String encoding){
return AcpService.base64Decode(base64Str, encoding);
}
/**
* luhn算法
*
* @param number
* @return
*/
public static int genLuhn(String number) {
number = number + "0";
int s1 = 0, s2 = 0;
String reverse = new StringBuffer(number).reverse().toString();
for (int i = 0; i < reverse.length(); i++) {
int digit = Character.digit(reverse.charAt(i), 10);
if (i % 2 == 0) {// this is for odd digits, they are 1-indexed in //
// the algorithm
s1 += digit;
} else {// add 2 * digit for 0-4, add 2 * digit - 9 for 5-9
s2 += 2 * digit;
if (digit >= 5) {
s2 -= 9;
}
}
}
int check = 10 - ((s1 + s2) % 10);
if (check == 10)
check = 0;
return check;
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI基本参数工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.Properties;
/**
*
* @ClassName SDKConfig
* @Description acpsdk配置文件acp_sdk.properties配置信息类
* @date 2016-7-22 下午4:04:55
*/
@Slf4j
public class SDKConfig {
public static final String FILE_NAME = "acp_sdk.properties";
/** 前台请求URL. */
private String frontRequestUrl;
/** 后台请求URL. */
private String backRequestUrl;
/** 二维码统一下单请求URL. */
private String orderRequestUrl;
/** 单笔查询 */
private String singleQueryUrl;
/** 批量查询 */
private String batchQueryUrl;
/** 批量交易 */
private String batchTransUrl;
/** 文件传输 */
private String fileTransUrl;
/** 签名证书路径. */
private String signCertPath;
/** 签名证书密码. */
private String signCertPwd;
/** 签名证书类型. */
private String signCertType;
/** 加密公钥证书路径. */
private String encryptCertPath;
/** 验证签名公钥证书目录. */
private String validateCertDir;
/** 按照商户代码读取指定签名证书目录. */
private String signCertDir;
// /** 磁道加密证书路径. */
// private String encryptTrackCertPath;
/** 磁道加密公钥模数. */
private String encryptTrackKeyModulus;
/** 磁道加密公钥指数. */
private String encryptTrackKeyExponent;
/** 6.0.0统一支付产品加密pin公钥证书路径. */
private String pinEncryptCertPath;
/** 有卡交易. */
private String cardRequestUrl;
/** app交易 */
private String appRequestUrl;
/** 证书使用模式(单证书/多证书) */
private String singleMode;
/** 安全密钥(SHA256和SM3计算时使用) */
private String secureKey;
/** 中级证书路径 */
private String middleCertPath;
/** 根证书路径 */
private String rootCertPath;
/** 是否验证验签证书CN,除了false都验 */
private boolean ifValidateCNName = true;
/** 是否验证https证书,默认都不验 */
private boolean ifValidateRemoteCert = false;
/** signMethod,没配按01吧 */
private String signMethod = "01";
/** version,没配按5.0.0 */
private String version = "5.0.0";
/** frontUrl */
private String frontUrl;
/** backUrl */
private String backUrl;
/** frontFailUrl */
private String frontFailUrl;
/*缴费相关地址*/
private String jfFrontRequestUrl;
private String jfBackRequestUrl;
private String jfSingleQueryUrl;
private String jfCardRequestUrl;
private String jfAppRequestUrl;
//二维码
private String qrcBackTransUrl;
private String qrcB2cIssBackTransUrl;
private String qrcB2cMerBackTransUrl;
private String qrcB2cMerBackSynTransUrl;
//综合认证
private String zhrzFrontRequestUrl;
private String zhrzBackRequestUrl;
private String zhrzSingleQueryUrl;
private String zhrzCardRequestUrl;
private String zhrzAppRequestUrl;
private String zhrzFaceRequestUrl;
/** acp6 */
private String transUrl;
/** 配置文件中的前台URL常量. */
public static final String SDK_FRONT_URL = "acpsdk.frontTransUrl";
/** 配置文件中的后台URL常量. */
public static final String SDK_BACK_URL = "acpsdk.backTransUrl";
/** 配置文件中的统一下单URL常量. */
public static final String SDK_ORDER_URL = "acpsdk.orderTransUrl";
/** 配置文件中的单笔交易查询URL常量. */
public static final String SDK_SIGNQ_URL = "acpsdk.singleQueryUrl";
/** 配置文件中的批量交易查询URL常量. */
public static final String SDK_BATQ_URL = "acpsdk.batchQueryUrl";
/** 配置文件中的批量交易URL常量. */
public static final String SDK_BATTRANS_URL = "acpsdk.batchTransUrl";
/** 配置文件中的文件类交易URL常量. */
public static final String SDK_FILETRANS_URL = "acpsdk.fileTransUrl";
/** 配置文件中的有卡交易URL常量. */
public static final String SDK_CARD_URL = "acpsdk.cardTransUrl";
/** 配置文件中的app交易URL常量. */
public static final String SDK_APP_URL = "acpsdk.appTransUrl";
/** 以下缴费产品使用,其余产品用不到,无视即可 */
// 前台请求地址
public static final String JF_SDK_FRONT_TRANS_URL= "acpsdk.jfFrontTransUrl";
// 后台请求地址
public static final String JF_SDK_BACK_TRANS_URL="acpsdk.jfBackTransUrl";
// 单笔查询请求地址
public static final String JF_SDK_SINGLE_QUERY_URL="acpsdk.jfSingleQueryUrl";
// 有卡交易地址
public static final String JF_SDK_CARD_TRANS_URL="acpsdk.jfCardTransUrl";
// App交易地址
public static final String JF_SDK_APP_TRANS_URL="acpsdk.jfAppTransUrl";
// 人到人
public static final String QRC_BACK_TRANS_URL="acpsdk.qrcBackTransUrl";
// 人到人
public static final String QRC_B2C_ISS_BACK_TRANS_URL="acpsdk.qrcB2cIssBackTransUrl";
// 人到人
public static final String QRC_B2C_MER_BACK_TRANS_URL="acpsdk.qrcB2cMerBackTransUrl";
public static final String QRC_B2C_MER_BACK_SYN_TRANS_URL="acpsdk.qrcB2cMerBackSynTransUrl";
/** 以下综合认证产品使用,其余产品用不到,无视即可 */
// 前台请求地址
public static final String ZHRZ_SDK_FRONT_TRANS_URL= "acpsdk.zhrzFrontTransUrl";
// 后台请求地址
public static final String ZHRZ_SDK_BACK_TRANS_URL="acpsdk.zhrzBackTransUrl";
// 单笔查询请求地址
public static final String ZHRZ_SDK_SINGLE_QUERY_URL="acpsdk.zhrzSingleQueryUrl";
// 有卡交易地址
public static final String ZHRZ_SDK_CARD_TRANS_URL="acpsdk.zhrzCardTransUrl";
// App交易地址
public static final String ZHRZ_SDK_APP_TRANS_URL="acpsdk.zhrzAppTransUrl";
// 图片识别交易地址
public static final String ZHRZ_SDK_FACE_TRANS_URL="acpsdk.zhrzFaceTransUrl";
// acp6
public static final String TRANS_URL="acpsdk.transUrl";
/** 配置文件中签名证书路径常量. */
public static final String SDK_SIGNCERT_PATH = "acpsdk.signCert.path";
/** 配置文件中签名证书密码常量. */
public static final String SDK_SIGNCERT_PWD = "acpsdk.signCert.pwd";
/** 配置文件中签名证书类型常量. */
public static final String SDK_SIGNCERT_TYPE = "acpsdk.signCert.type";
/** 配置文件中加密证书路径常量. */
public static final String SDK_ENCRYPTCERT_PATH = "acpsdk.encryptCert.path";
// /** 配置文件中磁道加密证书路径常量. */
// public static final String SDK_ENCRYPTTRACKCERT_PATH = "acpsdk.encryptTrackCert.path";
/** 配置文件中5.0.0有卡产品磁道加密公钥模数常量. */
public static final String SDK_ENCRYPTTRACKKEY_MODULUS = "acpsdk.encryptTrackKey.modulus";
/** 配置文件中5.0.0有卡产品磁道加密公钥指数常量. */
public static final String SDK_ENCRYPTTRACKKEY_EXPONENT = "acpsdk.encryptTrackKey.exponent";
/** 配置文件中验证签名证书目录常量. */
public static final String SDK_VALIDATECERT_DIR = "acpsdk.validateCert.dir";
/** 配置文件中6.0.0统一支付产品加密pin证书路径常量. */
public static final String SDK_PINENCRYPTCERT_PATH = "acpsdk.pinEncryptCert.path";
/** 配置文件中是否加密cvn2常量. */
public static final String SDK_CVN_ENC = "acpsdk.cvn2.enc";
/** 配置文件中是否加密cvn2有效期常量. */
public static final String SDK_DATE_ENC = "acpsdk.date.enc";
/** 配置文件中是否加密卡号常量. */
public static final String SDK_PAN_ENC = "acpsdk.pan.enc";
/** 配置文件中证书使用模式 */
public static final String SDK_SINGLEMODE = "acpsdk.singleMode";
/** 配置文件中安全密钥 */
public static final String SDK_SECURITYKEY = "acpsdk.secureKey";
/** 配置文件中根证书路径常量 */
public static final String SDK_ROOTCERT_PATH = "acpsdk.rootCert.path";
/** 配置文件中根证书路径常量 */
public static final String SDK_MIDDLECERT_PATH = "acpsdk.middleCert.path";
/** 配置是否需要验证验签证书CN,除了false之外的值都当true处理 */
public static final String SDK_IF_VALIDATE_CN_NAME = "acpsdk.ifValidateCNName";
/** 配置是否需要验证https证书,除了true之外的值都当false处理 */
public static final String SDK_IF_VALIDATE_REMOTE_CERT = "acpsdk.ifValidateRemoteCert";
/** signmethod */
public static final String SDK_SIGN_METHOD ="acpsdk.signMethod";
/** version */
public static final String SDK_VERSION = "acpsdk.version";
/** 后台通知地址 */
public static final String SDK_BACKURL = "acpsdk.backUrl";
/** 前台通知地址 */
public static final String SDK_FRONTURL = "acpsdk.frontUrl";
/** 前台失败通知地址 */
public static final String SDK_FRONT_FAIL_URL = "acpsdk.frontFailUrl";
/** 操作对象. */
private static SDKConfig config = null;
/** 属性文件对象. */
protected Properties properties;
protected SDKConfig() {
super();
}
/**
* 获取config对象.
* @return
*/
public static SDKConfig getConfig() {
if(config == null) {
log.warn("未主动调用loadPropertiesFromSrc或loadPropertiesFromPath方法,默认调loadPropertiesFromSrc初始化");
SDKConfig.loadPropertiesFromSrc();
}
if(config == null) {
log.error("初始化失败");
config = new SDKConfig();
}
return config;
}
/**
* 从properties文件加载
*
* @param rootPath
* 不包含文件名的目录.
*/
public static void loadPropertiesFromPath(String rootPath) {
Properties properties = getPropertiesFromPath(rootPath);
loadProperties(properties);
}
protected static Properties getPropertiesFromSrc() {
InputStream in = null;
try {
log.info("从classpath: " +SDKConfig.class.getClassLoader().getResource("").getPath()+" 获取属性文件"+FILE_NAME);
in = SDKConfig.class.getClassLoader().getResourceAsStream(FILE_NAME);
if (in == null) {
log.error(FILE_NAME + "属性文件未能在classpath指定的目录下 "+SDKConfig.class.getClassLoader().getResource("").getPath()+" 找到!");
return null;
}
Properties properties = new Properties();
properties.load(in);
return properties;
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
return null;
}
protected static Properties getPropertiesFromPath(String rootPath) {
if (rootPath == null || "".equals(rootPath.trim())) {
log.error("rootPath为空");
return null;
}
String path = rootPath + File.separator + FILE_NAME;
log.info("从路径读取配置文件: " + path);
File file = new File(path);
InputStream in = null;
if (!file.exists()) {
log.error(rootPath + "不存在" + FILE_NAME);
return null;
}
try {
in = new FileInputStream(file);
Properties properties = new Properties();
properties.load(in);
return properties;
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
return null;
}
/**
* 从classpath路径下加载配置参数
*/
public static void loadPropertiesFromSrc() {
Properties properties = getPropertiesFromSrc();
loadProperties(properties);
}
/**
* 根据传入的 {@link Properties}对象设置配置参数
*
* @param pro
*/
public static void loadProperties(Properties pro) {
config = new SDKConfig();
config.loadProperties1(pro);
}
/**
* 根据传入的 {@link Properties}对象设置配置参数
*
* @param pro
*/
protected void loadProperties1(Properties pro) {
if(pro == null) {
log.error("loadProperties input is null");
return;
}
if(this.properties == null) {
this.properties = pro;
}
log.info("开始从属性文件中加载配置项");
String value = null;
value = pro.getProperty(SDK_SIGNCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.signCertPath = value.trim();
log.info("配置项:私钥签名证书路径==>"+SDK_SIGNCERT_PATH +"==>"+ value+" 已加载");
}
value = pro.getProperty(SDK_SIGNCERT_PWD);
if (!SDKUtil.isEmpty(value)) {
this.signCertPwd = value.trim();
log.info("配置项:私钥签名证书密码==>"+SDK_SIGNCERT_PWD +" 已加载");
}
value = pro.getProperty(SDK_SIGNCERT_TYPE);
if (!SDKUtil.isEmpty(value)) {
this.signCertType = value.trim();
log.info("配置项:私钥签名证书类型==>"+SDK_SIGNCERT_TYPE +"==>"+ value+" 已加载");
}
value = pro.getProperty(SDK_ENCRYPTCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.encryptCertPath = value.trim();
log.info("配置项:敏感信息加密证书==>"+SDK_ENCRYPTCERT_PATH +"==>"+ value+" 已加载");
}
value = pro.getProperty(SDK_VALIDATECERT_DIR);
if (!SDKUtil.isEmpty(value)) {
this.validateCertDir = value.trim();
log.info("配置项:验证签名证书路径(这里配置的是目录,不要指定到公钥文件)==>"+SDK_VALIDATECERT_DIR +"==>"+ value+" 已加载");
}
value = pro.getProperty(SDK_PINENCRYPTCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.pinEncryptCertPath = value.trim();
log.info("配置项:6.0.0统一支付产品加密pin证书路径(这里配置的是目录,不要指定到公钥文件)==>"+SDK_PINENCRYPTCERT_PATH +"==>"+ value+" 已加载");
}
value = pro.getProperty(SDK_FRONT_URL);
if (!SDKUtil.isEmpty(value)) {
this.frontRequestUrl = value.trim();
}
value = pro.getProperty(SDK_BACK_URL);
if (!SDKUtil.isEmpty(value)) {
this.backRequestUrl = value.trim();
}
value = pro.getProperty(SDK_ORDER_URL);
if (!SDKUtil.isEmpty(value)) {
this.orderRequestUrl = value.trim();
}
value = pro.getProperty(SDK_BATQ_URL);
if (!SDKUtil.isEmpty(value)) {
this.batchQueryUrl = value.trim();
}
value = pro.getProperty(SDK_BATTRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.batchTransUrl = value.trim();
}
value = pro.getProperty(SDK_FILETRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.fileTransUrl = value.trim();
}
value = pro.getProperty(SDK_SIGNQ_URL);
if (!SDKUtil.isEmpty(value)) {
this.singleQueryUrl = value.trim();
}
value = pro.getProperty(SDK_CARD_URL);
if (!SDKUtil.isEmpty(value)) {
this.cardRequestUrl = value.trim();
}
value = pro.getProperty(SDK_APP_URL);
if (!SDKUtil.isEmpty(value)) {
this.appRequestUrl = value.trim();
}
// value = pro.getProperty(SDK_ENCRYPTTRACKCERT_PATH);
// if (!SDKUtil.isEmpty(value)) {
// this.encryptTrackCertPath = value.trim();
// }
value = pro.getProperty(SDK_SECURITYKEY);
if (!SDKUtil.isEmpty(value)) {
this.secureKey = value.trim();
}
value = pro.getProperty(SDK_ROOTCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.rootCertPath = value.trim();
}
value = pro.getProperty(SDK_MIDDLECERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.middleCertPath = value.trim();
}
/**缴费部分**/
value = pro.getProperty(JF_SDK_FRONT_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.jfFrontRequestUrl = value.trim();
}
value = pro.getProperty(JF_SDK_BACK_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.jfBackRequestUrl = value.trim();
}
value = pro.getProperty(JF_SDK_SINGLE_QUERY_URL);
if (!SDKUtil.isEmpty(value)) {
this.jfSingleQueryUrl = value.trim();
}
value = pro.getProperty(JF_SDK_CARD_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.jfCardRequestUrl = value.trim();
}
value = pro.getProperty(JF_SDK_APP_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.jfAppRequestUrl = value.trim();
}
value = pro.getProperty(QRC_BACK_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.qrcBackTransUrl = value.trim();
}
value = pro.getProperty(QRC_B2C_ISS_BACK_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.qrcB2cIssBackTransUrl = value.trim();
}
value = pro.getProperty(QRC_B2C_MER_BACK_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.qrcB2cMerBackTransUrl = value.trim();
}
value = pro.getProperty(QRC_B2C_MER_BACK_SYN_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.qrcB2cMerBackSynTransUrl = value.trim();
}
/**综合认证**/
value = pro.getProperty(ZHRZ_SDK_FRONT_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzFrontRequestUrl = value.trim();
}
value = pro.getProperty(ZHRZ_SDK_BACK_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzBackRequestUrl = value.trim();
}
value = pro.getProperty(ZHRZ_SDK_SINGLE_QUERY_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzSingleQueryUrl = value.trim();
}
value = pro.getProperty(ZHRZ_SDK_CARD_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzCardRequestUrl = value.trim();
}
value = pro.getProperty(ZHRZ_SDK_APP_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzAppRequestUrl = value.trim();
}
value = pro.getProperty(ZHRZ_SDK_FACE_TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.zhrzFaceRequestUrl = value.trim();
}
value = pro.getProperty(TRANS_URL);
if (!SDKUtil.isEmpty(value)) {
this.transUrl = value.trim();
}
value = pro.getProperty(SDK_ENCRYPTTRACKKEY_EXPONENT);
if (!SDKUtil.isEmpty(value)) {
this.encryptTrackKeyExponent = value.trim();
}
value = pro.getProperty(SDK_ENCRYPTTRACKKEY_MODULUS);
if (!SDKUtil.isEmpty(value)) {
this.encryptTrackKeyModulus = value.trim();
}
value = pro.getProperty(SDK_IF_VALIDATE_CN_NAME);
if (!SDKUtil.isEmpty(value)) {
if( SDKConstants.FALSE_STRING.equals(value.trim()))
this.ifValidateCNName = false;
}
value = pro.getProperty(SDK_IF_VALIDATE_REMOTE_CERT);
if (!SDKUtil.isEmpty(value)) {
if( SDKConstants.TRUE_STRING.equals(value.trim()))
this.ifValidateRemoteCert = true;
}
value = pro.getProperty(SDK_SIGN_METHOD);
if (!SDKUtil.isEmpty(value)) {
this.signMethod = value.trim();
}
value = pro.getProperty(SDK_SIGN_METHOD);
if (!SDKUtil.isEmpty(value)) {
this.signMethod = value.trim();
}
value = pro.getProperty(SDK_VERSION);
if (!SDKUtil.isEmpty(value)) {
this.version = value.trim();
}
value = pro.getProperty(SDK_FRONTURL);
if (!SDKUtil.isEmpty(value)) {
this.frontUrl = value.trim();
}
value = pro.getProperty(SDK_BACKURL);
if (!SDKUtil.isEmpty(value)) {
this.backUrl = value.trim();
}
value = pro.getProperty(SDK_FRONT_FAIL_URL);
if (!SDKUtil.isEmpty(value)) {
this.frontFailUrl = value.trim();
}
}
public String getFrontRequestUrl() {
return frontRequestUrl;
}
public String getBackRequestUrl() {
return backRequestUrl;
}
public String getOrderRequestUrl() {
return orderRequestUrl;
}
public String getSignCertPath() {
return signCertPath;
}
public String getSignCertPwd() {
return signCertPwd;
}
public String getSignCertType() {
return signCertType;
}
public String getEncryptCertPath() {
return encryptCertPath;
}
public String getValidateCertDir() {
return validateCertDir;
}
public String getSingleQueryUrl() {
return singleQueryUrl;
}
public String getBatchQueryUrl() {
return batchQueryUrl;
}
public String getBatchTransUrl() {
return batchTransUrl;
}
public String getFileTransUrl() {
return fileTransUrl;
}
public String getSignCertDir() {
return signCertDir;
}
public Properties getProperties() {
return properties;
}
public String getCardRequestUrl() {
return cardRequestUrl;
}
public String getAppRequestUrl() {
return appRequestUrl;
}
// public String getEncryptTrackCertPath() {
// return encryptTrackCertPath;
// }
public String getPinEncryptCertPath() {
return pinEncryptCertPath;
}
public String getJfFrontRequestUrl() {
return jfFrontRequestUrl;
}
public String getJfBackRequestUrl() {
return jfBackRequestUrl;
}
public String getJfSingleQueryUrl() {
return jfSingleQueryUrl;
}
public String getJfCardRequestUrl() {
return jfCardRequestUrl;
}
public String getJfAppRequestUrl() {
return jfAppRequestUrl;
}
public String getSingleMode() {
return singleMode;
}
public String getEncryptTrackKeyExponent() {
return encryptTrackKeyExponent;
}
public String getEncryptTrackKeyModulus() {
return encryptTrackKeyModulus;
}
public String getSecureKey() {
return secureKey;
}
public String getMiddleCertPath() {
return middleCertPath;
}
public boolean isIfValidateCNName() {
return ifValidateCNName;
}
public boolean isIfValidateRemoteCert() {
return ifValidateRemoteCert;
}
public String getSignMethod() {
return signMethod;
}
public String getQrcBackTransUrl() {
return qrcBackTransUrl;
}
public String getQrcB2cIssBackTransUrl() {
return qrcB2cIssBackTransUrl;
}
public String getQrcB2cMerBackTransUrl() {
return qrcB2cMerBackTransUrl;
}
public String getQrcB2cMerBackSynTransUrl() {
return qrcB2cMerBackSynTransUrl;
}
public String getZhrzFrontRequestUrl() {
return zhrzFrontRequestUrl;
}
public String getZhrzBackRequestUrl() {
return zhrzBackRequestUrl;
}
public String getZhrzSingleQueryUrl() {
return zhrzSingleQueryUrl;
}
public String getZhrzCardRequestUrl() {
return zhrzCardRequestUrl;
}
public String getZhrzAppRequestUrl() {
return zhrzAppRequestUrl;
}
public String getZhrzFaceRequestUrl() {
return zhrzFaceRequestUrl;
}
public String getVersion() {
return version;
}
public String getFrontUrl() {
return frontUrl;
}
public String getBackUrl() {
return backUrl;
}
public String getFrontFailUrl() {
return frontFailUrl;
}
public String getRootCertPath() {
return rootCertPath;
}
public String getTransUrl() {
return transUrl;
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI插件包常量定义
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
/**
*
* @ClassName SDKConstants
* @Description acpsdk常量类
* @date 2016-7-22 下午4:05:54
*/
public class SDKConstants {
public final static String COLUMN_DEFAULT = "-";
public final static String KEY_DELIMITER = "#";
/** memeber variable: blank. */
public static final String BLANK = "";
/** member variabel: space. */
public static final String SPACE = " ";
/** memeber variable: unline. */
public static final String UNLINE = "_";
/** memeber varibale: star. */
public static final String STAR = "*";
/** memeber variable: line. */
public static final String LINE = "-";
/** memeber variable: add. */
public static final String ADD = "+";
/** memeber variable: colon. */
public final static String COLON = "|";
/** memeber variable: point. */
public final static String POINT = ".";
/** memeber variable: comma. */
public final static String COMMA = ",";
/** memeber variable: slash. */
public final static String SLASH = "/";
/** memeber variable: div. */
public final static String DIV = "/";
/** memeber variable: left . */
public final static String LB = "(";
/** memeber variable: right. */
public final static String RB = ")";
/** memeber variable: rmb. */
public final static String CUR_RMB = "RMB";
/** memeber variable: .page size */
public static final int PAGE_SIZE = 10;
/** memeber variable: String ONE. */
public static final String ONE = "1";
/** memeber variable: String ZERO. */
public static final String ZERO = "0";
/** memeber variable: number six. */
public static final int NUM_SIX = 6;
/** memeber variable: equal mark. */
public static final String EQUAL = "=";
/** memeber variable: operation ne. */
public static final String NE = "!=";
/** memeber variable: operation le. */
public static final String LE = "<=";
/** memeber variable: operation ge. */
public static final String GE = ">=";
/** memeber variable: operation lt. */
public static final String LT = "<";
/** memeber variable: operation gt. */
public static final String GT = ">";
/** memeber variable: list separator. */
public static final String SEP = "./";
/** memeber variable: Y. */
public static final String Y = "Y";
/** memeber variable: AMPERSAND. */
public static final String AMPERSAND = "&";
/** memeber variable: SQL_LIKE_TAG. */
public static final String SQL_LIKE_TAG = "%";
/** memeber variable: @. */
public static final String MAIL = "@";
/** memeber variable: number zero. */
public static final int NZERO = 0;
public static final String LEFT_BRACE = "{";
public static final String RIGHT_BRACE = "}";
/** memeber variable: string true. */
public static final String TRUE_STRING = "true";
/** memeber variable: string false. */
public static final String FALSE_STRING = "false";
/** memeber variable: forward success. */
public static final String SUCCESS = "success";
/** memeber variable: forward fail. */
public static final String FAIL = "fail";
/** memeber variable: global forward success. */
public static final String GLOBAL_SUCCESS = "$success";
/** memeber variable: global forward fail. */
public static final String GLOBAL_FAIL = "$fail";
public static final String UTF_8_ENCODING = "UTF-8";
public static final String GBK_ENCODING = "GBK";
public static final String CONTENT_TYPE = "Content-type";
public static final String APP_XML_TYPE = "application/xml;charset=utf-8";
public static final String APP_FORM_TYPE = "application/x-www-form-urlencoded;charset=";
public static final String VERSION_1_0_0 = "1.0.0";
public static final String VERSION_5_0_0 = "5.0.0";
public static final String VERSION_5_0_1 = "5.0.1";
public static final String VERSION_5_1_0 = "5.1.0";
public static final String VERSION_6_0_0 = "6.0.0";
public static final String SIGNMETHOD_RSA = "01";
public static final String SIGNMETHOD_SHA256 = "11";
public static final String SIGNMETHOD_SM3 = "12";
public static final String SIGNMETHOD6_RSA_SHA256 = "RSA-SHA256";
public static final String UNIONPAY_CNNAME = "中国银联股份有限公司";
public static final String QRC_SIGNTYPE_SHA1WITHRSA = "01";
public static final String QRC_SIGNTYPE_SHA256WITHRSA = "02";
public static final String QRC_SIGNTYPE_SM3WITHSM2 = "03";
public static final String CERTTYPE_01 = "01";// 敏感信息加密公钥
public static final String CERTTYPE_02 = "02";// 磁道加密公钥
/******************************************** 5.0报文接口定义 ********************************************/
/** 版本号. */
public static final String param_version = "version";
/** 证书ID. */
public static final String param_certId = "certId";
/** 签名. */
public static final String param_signature = "signature";
/** 签名方法. */
public static final String param_signMethod = "signMethod";
/** 编码方式. */
public static final String param_encoding = "encoding";
/** 交易类型. */
public static final String param_txnType = "txnType";
/** 交易子类. */
public static final String param_txnSubType = "txnSubType";
/** 业务类型. */
public static final String param_bizType = "bizType";
/** 前台通知地址 . */
public static final String param_frontUrl = "frontUrl";
/** 后台通知地址. */
public static final String param_backUrl = "backUrl";
/** 接入类型. */
public static final String param_accessType = "accessType";
/** 收单机构代码. */
public static final String param_acqInsCode = "acqInsCode";
/** 商户类别. */
public static final String param_merCatCode = "merCatCode";
/** 商户类型. */
public static final String param_merType = "merType";
/** 商户代码. */
public static final String param_merId = "merId";
/** 商户名称. */
public static final String param_merName = "merName";
/** 商户简称. */
public static final String param_merAbbr = "merAbbr";
/** 二级商户代码. */
public static final String param_subMerId = "subMerId";
/** 二级商户名称. */
public static final String param_subMerName = "subMerName";
/** 二级商户简称. */
public static final String param_subMerAbbr = "subMerAbbr";
/** Cupsecure 商户代码. */
public static final String param_csMerId = "csMerId";
/** 商户订单号. */
public static final String param_orderId = "orderId";
/** 交易时间. */
public static final String param_txnTime = "txnTime";
/** 发送时间. */
public static final String param_txnSendTime = "txnSendTime";
/** 订单超时时间间隔. */
public static final String param_orderTimeoutInterval = "orderTimeoutInterval";
/** 支付超时时间. */
public static final String param_payTimeoutTime = "payTimeoutTime";
/** 默认支付方式. */
public static final String param_defaultPayType = "defaultPayType";
/** 支持支付方式. */
public static final String param_supPayType = "supPayType";
/** 支付方式. */
public static final String param_payType = "payType";
/** 自定义支付方式. */
public static final String param_customPayType = "customPayType";
/** 物流标识. */
public static final String param_shippingFlag = "shippingFlag";
/** 收货地址-国家. */
public static final String param_shippingCountryCode = "shippingCountryCode";
/** 收货地址-省. */
public static final String param_shippingProvinceCode = "shippingProvinceCode";
/** 收货地址-市. */
public static final String param_shippingCityCode = "shippingCityCode";
/** 收货地址-地区. */
public static final String param_shippingDistrictCode = "shippingDistrictCode";
/** 收货地址-详细. */
public static final String param_shippingStreet = "shippingStreet";
/** 商品总类. */
public static final String param_commodityCategory = "commodityCategory";
/** 商品名称. */
public static final String param_commodityName = "commodityName";
/** 商品URL. */
public static final String param_commodityUrl = "commodityUrl";
/** 商品单价. */
public static final String param_commodityUnitPrice = "commodityUnitPrice";
/** 商品数量. */
public static final String param_commodityQty = "commodityQty";
/** 是否预授权. */
public static final String param_isPreAuth = "isPreAuth";
/** 币种. */
public static final String param_currencyCode = "currencyCode";
/** 账户类型. */
public static final String param_accType = "accType";
/** 账号. */
public static final String param_accNo = "accNo";
/** 支付卡类型. */
public static final String param_payCardType = "payCardType";
/** 发卡机构代码. */
public static final String param_issInsCode = "issInsCode";
/** 持卡人信息. */
public static final String param_customerInfo = "customerInfo";
/** 交易金额. */
public static final String param_txnAmt = "txnAmt";
/** 余额. */
public static final String param_balance = "balance";
/** 地区代码. */
public static final String param_districtCode = "districtCode";
/** 附加地区代码. */
public static final String param_additionalDistrictCode = "additionalDistrictCode";
/** 账单类型. */
public static final String param_billType = "billType";
/** 账单号码. */
public static final String param_billNo = "billNo";
/** 账单月份. */
public static final String param_billMonth = "billMonth";
/** 账单查询要素. */
public static final String param_billQueryInfo = "billQueryInfo";
/** 账单详情. */
public static final String param_billDetailInfo = "billDetailInfo";
/** 账单金额. */
public static final String param_billAmt = "billAmt";
/** 账单金额符号. */
public static final String param_billAmtSign = "billAmtSign";
/** 绑定标识号. */
public static final String param_bindId = "bindId";
/** 风险级别. */
public static final String param_riskLevel = "riskLevel";
/** 绑定信息条数. */
public static final String param_bindInfoQty = "bindInfoQty";
/** 绑定信息集. */
public static final String param_bindInfoList = "bindInfoList";
/** 批次号. */
public static final String param_batchNo = "batchNo";
/** 总笔数. */
public static final String param_totalQty = "totalQty";
/** 总金额. */
public static final String param_totalAmt = "totalAmt";
/** 文件类型. */
public static final String param_fileType = "fileType";
/** 文件名称. */
public static final String param_fileName = "fileName";
/** 批量文件内容. */
public static final String param_fileContent = "fileContent";
/** 商户摘要. */
public static final String param_merNote = "merNote";
/** 商户自定义域. */
// public static final String param_merReserved = "merReserved";//接口变更删除
/** 请求方保留域. */
public static final String param_reqReserved = "reqReserved";// 新增接口
/** 保留域. */
public static final String param_reserved = "reserved";
/** 终端号. */
public static final String param_termId = "termId";
/** 终端类型. */
public static final String param_termType = "termType";
/** 交互模式. */
public static final String param_interactMode = "interactMode";
/** 发卡机构识别模式. */
// public static final String param_recognitionMode = "recognitionMode";
public static final String param_issuerIdentifyMode = "issuerIdentifyMode";// 接口名称变更
/** 商户端用户号. */
public static final String param_merUserId = "merUserId";
/** 持卡人IP. */
public static final String param_customerIp = "customerIp";
/** 查询流水号. */
public static final String param_queryId = "queryId";
/** 原交易查询流水号. */
public static final String param_origQryId = "origQryId";
/** 系统跟踪号. */
public static final String param_traceNo = "traceNo";
/** 交易传输时间. */
public static final String param_traceTime = "traceTime";
/** 清算日期. */
public static final String param_settleDate = "settleDate";
/** 清算币种. */
public static final String param_settleCurrencyCode = "settleCurrencyCode";
/** 清算金额. */
public static final String param_settleAmt = "settleAmt";
/** 清算汇率. */
public static final String param_exchangeRate = "exchangeRate";
/** 兑换日期. */
public static final String param_exchangeDate = "exchangeDate";
/** 响应时间. */
public static final String param_respTime = "respTime";
/** 原交易应答码. */
public static final String param_origRespCode = "origRespCode";
/** 原交易应答信息. */
public static final String param_origRespMsg = "origRespMsg";
/** 应答码. */
public static final String param_respCode = "respCode";
/** 应答码信息. */
public static final String param_respMsg = "respMsg";
// 新增四个报文字段merUserRegDt merUserEmail checkFlag activateStatus
/** 商户端用户注册时间. */
public static final String param_merUserRegDt = "merUserRegDt";
/** 商户端用户注册邮箱. */
public static final String param_merUserEmail = "merUserEmail";
/** 验证标识. */
public static final String param_checkFlag = "checkFlag";
/** 开通状态. */
public static final String param_activateStatus = "activateStatus";
/** 加密证书ID. */
public static final String param_encryptCertId = "encryptCertId";
/** 用户MAC、IMEI串号、SSID. */
public static final String param_userMac = "userMac";
/** 关联交易. */
// public static final String param_relationTxnType = "relationTxnType";
/** 短信类型 */
public static final String param_smsType = "smsType";
/** 风控信息域 */
public static final String param_riskCtrlInfo = "riskCtrlInfo";
/** IC卡交易信息域 */
public static final String param_ICTransData = "ICTransData";
/** VPC交易信息域 */
public static final String param_VPCTransData = "VPCTransData";
/** 安全类型 */
public static final String param_securityType = "securityType";
/** 银联订单号 */
public static final String param_tn = "tn";
/** 分期付款手续费率 */
public static final String param_instalRate = "instalRate";
/** 分期付款手续费率 */
public static final String param_mchntFeeSubsidy = "mchntFeeSubsidy";
/** 签名公钥证书 */
public static final String param_signPubKeyCert = "signPubKeyCert";
/** 加密公钥证书 */
public static final String param_encryptPubKeyCert = "encryptPubKeyCert";
/** 证书类型 */
public static final String param_certType = "certType";
/** 二维码接口签名方法. */
public static final String param_signType = "signType";
/** 二维码接口交易类型. */
public static final String param_reqType = "reqType";
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.PublicKey;
import java.util.*;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
/**
*
* @ClassName SDKUtil
* @Description acpsdk工具类
* @date 2016-7-22 下午4:06:18
*/
@Slf4j
public class SDKUtil {
/**
* 全渠道5.0、二维码signType=01用。
* 1. 按ascii排序。【注意不是字母顺序】
* 2. 对1的结果sha1得到byte数组。
* 3. 对2的结果用16进制小写字符串表示。【注意必须是小写】
* 4. 对3的结果取byte数组。【注意不是16进制字符串转byte数组,而是当普通字符串转】
* 5. 对4的结果用私钥算签名,算法为sha1withrsa。
* 6. 对5的结果做base64,得到一个字符串就是签名。
* @param data
* @param certPath
* @param certPwd
* @param encoding
* @return
*/
public static String signRsa(Map<String, String> data, String certPath, String certPwd, String encoding) {
try{
String stringData = createLinkString(data, true, false, encoding);
log.info("打印排序后待签名请求报文串(交易返回11验证签名失败时可以用来同正确的进行比对):[" + stringData + "]");
byte[] sha1 = SecureUtil.sha1(stringData.getBytes(encoding));
String sha1Hex = byteArrayToHexString(sha1).toLowerCase();
log.info("sha1结果(交易返回11验证签名失败可以用来同正确的进行比对):[" + sha1Hex + "]");
return Base64.encodeBase64String(SecureUtil.getSignature(CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), sha1Hex.getBytes()));
} catch (Exception e) {
log.error("calcSignRsa Error", e);
return null;
}
}
/**
* 全渠道5.1signMethod=01、二维码signType=02(以及少数接口)用。
* 1. 按ascii排序。【注意不是字母顺序】
* 2. 对1的结果sha256得到byte数组。
* 3. 对2的结果用16进制小写字符串表示。【注意必须是小写】
* 4. 对3的结果取byte数组。【注意不是16进制字符串转byte数组,而是当普通字符串转】
* 5. 对4的结果用私钥算签名,算法为sha256withrsa。
* 6. 对5的结果做base64,得到一个字符串就是签名。
* @param data
* @param certPath
* @param certPwd
* @param encoding
* @return
*/
public static String signRsa2(Map<String, String> data, String certPath, String certPwd, String encoding) {
try {
String stringData = createLinkString(data, true, false, encoding);
log.info("打印排序后待签名请求报文串(交易返回11验证签名失败时可以用来同正确的进行比对):[" + stringData + "]");
byte[] sha256 = SecureUtil.sha256(stringData.getBytes(encoding));
String sha256Hex = byteArrayToHexString(sha256).toLowerCase();
log.info("sha256(交易返回11验证签名失败可以用来同正确的进行比对):[" + sha256Hex + "]");
return Base64.encodeBase64String(SecureUtil.getSignatureSHA256(CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), sha256Hex.getBytes()));
} catch (Exception e) {
log.error("calcSignRsa2 Error", e);
return null;
}
}
/**
* 全渠道5.1signMethod=11用。
* @param data
* @param encoding
* @return
*/
public static String signSha256(Map<String, String> data, String secureKey, String encoding) {
String sorted = createLinkString(data, true, false, encoding);
log.info("排序串:[" + sorted + "]");
return SDKUtil.calcSignSha256(sorted, secureKey, encoding);
}
/**
* 全渠道5.1signMethod=12用。
* @param data
* @param encoding
* @return
*/
public static String signSm3(Map<String, String> data, String secureKey, String encoding) {
String sorted = createLinkString(data, true, false, encoding);
log.info("排序串:[" + sorted + "]");
return SDKUtil.calcSignSm3(sorted, secureKey, encoding);
}
private static String calcSignSm3(String sorted, String secureKey, String encoding) {
try {
String s = sorted + SDKConstants.AMPERSAND
+ byteArrayToHexString(SecureUtil.sm3(secureKey.getBytes(encoding)));
return byteArrayToHexString(SecureUtil.sm3(s.getBytes(encoding)));
} catch (Exception e) {
log.error("calcSignSm3 Error", e);
return null;
}
}
private static String calcSignSha256(String sorted, String secureKey, String encoding) {
try {
String s = sorted + SDKConstants.AMPERSAND
+ byteArrayToHexString(SecureUtil.sha256(secureKey.getBytes(encoding)));
return byteArrayToHexString(SecureUtil.sha256(s.getBytes(encoding)));
} catch (Exception e) {
log.error("calcSignSha256 Error", e);
return null;
}
}
/**
* 全渠道5.0接口、二维码signType=01用。
* @param resData
* @param encoding
* @return
*/
public static boolean verifyRsa(Map<String, String> resData, PublicKey pubKey, String encoding) {
try{
String stringSign = resData.remove(SDKConstants.param_signature);
if(isEmpty(stringSign)) {
log.error("signature is null. verifyRsa fail.");
return false;
}
log.info("签名原文:[" + stringSign + "]");
String stringData = createLinkString(resData, true, false, encoding);
log.info("待验签排序串:[" + stringData + "]");
byte[] sha1 = SecureUtil.sha1(stringData.getBytes(encoding));
String sha1Hex = byteArrayToHexString(sha1).toLowerCase();
log.info("sha1结果:[" + sha1Hex + "]");
return SecureUtil.verifySignature(pubKey,
sha1Hex.getBytes(encoding),
Base64.decodeBase64(stringSign));
} catch (Exception e) {
log.error("verifyRsa fail." + e.getMessage(), e);
return false;
}
}
/**
* 全渠道5.1、二维码signType=02用。
* @param resData
* @param encoding
* @param publicKey
* @return
*/
public static boolean verifyRsa2(Map<String, String> resData, PublicKey publicKey, String encoding) {
try{
String stringSign = resData.remove(SDKConstants.param_signature);
if(isEmpty(stringSign)) {
log.error("signature is null. verifyRsa2 fail.");
return false;
}
log.info("签名原文:[" + stringSign + "]");
String stringData = createLinkString(resData, true, false, encoding);
log.info("待验签排序串:[" + stringData + "]");
byte[] sha256 = SecureUtil.sha256(stringData.getBytes(encoding));
String sha256Hex = byteArrayToHexString(sha256).toLowerCase();
log.info("sha256结果:[" + sha256Hex + "]");
boolean result = SecureUtil.verifySignatureSHA256(publicKey,
sha256Hex.getBytes(encoding),
Base64.decodeBase64(stringSign));
log.info("验证签名" + (result ? "成功" : "失败"));
return result;
} catch (Exception e) {
log.error("verifyRsa2 fail." + e.getMessage(), e);
return false;
}
}
public static boolean verifySha256(Map<String, String> resData, String secureKey, String encoding){
try{
if(isEmpty(secureKey)) {
log.error("secureKey is null. verifySha256 fail.");
return false;
}
String sign = resData.remove(SDKConstants.param_signature);
if(isEmpty(sign)) {
log.error("signature is null. verifySha256 fail.");
return false;
}
log.info("签名原文:[" + sign + "]");
String sorted = createLinkString(resData, true, false, encoding);
log.info("排序串:[" + sorted + "]");
String expectedSign = calcSignSha256(sorted, secureKey, encoding);
boolean result = sign.equals(expectedSign);
if(!result) log.error("验签失败,签名预期:" + expectedSign + ", 签名实际:" + sign);
else log.info("验签成功");
return result;
} catch (Exception e) {
log.error("verifySha256 fail." + e.getMessage(), e);
return false;
}
}
public static boolean verifySm3(Map<String, String> resData, String secureKey, String encoding){
try{
if(isEmpty(secureKey)) {
log.error("secureKey is null. verifySm3 fail.");
return false;
}
String sign = resData.remove(SDKConstants.param_signature);
if(isEmpty(sign)) {
log.error("signature is null. verifySm3 fail.");
return false;
}
log.info("签名原文:[" + sign + "]");
String sorted = createLinkString(resData, true, false, encoding);
log.info("排序串:[" + sorted + "]");
String expectedSign = calcSignSm3(sorted, secureKey, encoding);
boolean result = sign.equals(expectedSign);
if(!result) log.error("验签失败,签名预期:" + expectedSign + ", 签名实际:" + sign);
else log.info("验签成功");
return result;
} catch (Exception e) {
log.error("verifySm3 fail." + e.getMessage(), e);
return false;
}
}
/**
* 把请求要素按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param para
* 请求要素
* @param sort
* 是否需要根据key值作升序排列
* @param encode
* 是否需要URL编码
* @return 拼接成的字符串
*/
public static String createLinkString(Map<String, String> para, boolean sort, boolean encode, String charset) {
List<String> keys = new ArrayList<String>(para.keySet());
if (sort)
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = para.get(key);
if (encode && value != null) {
try {
value = URLEncoder.encode(value, charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(charset + "送错了.");
}
}
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
sb.append(key).append("=").append(value);
} else {
sb.append(key).append("=").append(value).append("&");
}
}
return sb.toString();
}
/**
* 解析应答字符串,生成应答要素。
* 解析全渠道5.0、5.1,二维码的应答报文时不要用这个方法哦。
* @param str 需要解析的字符串
* @param charset
* @return 解析的结果map
* @throws UnsupportedEncodingException
*/
public static Map<String, String> parseQString(String str, String charset) {
if (str == null || str.length() == 0 ) return new HashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
int len = str.length();
StringBuilder temp = new StringBuilder();
char curChar;
String key = null;
boolean isKey = true;
for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串
curChar = str.charAt(i);// 取当前字符
if (curChar == '&') {// 如果读取到&分割符
putKeyValueToMap(temp, isKey, key, map, true, charset);
temp.setLength(0);
isKey = true;
} else {
if (isKey) {// 如果当前生成的是key
if (curChar == '=') {// 如果读取到=分隔符
key = temp.toString();
temp.setLength(0);
isKey = false;
} else {
temp.append(curChar);
}
} else {// 如果当前生成的是value
temp.append(curChar);
}
}
}
putKeyValueToMap(temp, isKey, key, map, true, charset);
return map;
}
/**
* 解析应答字符串,生成应答要素。
* 处理全渠道应答报文那种不带url编码又可能在value里成对出现括号且括号里带&和=的情况。
* 报文解析工具本身用不到,给验签的小工具用。
*
* @param str 需要解析的字符串
* @return 解析的结果map
* @throws UnsupportedEncodingException
*/
public static Map<String, String> parseRespString(String str) {
if (str == null || str.length() == 0 ) return new HashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
int len = str.length();
StringBuilder temp = new StringBuilder();
char curChar;
String key = null;
boolean isKey = true;
boolean isOpen = false;//值里有嵌套
char openName = 0;
for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串
curChar = str.charAt(i);// 取当前字符
if (isKey) {// 如果当前生成的是key
if (curChar == '=') {// 如果读取到=分隔符
key = temp.toString();
temp.setLength(0);
isKey = false;
} else {
temp.append(curChar);
}
} else {// 如果当前生成的是value
if(isOpen){
if(curChar == openName){
isOpen = false;
}
}else{//如果没开启嵌套
if(curChar == '{'){//如果碰到,就开启嵌套
isOpen = true;
openName ='}';
}
if(curChar == '['){
isOpen = true;
openName =']';
}
}
if (curChar == '&' && !isOpen) {// 如果读取到&分割符,同时这个分割符不是值域,这时将map里添加
putKeyValueToMap(temp, isKey, key, map, false, null);
temp.setLength(0);
isKey = true;
} else {
temp.append(curChar);
}
}
}
putKeyValueToMap(temp, isKey, key, map, false, null);
return map;
}
private static void putKeyValueToMap(StringBuilder temp, boolean isKey, String key, Map<String, String> map,
boolean decode, String charset) {
try {
if (decode) {
key = URLDecoder.decode(key, charset);
}
if (isKey) {
key = temp.toString();
map.put(key, "");
} else {
if (decode) {
String value = URLDecoder.decode(temp.toString(), charset);
map.put(key, value);
} else {
map.put(key, temp.toString());
}
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("编码有问题: " + charset);
}
}
/**
*
* 获取应答报文中的加密公钥证书,并存储到本地,并备份原始证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1。
*
* @param strCert
* @param certType
* @return
*/
public static int updateEncryptCert(String strCert, String certType ) {
if (isEmpty(strCert) || isEmpty(certType))
return -1;
if (CERTTYPE_01.equals(certType)) {
// 更新敏感信息加密公钥
return CertUtil.resetEncryptCertPublicKey(strCert);
} else if (CERTTYPE_02.equals(certType)) {
// 更新pin敏感信息加密公钥
return CertUtil.resetPinEncryptCertPublicKey(strCert);
} else {
log.info("unknown cerType:"+certType);
return -1;
}
}
/**
* 过滤请求报文中的空字符串或者空字符串
* @param contentData
* @return
*/
public static Map<String, String> filterBlank(Map<String, String> contentData){
Map<String, String> submitFromData = new HashMap<String, String>();
Set<String> keyset = contentData.keySet();
for(String key : keyset){
String value = contentData.get(key);
if (!isEmpty(value)) {
submitFromData.put(key, value.trim()); //不知道为啥一直有个trim,如果值里自带空格岂不是要出bug……但一直就这样,先 不管它吧。
}
}
return submitFromData;
}
/**
* 解压缩.
*
* @param inputByte
* byte[]数组类型的数据
* @return 解压缩后的数据
* @throws IOException
*/
public static byte[] inflater(final byte[] inputByte) throws IOException {
int compressedDataLength = 0;
Inflater compresser = new Inflater(false);
compresser.setInput(inputByte, 0, inputByte.length);
ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
byte[] result = new byte[1024];
try {
while (!compresser.finished()) {
compressedDataLength = compresser.inflate(result);
if (compressedDataLength == 0) {
break;
}
o.write(result, 0, compressedDataLength);
}
} catch (Exception ex) {
log.error("Data format error!", ex);
} finally {
o.close();
}
compresser.end();
return o.toByteArray();
}
/**
* 压缩.
*
* @param inputByte
* 需要解压缩的byte[]数组
* @return 压缩后的数据
* @throws IOException
*/
public static byte[] deflater(final byte[] inputByte) throws IOException {
int compressedDataLength = 0;
Deflater compresser = new Deflater();
compresser.setInput(inputByte);
compresser.finish();
ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
byte[] result = new byte[1024];
try {
while (!compresser.finished()) {
compressedDataLength = compresser.deflate(result);
o.write(result, 0, compressedDataLength);
}
} finally {
o.close();
}
compresser.end();
return o.toByteArray();
}
/**
* 判断字符串是否为NULL或空
*
* @param s
* 待判断的字符串数据
* @return 判断结果 true-是 false-否
*/
public static boolean isEmpty(String s) {
return null == s || "".equals(s.trim());
}
/**
*
* @param hexString
* @return
*/
public static byte[] hexStringToByteArray(String hexString){
try {
return Hex.decodeHex(hexString.toCharArray());
} catch (DecoderException e) {
log.error("非正常16进制字符串" + hexString, e);
return null;
}
}
/**
*
* @param bytes
* @return 16进制字符串,小写
*/
public static String byteArrayToHexString(byte[] bytes){
return new String(Hex.encodeHex(bytes, true));
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 报文加密解密等操作的工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.digests.SM3Digest;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.security.*;
import java.util.Arrays;
/**
*
* @ClassName SecureUtil
* @Description acpsdk安全算法工具类
* @date 2016-7-22 下午4:08:32
*/
@Slf4j
public class SecureUtil {
/**
* @param bytes
* @return
*/
public static byte[] sha1(byte[] bytes) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(bytes);
return messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
log.error("SHA1计算失败", e);
return null;
}
}
/**
* @param bytes
* @return
*/
public static byte[] sha256(byte[] bytes) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(bytes);
return messageDigest.digest();
} catch (Exception e) {
log.error("SHA256计算失败", e);
return null;
}
}
/**
* @param bytes
* @return
*/
public static byte[] sm3(byte[] bytes) {
SM3Digest sm3 = new SM3Digest();
sm3.update(bytes, 0, bytes.length);
byte[] result = new byte[sm3.getDigestSize()];
sm3.doFinal(result, 0);
return result;
}
public static byte[] getSignature(PrivateKey priKey, byte[] digest) {
byte[] mesDigest;
Signature sig;
try {
sig = Signature.getInstance("SHA1withRSA");
sig.initSign(priKey);
sig.update(digest);
mesDigest = sig.sign();
return mesDigest;
} catch (Exception e) {
log.error("签名计算失败", e);
return null;
}
}
public static byte[] getSignatureSHA256(PrivateKey priKey, byte[] digest) {
byte[] mesDigest;
Signature sig;
try {
sig = Signature.getInstance("SHA256withRSA");
sig.initSign(priKey);
sig.update(digest);
mesDigest = sig.sign();
return mesDigest;
} catch (Exception e) {
log.error("签名计算失败", e);
return null;
}
}
public static boolean verifySignature(PublicKey pubKey, byte[] digest, byte[] signature) {
try {
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(pubKey);
sig.update(digest);
boolean ok = sig.verify(signature);
return ok;
} catch (Exception e) {
log.error("验签异常", e);
return false;
}
}
public static boolean verifySignatureSHA256(PublicKey pubKey, byte[] digest, byte[] signature) {
if (pubKey == null || digest == null || signature == null) {
if(pubKey == null){
log.error("验签时pubKey传入了空值,验签失败");
} else if (digest == null){
log.error("验签时digest传入了空值,验签失败");
} else if (signature == null){
log.error("验签时signature传入了空值,验签失败");
} else {
log.error("验签时传入了空值,验签失败");
}
return false;
}
try {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(digest);
boolean ok = sig.verify(signature);
return ok;
} catch (Exception e) {
log.error("验签异常", e);
return false;
}
}
public static byte[] encrypt(Key key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("加密失败", e);
return null;
}
}
public static byte[] decrypt(Key Key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, Key);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("解密失败", e);
return null;
}
}
/**
* ANSIX9.8格式(带主账号信息)pinblock
* @param pan 卡号
* @param pin
* @return
*/
public static byte[] pinblock(String pan, String pin){
if(SDKUtil.isEmpty(pan) || SDKUtil.isEmpty(pin)){
log.error("卡号或pin为空,无法算pinblock。");
return null;
}
pan = pan.trim();
pin = pin.trim();
if (!pan.matches("^[0-9]{13,19}$")) {
log.error("卡号格式不对,无法算pinblock。");
return null;
}
if (!pin.matches("^[0-9]{4,6}$")) {
log.error("pin格式不对,无法算pinblock。");
return null;
}
pan = ("0000") + pan.substring(pan.length() - 13, pan.length() - 1);
int blockLen = 8;
try {
pin = "0" + pin.length() + pin;
byte[] pinbyte = Arrays.copyOf(Hex.decodeHex(pin.toCharArray()), blockLen);
Arrays.fill(pinbyte, pin.length()/2, blockLen, (byte)0xff);
byte[] panbyte = Hex.decodeHex(pan.toCharArray());
byte[] tempPin = new byte[blockLen];
for (int i = 0; i < blockLen; i++) {
tempPin[i] = (byte) (pinbyte[i] ^ panbyte[i]);
}
return tempPin;
} catch (Exception e){
log.error("pinblock计算异常啦……", e);
return null;
}
}
// /**
// * ANSI X9.8格式(不带主账号信息)pinblock
// * @param pin
// * @return
// */
// public static byte[] pinblock(String pin){
//
// if(SDKUtil.isEmpty(pin)){
// log.error("卡号或pin为空,无法算pinblock。");
// return null;
// }
// pin = pin.trim();
// if (!pin.matches("^[0-9]{4,6}$")) {
// log.error("pin格式不对,无法算pinblock。");
// return null;
// }
// int blockLen = 8;
// try {
// pin = "0" + pin.length() + pin;
// byte[] pinbyte = Arrays.copyOf(Hex.decodeHex(pin.toCharArray()), blockLen);
// Arrays.fill(pinbyte, pin.length()/2, blockLen, (byte)0xff);
// return pinbyte;
// } catch (Exception e){
// log.error("pinblock计算异常啦……", e);
// return null;
// }
// }
public static byte[] tripleDesEncryptECBPKCS5Padding(byte[] key, byte[] data) {
try {
if(data == null || data.length % 8 != 0)
throw new IllegalArgumentException("data is null or error data length.");
SecretKey sk = getTripleDesKey(key);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sk);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("加密失败", e);
return null;
}
}
/**
* 后补0到位数为unitLength的整数倍
* @param value
* @return
*/
public static byte[] rightPadZero(byte[] value, final int unitLength){
if (value.length % unitLength == 0)
return value;
int len = (value.length/unitLength + 1) * unitLength;
return Arrays.copyOf(value, len);
}
/**
* 通过byte数组得到SecretKey类型的密钥
* @param key
* @return
* @throws IllegalArgumentException
*/
private static SecretKey getTripleDesKey(byte[] key) {
if (key == null || !(key.length== 8||key.length== 16||key.length== 24))
throw new IllegalArgumentException("key is null or error key length.");
byte[] specKey = new byte[24];
try {
switch (key.length) {
case 16:
System.arraycopy(key, 0, specKey, 0, 16);
System.arraycopy(key, 0, specKey, 16, 8);
break;
case 8:
System.arraycopy(key, 0, specKey, 0, 8);
System.arraycopy(key, 0, specKey, 8, 8);
System.arraycopy(key, 0, specKey, 16, 8);
break;
case 24:
System.arraycopy(key, 0, specKey, 0, 24);
break;
default:
throw new IllegalArgumentException("error key length.");
}
DESedeKeySpec ks = new DESedeKeySpec(specKey);
SecretKey sk = SecretKeyFactory.getInstance("DESede")
.generateSecret(ks);
return sk;
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException("exception in 3des-ecb encryption", e);
}
}
}
【哪个sdk新就用哪个哦】【不完全向下兼容注意】
2020/9/29:
增加应答只需要关心200的post。
2020/6/23:
规范大改了,请参考最新规范进行修改。
1. 应答新增returnMsg,调整了应答码和交易状态相关字段。具体的值见规范。
目前使用的:
1)returnCode:代表此次交易请求的业务结果,查询交易表示查询操作的业务结果,具体交易结果,以交易应答码、交易状态码为准。
2)respCode:交易结果应答码。
3)xxxStatus:各类交易的状态
① transferStatus-转账状态,仅转账交易出现
② billStatus-账单状态,仅缴费交易用
③ entryStatus-入账状态,入账状态查询用
④ transStatus-消费/预授权交易的状态,仅消费和预授权查询和通知用
已删除的:resultCode、transCode、transMsg。
2. 应答新增merTransIndex,查询接口会原样应答,但目前并没有往发卡或者二维码的付款方送,大概没别的作用。
3. 应答新增preAuthId,没什么用,如果需要收单手工帮你处理预授权进行撤销或完成时可用提供他们,可方便他们处理。卡号+商户号+preAuthId在预授权超时或结束(完成或撤销)前是唯一的。
4. 新增respCode、transStatus取值TRANS_PRE_AUTH_COMPLETED,表示已被预授权完成。
5. 查询应答新增transStatus字段表示被查询的原交易的状态。
6. respCode和respMsg下沉到bizContent中.
7. traceNo和traceTime替换成清算主键settleKey,settleKey在收单和发卡的清算文件内是唯一的,可用于和收单对账,以及找银联和发卡查交易。
8. merCertId、cupCertId改为certId。
9. accessId:填写商户号。
10. accessType填写0。
11. 订单号、交易时间、商户代码从公共参数下沉到bizContent中。
12. 增加入账状态状态查询接口。
13. 转账交易termId从必填M改成可选O。
14. 两方转账也返回transferStatus。
2020/4/29:
6.0接口地址修正最终版,注意获取地址方式改getTransUrl,配置文件增加acpsdk.transUrl=https://gateway.test.95516.com/api/trans.do。
2020/4/4:
不完全向下兼容,注意对应修改,修改后的样例可参考assets/sdk测试类.
修改后:
全渠道5.0、5.1用AcpService。
全渠道6.0用Acp6Service。
二维码用QrcService。。
LogUtil删除,请改成直接调log4j或slf4j打印。
原二维码DemoBase中:
DemoBase.getAddnCond->QrcService.getKVBase64Field
DemoBase.formInfoBase64->QrcService.getKVBase64Field
DemoBase.getPayeeInfo->QrcService.getKVBase64Field
DemoBase.getPayeeInfoWithEncrpyt->QrcService.getKVEncBase64Field
DemoBase.getPayerInfo->QrcService.getKVBase64Field
DemoBase.getPayerInfoWithEncrpyt->QrcService.getKVEncBase64Field
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment