貌似最近銀聯出了什麼活動,公司急需接入銀聯支付, 作爲寫代碼的我們 , 只能服從命令,特地記錄下接入銀聯的過程 ,後續還會說接入支付寶和微信的親自體驗歷程....................................................................
1.申請商戶
首先得去銀聯那裏開通商戶,這個基本上是別人完成的 ,然後銀聯會給我們發一封郵件,裏面包含商戶號和生產環境的一些配置,這裏有一個注意的就是得使用IE瀏覽器安裝控件 , 谷歌是不行滴,,,,,,,附件裏面也有操作文檔 ,可以查看,就不多說了,補幾張過程圖吧
配置好後 下載一個.pfx的簽名文件,後面用到
2. 下載官方DEMO
去銀聯官網上下載官方的DEMO,解壓後找到JAVA的SDK,文件如圖
這裏面含有一些配置文件,在assets文件夾下
3.測試環境配置
下面進入代碼階段,先配置下測試環境,後面改成正式環境即可
文件的結構:
先貼出配置文件代碼,找到一個叫acp_sdk.properties的配置文件
##############SDK配置文件(證書方式簽名)################
# 說明:
# 1. 使用時請刪除後綴的“.證書”,並將此文件複製到src文件夾下替換原來的acp_sdk.properties。
# 2. 具體配置項請根據註釋修改。
#
################################################
#商戶號
acpsdk.merId=777290058110048
##########################入網測試環境交易發送地址(線上測試需要使用生產環境交易請求地址)#############################
##交易請求地址
#測試環境
acpsdk.frontTransUrl=https://gateway.test.95516.com/gateway/api/frontTransReq.do
acpsdk.backTransUrl=https://gateway.test.95516.com/gateway/api/backTransReq.do
acpsdk.singleQueryUrl=https://gateway.test.95516.com/gateway/api/queryTrans.do
acpsdk.batchTransUrl=https://gateway.test.95516.com/gateway/api/batchTrans.do
acpsdk.fileTransUrl=https://filedownload.test.95516.com/
acpsdk.appTransUrl=https://gateway.test.95516.com/gateway/api/appTransReq.do
acpsdk.cardTransUrl=https://gateway.test.95516.com/gateway/api/cardTransReq.do
#以下繳費產品使用,其餘產品用不到
#測試
acpsdk.jfFrontTransUrl=https://gateway.test.95516.com/jiaofei/api/frontTransReq.do
acpsdk.jfBackTransUrl=https://gateway.test.95516.com/jiaofei/api/backTransReq.do
acpsdk.jfSingleQueryUrl=https://gateway.test.95516.com/jiaofei/api/queryTrans.do
acpsdk.jfCardTransUrl=https://gateway.test.95516.com/jiaofei/api/cardTransReq.do
acpsdk.jfAppTransUrl=https://gateway.test.95516.com/jiaofei/api/appTransReq.do
########################################################################
# 報文版本號,固定5.1.0,請勿改動
acpsdk.version=5.1.0
# 簽名方式,證書方式固定01,請勿改動
acpsdk.signMethod=01
# 是否驗證驗簽證書的CN,測試環境請設置false,生產環境請設置true。非false的值默認都當true處理。
acpsdk.ifValidateCNName=false
# 是否驗證https證書,測試環境請設置false,生產環境建議優先嚐試true,不行再false。非true的值默認都當false處理。
acpsdk.ifValidateRemoteCert=false
#前臺通知地址,填寫後臺接收銀聯前臺通知的地址
acpsdk.frontUrl=https://XXXXXXXXx/union/successRedict
#後臺通知地址,填寫後臺接收銀聯後臺通知的地址,必須外網能訪問
#111.204.128.115:8929 ---> 192.168.0.54:8087
acpsdk.backUrl=https://XXXXXXXXx/union/backRcvResponse
#111.204.128.115:8917 ---> 192.168.0.54:8085
#acpsdk.backUrl=http://111.204.128.115:8917/garage/wx/pay/union/backRcvResponse
#111.204.128.115:8925 ---> 192.168.1.19:8087
#acpsdk.backUrl=http://111.204.128.115:8925/garageWechat/wx/pay/union/backRcvResponse
#########################入網測試環境簽名證書配置 ################################
# 多證書的情況證書路徑爲代碼指定,可不對此塊做配置。
# 簽名證書路徑,必須使用絕對路徑,如果不想使用絕對路徑,可以自行實現相對路徑獲取證書的方法;測試證書所有商戶共用開發包中的測試簽名證書,生產環境請從cfca下載得到。
# windows樣例:
acpsdk.signCert.path=cert/acp_test_sign.pfx
# linux樣例(注意:在linux下讀取證書需要保證證書有被應用讀的權限)(後續其他路徑配置也同此條說明)
#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/acp700000000000001.pfx
# 簽名證書密碼,測試環境固定000000,生產環境請修改爲從cfca下載的正式證書的密碼,正式環境證書密碼位數需小於等於6位,否則上傳到商戶服務網站會失敗
acpsdk.signCert.pwd=123456
# 簽名證書類型,固定不需要修改
acpsdk.signCert.type=PKCS12
##########################加密證書配置################################
# 敏感信息加密證書路徑(商戶號開通了商戶對敏感信息加密的權限,需要對 卡號accNo,pin和phoneNo,cvn2,expired加密(如果這些上送的話),對敏感信息加密使用)
acpsdk.encryptCert.path=cert/acp_test_enc.cer
##########################驗簽證書配置################################
# 驗籤中級證書路徑(銀聯提供)
acpsdk.middleCert.path=cert/acp_test_middle.cer
# 驗籤根證書路徑(銀聯提供)
acpsdk.rootCert.path=cert/acp_test_root.cer
其中測試環境的證書,在下載的DEMO中含有
配置文件到此基本上就已經完成了,下面貼出支付的代碼
@Override
public void pay(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding(DemoBase.encoding);
response.setContentType("text/html; charset="+ DemoBase.encoding);
String orderId = String.valueOf(System.currentTimeMillis()); //實際上是orderSn
String txnAmt = null; //訂單金額
String txnTime = null; //訂單發送時間
//交易金額,單位分,不要帶小數點
BigDecimal amount = new BigDecimal("0.01");
amount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);
txnAmt = amount.toString();
//訂單發送時間用於查詢用
Date sendPaymentDate = new Date();
txnTime = formatTime(sendPaymentDate);
Map<String, String> requestData = new HashMap<>();
/***銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改***/
requestData.put("version", DemoBase.version); //版本號,全渠道默認值
requestData.put("encoding", DemoBase.encoding); //字符集編碼,可以使用UTF-8,GBK兩種方式
requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //簽名方法
requestData.put("txnType", "01"); //交易類型 ,01:消費
requestData.put("txnSubType", "01"); //交易子類型, 01:自助消費
requestData.put("bizType", "000201"); //業務類型,B2C網關支付,手機wap支付
requestData.put("channelType", "08"); //渠道類型,這個字段區分B2C網關支付和手機wap支付;07:PC,平板 08:手機
/***商戶接入參數***/
requestData.put("merId", merId); //商戶號碼,請改成自己申請的正式商戶號或者open上註冊得來的777測試商戶號
requestData.put("accessType", "0"); //接入類型,0:直連商戶
requestData.put("orderId", String.valueOf(System.currentTimeMillis())); //商戶訂單號,8-40位數字字母,不能含“-”或“_”,可以自行定製規則
requestData.put("txnTime", formatTime(new Date())); //訂單發送時間,取系統時間,格式爲YYYYMMDDhhmmss,必須取當前時間,否則會報txnTime無效
requestData.put("currencyCode", "156"); //交易幣種(境內商戶一般是156 人民幣)
requestData.put("txnAmt", "1"); //交易金額,單位分,不要帶小數點
requestData.put("accType", "01"); //賬號類型 01:銀行卡02:存摺03:IC卡帳號類型(卡介質)
//前臺通知地址 (需設置爲外網能訪問 http https均可),支付成功後的頁面 點擊“返回商戶”按鈕的時候將異步通知報文post到該地址
//如果想要實現過幾秒中自動跳轉回商戶頁面權限,需聯繫銀聯業務申請開通自動返回商戶權限
//異步通知參數詳見open.unionpay.com幫助中心 下載 產品接口規範 網關支付產品接口規範 消費交易 商戶通知
// requestData.put("frontUrl", DemoBase.frontUrl);
//後臺通知地址(需設置爲【外網】能訪問 http https均可),支付成功後銀聯會自動將異步通知報文post到商戶上送的該地址,失敗的交易銀聯不會發送後臺通知
//後臺通知參數詳見open.unionpay.com幫助中心 下載 產品接口規範 網關支付產品接口規範 消費交易 商戶通知
//注意:1.需設置爲外網能訪問,否則收不到通知 2.http https均可 3.收單後臺通知後需要10秒內返回http200或302狀態碼
// 4.如果銀聯通知服務器發送通知後10秒內未收到返回狀態碼或者應答碼非http200,那麼銀聯會間隔一段時間再次發送。總共發送5次,每次的間隔時間爲0,1,2,4分鐘。
// 5.後臺通知地址如果上送了帶有?的參數,例如:http://abc/web?a=b&c=d 在後臺通知處理程序驗證簽名之前需要編寫邏輯將這些字段去掉再驗籤,否則將會驗籤失敗
requestData.put("backUrl", DemoBase.backUrl);
// 訂單超時時間。
// 超過此時間後,除網銀交易外,其他交易銀聯繫統會拒絕受理,提示超時。 跳轉銀行網銀交易如果超時後交易成功,會自動退款,大約5個工作日金額返還到持卡人賬戶。
// 此時間建議取支付時的北京時間加15分鐘。
// 超過超時時間調查詢接口應答origRespCode不是A6或者00的就可以判斷爲失敗。
// requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));
//////////////////////////////////////////////////
//
// 報文中特殊用法請查看 PCwap網關跳轉支付特殊用法.txt
//
//////////////////////////////////////////////////
/**請求參數設置完畢,以下對請求參數進行簽名並生成html表單,將表單寫入瀏覽器跳轉打開銀聯頁面**/
/* Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding); //報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl(); //獲取請求銀聯的前臺地址:對應屬性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding); //生成自動跳轉的Html表單*/
Map<String, String> reqData = AcpService.sign(requestData,DemoBase.encoding); //報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl(); //交易請求url從配置文件讀取對應屬性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = AcpService.post(reqData,requestAppUrl,DemoBase.encoding); //發送請求報文並接受同步應答(默認連接超時時間30秒,讀取返回結果超時時間30秒);這裏調用signData之後,調用submitUrl之前不能對submitFromData中的鍵值對做任何修改,如果修改會導致驗籤不通過
// LogUtil.writeLog("打印請求HTML,此爲請求報文,爲聯調排查問題的依據:"+html);
//將生成的html寫到瀏覽器中完成自動跳轉打開銀聯支付頁面;這裏調用signData之後,將html寫到瀏覽器跳轉到銀聯頁面之前均不能對html中的表單項的名稱和值進行修改,如果修改會導致驗籤不通過
/**對應答碼的處理,請根據您的業務邏輯來編寫程序,以下應答碼處理邏輯僅供參考------------->**/
//應答碼規範參考open.unionpay.com幫助中心 下載 產品接口規範 《平臺接入接口規範-第5部分-附錄》
if(!rspData.isEmpty()){
if(AcpService.validate(rspData, DemoBase.encoding)){
LogUtil.writeLog("驗證簽名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//成功,獲取tn號
String tn = rspData.get("tn");
System.out.println(tn);
//TODO
}else{
//其他應答碼爲失敗請排查原因或做失敗處理
//TODO
}
}else{
LogUtil.writeErrorLog("驗證簽名失敗");
//TODO 檢查驗證簽名失敗的原因
}
}else{
//未返回正確的http狀態
LogUtil.writeErrorLog("未獲取到返回報文或返回http狀態碼非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
response.getWriter().write("請求報文:<br/>"+reqMessage+"<br/>" + "應答報文:</br>"+rspMessage+"");
}
如果是手機控件支付的話 將獲取到的銀聯的訂單號tn之前返回給手機端即可,無需其他處理
測試商戶號在官網申請支付測試,會找到測試商戶號
也含有測試的銀行卡和手機號
至此,測試環境即可完成,調用接口,返回如下信息
大功告成!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4.正式環境配置
1.將請求連接換成正式請求連接
2.將配置文件中的某些false改成true
3.證書的修改,在下載的DEMo中會找到三個證書,還缺啥呢? 對就是缺少前面下載的那個.pfx結尾的文件,將下載的證書copy進來,然後將配置文件中的證書修改成正式的即可
4. 注意 注意 注意
本人用的是springboot項目 ,發佈時使用jar包發佈的,那麼用官方的包中的讀取配置文件方式是有問題的,所以需要修改讀取配置文件方式
修改後
這下問題是解決了,代碼 代碼 代碼 一個個貼出來
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wangfei on 2017/4/22.
*/
@Validated
@RestController
@RequestMapping("/union")
public class UnipayController {
@Autowired
private UnionPaymentService service;
/**
* 支付
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/pay", method = {RequestMethod.POST, RequestMethod.GET})
public void pay(HttpServletRequest request, HttpServletResponse response) throws IOException{
service.pay(request, response);
}
/**
* 後臺回調
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/backRcvResponse", method = {RequestMethod.GET, RequestMethod.POST})
public void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
service.backRcvResponse(request, response);
}
/**
* 前臺回調
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/frontRcvResponse", method = RequestMethod.POST)
public void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
service.frontRcvResponse(request, response);
}
/**
* 成功後跳轉
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/successRedict", method = RequestMethod.POST)
public void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException {
service.successRedict(request, response);
}
/**
* 查詢、檢查交易狀態
* @param request
* @param response
*/
@RequestMapping(value = "/query", method = RequestMethod.POST)
public void query(HttpServletRequest request, HttpServletResponse response) throws IOException {
service.query(request, response);
}
/**
* 交易狀態查詢
* @param orderId
*/
@RequestMapping(value = "/check", method = RequestMethod.POST)
public JsonResult check(Long orderId) {
try {
service.check(orderId);
return JsonResult.resultSuccess("已支付", "");
} catch (Exception e) {
e.printStackTrace();
return JsonResult.resultError("未發現支付信息");
}
}
}
package com.example.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface UnionPaymentService {
/**
* 支付
* @param request
* @param response
*/
void pay(HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 後臺回調
* @param request
* @param response
*/
void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 前臺回調
* @param request
* @param response
* @throws IOException
*/
void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
/**
* 交易狀態查詢
* @param request
* @param response
*/
void query(HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 支付成功後的跳轉
* @param request
* @param response
*/
void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 檢查支付結果
* @param shopOrderId
* @throws IOException
*/
void check(Long shopOrderId);
}
package com.example.demo;
import com.example.demo.union.DemoBase;
import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.service.AcpService;
import com.example.demo.union.util.CertUtil;
import com.example.demo.union.util.LogUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* Created by gangsun on 2017/4/12.
*/
@Service
public class UnionPaymentServiceImpl implements UnionPaymentService {
//先取商戶號
private String merId = "700000000000001";
private String redictUrl = "http://www.baidu.com";
@PostConstruct
public void init(){
System.out.println("銀聯支付初始化");
SDKConfig.getConfig().loadPropertiesFromSrc(); //從classpath加載acp_sdk.properties文件
CertUtil.init();
}
/**
* 支付
* @param request
* @param response
*/
@Override
public void pay(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding(DemoBase.encoding);
response.setContentType("text/html; charset="+ DemoBase.encoding);
String orderId = String.valueOf(System.currentTimeMillis()); //實際上是orderSn
String txnAmt = null; //訂單金額
String txnTime = null; //訂單發送時間
//交易金額,單位分,不要帶小數點
BigDecimal amount = new BigDecimal("0.01");
amount = amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);
txnAmt = amount.toString();
//訂單發送時間用於查詢用
Date sendPaymentDate = new Date();
txnTime = formatTime(sendPaymentDate);
Map<String, String> requestData = new HashMap<>();
/***銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改***/
requestData.put("version", DemoBase.version); //版本號,全渠道默認值
requestData.put("encoding", DemoBase.encoding); //字符集編碼,可以使用UTF-8,GBK兩種方式
requestData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //簽名方法
requestData.put("txnType", "01"); //交易類型 ,01:消費
requestData.put("txnSubType", "01"); //交易子類型, 01:自助消費
requestData.put("bizType", "000201"); //業務類型,B2C網關支付,手機wap支付
requestData.put("channelType", "08"); //渠道類型,這個字段區分B2C網關支付和手機wap支付;07:PC,平板 08:手機
/***商戶接入參數***/
requestData.put("merId", merId); //商戶號碼,請改成自己申請的正式商戶號或者open上註冊得來的777測試商戶號
requestData.put("accessType", "0"); //接入類型,0:直連商戶
requestData.put("orderId", String.valueOf(System.currentTimeMillis())); //商戶訂單號,8-40位數字字母,不能含“-”或“_”,可以自行定製規則
requestData.put("txnTime", formatTime(new Date())); //訂單發送時間,取系統時間,格式爲YYYYMMDDhhmmss,必須取當前時間,否則會報txnTime無效
requestData.put("currencyCode", "156"); //交易幣種(境內商戶一般是156 人民幣)
requestData.put("txnAmt", "1"); //交易金額,單位分,不要帶小數點
requestData.put("accType", "01"); //賬號類型 01:銀行卡02:存摺03:IC卡帳號類型(卡介質)
//前臺通知地址 (需設置爲外網能訪問 http https均可),支付成功後的頁面 點擊“返回商戶”按鈕的時候將異步通知報文post到該地址
//如果想要實現過幾秒中自動跳轉回商戶頁面權限,需聯繫銀聯業務申請開通自動返回商戶權限
//異步通知參數詳見open.unionpay.com幫助中心 下載 產品接口規範 網關支付產品接口規範 消費交易 商戶通知
// requestData.put("frontUrl", DemoBase.frontUrl);
//後臺通知地址(需設置爲【外網】能訪問 http https均可),支付成功後銀聯會自動將異步通知報文post到商戶上送的該地址,失敗的交易銀聯不會發送後臺通知
//後臺通知參數詳見open.unionpay.com幫助中心 下載 產品接口規範 網關支付產品接口規範 消費交易 商戶通知
//注意:1.需設置爲外網能訪問,否則收不到通知 2.http https均可 3.收單後臺通知後需要10秒內返回http200或302狀態碼
// 4.如果銀聯通知服務器發送通知後10秒內未收到返回狀態碼或者應答碼非http200,那麼銀聯會間隔一段時間再次發送。總共發送5次,每次的間隔時間爲0,1,2,4分鐘。
// 5.後臺通知地址如果上送了帶有?的參數,例如:http://abc/web?a=b&c=d 在後臺通知處理程序驗證簽名之前需要編寫邏輯將這些字段去掉再驗籤,否則將會驗籤失敗
requestData.put("backUrl", DemoBase.backUrl);
// 訂單超時時間。
// 超過此時間後,除網銀交易外,其他交易銀聯繫統會拒絕受理,提示超時。 跳轉銀行網銀交易如果超時後交易成功,會自動退款,大約5個工作日金額返還到持卡人賬戶。
// 此時間建議取支付時的北京時間加15分鐘。
// 超過超時時間調查詢接口應答origRespCode不是A6或者00的就可以判斷爲失敗。
// requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));
//////////////////////////////////////////////////
//
// 報文中特殊用法請查看 PCwap網關跳轉支付特殊用法.txt
//
//////////////////////////////////////////////////
/**請求參數設置完畢,以下對請求參數進行簽名並生成html表單,將表單寫入瀏覽器跳轉打開銀聯頁面**/
/* Map<String, String> submitFromData = AcpService.sign(requestData,DemoBase.encoding); //報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl(); //獲取請求銀聯的前臺地址:對應屬性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,DemoBase.encoding); //生成自動跳轉的Html表單*/
Map<String, String> reqData = AcpService.sign(requestData,DemoBase.encoding); //報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl(); //交易請求url從配置文件讀取對應屬性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = AcpService.post(reqData,requestAppUrl,DemoBase.encoding); //發送請求報文並接受同步應答(默認連接超時時間30秒,讀取返回結果超時時間30秒);這裏調用signData之後,調用submitUrl之前不能對submitFromData中的鍵值對做任何修改,如果修改會導致驗籤不通過
// LogUtil.writeLog("打印請求HTML,此爲請求報文,爲聯調排查問題的依據:"+html);
//將生成的html寫到瀏覽器中完成自動跳轉打開銀聯支付頁面;這裏調用signData之後,將html寫到瀏覽器跳轉到銀聯頁面之前均不能對html中的表單項的名稱和值進行修改,如果修改會導致驗籤不通過
/**對應答碼的處理,請根據您的業務邏輯來編寫程序,以下應答碼處理邏輯僅供參考------------->**/
//應答碼規範參考open.unionpay.com幫助中心 下載 產品接口規範 《平臺接入接口規範-第5部分-附錄》
if(!rspData.isEmpty()){
if(AcpService.validate(rspData, DemoBase.encoding)){
LogUtil.writeLog("驗證簽名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//成功,獲取tn號
String tn = rspData.get("tn");
System.out.println(tn);
//TODO
}else{
//其他應答碼爲失敗請排查原因或做失敗處理
//TODO
}
}else{
LogUtil.writeErrorLog("驗證簽名失敗");
//TODO 檢查驗證簽名失敗的原因
}
}else{
//未返回正確的http狀態
LogUtil.writeErrorLog("未獲取到返回報文或返回http狀態碼非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
response.getWriter().write("請求報文:<br/>"+reqMessage+"<br/>" + "應答報文:</br>"+rspMessage+"");
}
/**
* 後臺回調
* @param request
* @param response
*/
@Override
public void backRcvResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
LogUtil.writeLog("BackRcvResponse接收後臺通知開始");
String encoding = request.getParameter(SDKConstants.param_encoding);
// 獲取銀聯通知服務器發送的後臺通知參數
Map<String, String> reqParam = getAllRequestParam(request);
LogUtil.printRequestLog(reqParam);
Map<String, String> valideData = null;
if (null != reqParam && !reqParam.isEmpty()) {
Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
valideData = new HashMap<String, String>(reqParam.size());
while (it.hasNext()) {
Map.Entry<String, String> e = it.next();
String key = (String) e.getKey();
String value = (String) e.getValue();
value = new String(value.getBytes(encoding), encoding);
valideData.put(key, value);
}
}
//重要!驗證簽名前不要修改reqParam中的鍵值對的內容,否則會驗籤不過
if (!AcpService.validate(valideData, encoding)) {
LogUtil.writeLog("驗證簽名結果[失敗].");
//驗籤失敗,需解決驗籤問題
} else {
LogUtil.writeLog("驗證簽名結果[成功].");
//【注:爲了安全驗籤成功才應該寫商戶的成功處理邏輯】交易成功,更新商戶訂單狀態
String orderId =valideData.get("orderId"); //獲取後臺通知的數據,其他字段也可用類似方式獲取
String orderSn =orderId; //orderId其實存的是Sn
String respCode = valideData.get("respCode");
String txnAmt = valideData.get("txnAmt");
BigDecimal txnAmount = (new BigDecimal(txnAmt)).multiply(new BigDecimal(0.01));
String queryId = valideData.get("queryId");
String traceTime = valideData.get("traceTime");
String payCardNo = valideData.get("payCardNo");
String payCardType = valideData.get("payCardType"); //支付卡類型
String paymentMethodMethod; //PayPaymentMethod裏面的method字段
if(StringUtils.isEmpty(payCardType)){
paymentMethodMethod = "UNION"; //對之前代碼做兼容,如果沒有支付卡類型的情況走默認
}else{
paymentMethodMethod = "UNION-" + payCardType;
}
//判斷respCode=00、A6後,對涉及資金類的交易,請再發起查詢接口查詢,確定交易成功後更新數據庫。
if("00".equals(respCode)){ // 00 交易成功
//todo 若交易成功
}else if("A6".equals(respCode)){ // A6 部分成功
}
}
LogUtil.writeLog("BackRcvResponse接收後臺通知結束");
//返回給銀聯服務器http 200 狀態碼
response.getWriter().print("ok");
}
@Override
public void frontRcvResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LogUtil.writeLog("FrontRcvResponse前臺接收報文返回開始");
String encoding = request.getParameter(SDKConstants.param_encoding);
LogUtil.writeLog("返回報文中encoding=[" + encoding + "]");
String pageResult = "";
if (DemoBase.encoding.equalsIgnoreCase(encoding)) {
pageResult = "/utf8_result.jsp";
} else {
pageResult = "/gbk_result.jsp";
}
Map<String, String> respParam = getAllRequestParam(request);
// 打印請求報文
LogUtil.printRequestLog(respParam);
Map<String, String> valideData = null;
StringBuffer page = new StringBuffer();
if (null != respParam && !respParam.isEmpty()) {
Iterator<Map.Entry<String, String>> it = respParam.entrySet()
.iterator();
valideData = new HashMap<String, String>(respParam.size());
while (it.hasNext()) {
Map.Entry<String, String> e = it.next();
String key = (String) e.getKey();
String value = (String) e.getValue();
value = new String(value.getBytes(encoding), encoding);
page.append("<tr><td width=\"30%\" align=\"right\">" + key
+ "(" + key + ")</td><td>" + value + "</td></tr>");
valideData.put(key, value);
}
}
if (!AcpService.validate(valideData, encoding)) {
page.append("<tr><td width=\"30%\" align=\"right\">驗證簽名結果</td><td>失敗</td></tr>");
LogUtil.writeLog("驗證簽名結果[失敗].");
} else {
page.append("<tr><td width=\"30%\" align=\"right\">驗證簽名結果</td><td>成功</td></tr>");
LogUtil.writeLog("驗證簽名結果[成功].");
System.out.println(valideData.get("orderId")); //其他字段也可用類似方式獲取
String respCode = valideData.get("respCode");
//判斷respCode=00、A6後,對涉及資金類的交易,請再發起查詢接口查詢,確定交易成功後更新數據庫。
}
request.setAttribute("result", page.toString());
request.getRequestDispatcher(pageResult).forward(request, response);
LogUtil.writeLog("FrontRcvResponse前臺接收報文返回結束");
}
@Override
public void query(HttpServletRequest request, HttpServletResponse response) throws IOException {
String orderId = String.valueOf(System.currentTimeMillis()); //實際上是orderSn
String txnTime = null; //訂單發送時間
//訂單發送時間
Map<String, String> data = new HashMap<String, String>();
/***銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改***/
data.put("version", DemoBase.version); //版本號
data.put("encoding", DemoBase.encoding); //字符集編碼 可以使用UTF-8,GBK兩種方式
data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //簽名方法
data.put("txnType", "00"); //交易類型 00-默認
data.put("txnSubType", "00"); //交易子類型 默認00
data.put("bizType", "000201"); //業務類型 B2C網關支付,手機wap支付
/***商戶接入參數***/
data.put("merId", merId); //商戶號碼,請改成自己申請的商戶號或者open上註冊得來的777商戶號測試
data.put("accessType", "0"); //接入類型,商戶接入固定填0,不需修改
/***要調通交易以下字段必須修改***/
data.put("orderId", orderId); //****商戶訂單號,每次發交易測試需修改爲被查詢的交易的訂單號
data.put("txnTime", txnTime); //****訂單發送時間,每次發交易測試需修改爲被查詢的交易的訂單發送時間
/**請求參數設置完畢,以下對請求參數進行簽名併發送http post請求,接收同步應答報文------------->**/
Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);//報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
String url = SDKConfig.getConfig().getSingleQueryUrl();// 交易請求url從配置文件讀取對應屬性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
//這裏調用signData之後,調用submitUrl之前不能對submitFromData中的鍵值對做任何修改,如果修改會導致驗籤不通過
Map<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);
/**對應答碼的處理,請根據您的業務邏輯來編寫程序,以下應答碼處理邏輯僅供參考------------->**/
//應答碼規範參考open.unionpay.com幫助中心 下載 產品接口規範 《平臺接入接口規範-第5部分-附錄》
if(!rspData.isEmpty()){
if(AcpService.validate(rspData, DemoBase.encoding)){
LogUtil.writeLog("驗證簽名成功");
if("00".equals(rspData.get("respCode"))){//如果查詢交易成功
//處理被查詢交易的應答碼邏輯
String origRespCode = rspData.get("origRespCode");
if("00".equals(origRespCode)){
//交易成功,更新商戶訂單狀態
//TODO
}else if("03".equals(origRespCode) ||
"04".equals(origRespCode) ||
"05".equals(origRespCode)){
//需再次發起交易狀態查詢交易
//TODO
}else{
//其他應答碼爲失敗請排查原因
//TODO
}
}else{//查詢交易本身失敗,或者未查到原交易,檢查查詢交易報文要素
//TODO
}
}else{
LogUtil.writeErrorLog("驗證簽名失敗");
//TODO 檢查驗證簽名失敗的原因
}
}else{
//未返回正確的http狀態
LogUtil.writeErrorLog("未獲取到返回報文或返回http狀態碼非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
response.getWriter().write("</br>請求報文:<br/>"+reqMessage+"<br/>" + "應答報文:</br>"+rspMessage+"");
}
@Override
public void check(Long shopOrderId){
String orderId = String.valueOf(System.currentTimeMillis()); //實際上是orderSn
String txnTime = null; //訂單發送時間
Map<String, String> data = new HashMap<String, String>();
/***銀聯全渠道系統,產品參數,除了encoding自行選擇外其他不需修改***/
data.put("version", DemoBase.version); //版本號
data.put("encoding", DemoBase.encoding); //字符集編碼 可以使用UTF-8,GBK兩種方式
data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //簽名方法
data.put("txnType", "00"); //交易類型 00-默認
data.put("txnSubType", "00"); //交易子類型 默認00
data.put("bizType", "000201"); //業務類型 B2C網關支付,手機wap支付
/***商戶接入參數***/
data.put("merId", merId); //商戶號碼,請改成自己申請的商戶號或者open上註冊得來的777商戶號測試
data.put("accessType", "0"); //接入類型,商戶接入固定填0,不需修改
/***要調通交易以下字段必須修改***/
data.put("orderId", orderId); //****商戶訂單號,每次發交易測試需修改爲被查詢的交易的訂單號
data.put("txnTime", txnTime); //****訂單發送時間,每次發交易測試需修改爲被查詢的交易的訂單發送時間
/**請求參數設置完畢,以下對請求參數進行簽名併發送http post請求,接收同步應答報文------------->**/
Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);//報文中certId,signature的值是在signData方法中獲取並自動賦值的,只要證書配置正確即可。
LogUtil.writeLog("查詢請求數據: " + reqData.toString());
String url = SDKConfig.getConfig().getSingleQueryUrl();// 交易請求url從配置文件讀取對應屬性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
//這裏調用signData之後,調用submitUrl之前不能對submitFromData中的鍵值對做任何修改,如果修改會導致驗籤不通過
Map<String, String> rspData = AcpService.post(reqData,url,DemoBase.encoding);
LogUtil.writeLog("查詢響應數據: " + rspData.toString());
/**對應答碼的處理,請根據您的業務邏輯來編寫程序,以下應答碼處理邏輯僅供參考------------->**/
//應答碼規範參考open.unionpay.com幫助中心 下載 產品接口規範 《平臺接入接口規範-第5部分-附錄》
if(!rspData.isEmpty()){
if(AcpService.validate(rspData, DemoBase.encoding)){
LogUtil.writeLog("驗證簽名成功");
if("00".equals(rspData.get("respCode"))){//如果查詢交易成功
//處理被查詢交易的應答碼邏輯
String origRespCode = rspData.get("origRespCode");
if("00".equals(origRespCode)){
//交易成功,更新商戶訂單狀態
String txnAmt = rspData.get("txnAmt");
BigDecimal txnAmount = null;
if(!StringUtils.isEmpty(txnAmt)){
txnAmount = (new BigDecimal(txnAmt)).multiply(new BigDecimal(0.01)); //分轉換爲元;
}
String queryId = rspData.get("queryId");
String traceTime = rspData.get("traceTime");
String payCardNo = rspData.get("payCardNo");
String payCardType = rspData.get("payCardType"); //支付卡類型
String paymentMethodMethod; //PayPaymentMethod裏面的method字段
if(StringUtils.isEmpty(payCardType)){
paymentMethodMethod = "UNION"; //對之前代碼做兼容,如果沒有支付卡類型的情況走默認
}else{
paymentMethodMethod = "UNION-" + payCardType;
}
//更改支付狀態
//改變工單狀態
}else if("03".equals(origRespCode) ||
"04".equals(origRespCode) ||
"05".equals(origRespCode)){
//需再次發起交易狀態查詢交易
throw new RuntimeException("查詢結果:訂單號" + orderId + "交易失敗,應答碼爲“" + origRespCode + "”");
}else{
//其他應答碼爲失敗請排查原因
throw new RuntimeException("查詢結果:訂單號" + orderId + "交易失敗,應答碼爲“" + origRespCode + "”");
}
}else{//查詢交易本身失敗,或者未查到原交易,檢查查詢交易報文要素
throw new RuntimeException("查詢失敗");
}
}else{
LogUtil.writeErrorLog("驗證簽名失敗");
throw new RuntimeException("驗證簽名失敗");
//TODO 檢查驗證簽名失敗的原因
}
}else{
//未返回正確的http狀態
LogUtil.writeErrorLog("未獲取到返回報文或返回http狀態碼非200");
throw new RuntimeException("未獲取到返回報文或返回http狀態碼非200");
}
}
@Override
public void successRedict(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.sendRedirect(redictUrl);
}
//---------------------------------------------------- private -----------------------------------------------------
/**
* 獲取請求參數中所有的信息
*
* @param request
* @return
*/
private static Map<String, String> getAllRequestParam(final HttpServletRequest request) {
Map<String, String> res = new HashMap<String, String>();
Enumeration<?> temp = request.getParameterNames();
if (null != temp) {
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
res.put(en, value);
//在報文上送時,如果字段的值爲空,則不上送<下面的處理爲在獲取所有參數數據時,判斷若值爲空,則刪除這個字段>
//System.out.println("ServletUtil類247行 temp數據的鍵=="+en+" 值==="+value);
if (null == res.get(en) || "".equals(res.get(en))) {
res.remove(en);
}
}
}
return res;
}
//收費比率 精確到分保留兩位小數四捨五入
private BigDecimal getFeeAmount(BigDecimal amount, BigDecimal feeRatio, BigDecimal feeMax) {
BigDecimal fee = new BigDecimal(0);
if(null == amount || null == feeRatio) return fee;
//金額乘以費率 = 手續費
fee = amount.multiply(feeRatio);
//最大值爲feeMax
if(null != feeMax && feeMax.compareTo(new BigDecimal("0")) >= 0) fee = fee.max(feeMax);
//設置精確到分並四捨五入
fee = fee.setScale(4, BigDecimal.ROUND_HALF_UP);
return fee;
}
/**
* 時間格式化
* @param date
* @return
*/
private static String formatTime(Date date){
return new SimpleDateFormat("yyyyMMddHHmmss").format(date);
}
private static Date formatTime(String dateStr){
if(null == dateStr) return null;
if(dateStr.length() == 14){
try {
return new SimpleDateFormat("yyyyMMddHHmmss").parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}else{
try {
return new SimpleDateFormat("MMddHHmmss").parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
}
package com.example.demo;
import java.util.HashMap;
import java.util.Map;
/**
* Created by think on 2017/6/5.
*/
public class JsonResult {
private Boolean success = Boolean.valueOf(false);
private String msg = "";
private Object obj = null;
private Integer type = Integer.valueOf(0);
private Map<String, String> fieldErrors = new HashMap();
public JsonResult() {
}
public JsonResult(Boolean success, String msg) {
this.success = success;
this.msg = msg;
}
public JsonResult(Boolean success, String msg, Integer type) {
this.success = success;
this.msg = msg;
this.type = type;
}
public JsonResult(Boolean success, String msg, Object obj, Integer type) {
this.success = success;
this.msg = msg;
this.obj = obj;
this.type = type;
}
public Boolean getSuccess() {
return Boolean.valueOf(this.success.booleanValue() && this.fieldErrors.size() == 0);
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getMsg() {
return this.msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getObj() {
return this.obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Integer getType() {
return this.type;
}
public void setType(Integer type) {
this.type = type;
}
public void addFieldError(String field, String message) {
this.fieldErrors.put(field, message);
}
public void addFieldErrors(Map<String, String> fieldErrors) {
this.fieldErrors.putAll(fieldErrors);
}
public Map<String, String> getFieldErrors() {
return this.fieldErrors;
}
public static JsonResult resultError(String message) {
return resultError(message, 0);
}
public static JsonResult resultError(String message, int type) {
return new JsonResult(Boolean.valueOf(false), message, Integer.valueOf(0));
}
public static JsonResult resultSuccess(Object obj) {
return resultSuccess("", obj);
}
public static JsonResult resultSuccess(String message, Object obj) {
return resultSuccess(message, obj, 0);
}
public static JsonResult resultSuccess(String message, Object obj, int type) {
return new JsonResult(Boolean.valueOf(true), message, obj, Integer.valueOf(type));
}
}
package com.example.demo.union;
import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* 名稱: demo中用到的方法<br>
* 日期: 2015-09<br>
* 版本: 1.0.0
* 版權: 中國銀聯<br>
* 說明:以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己需要,按照技術文檔編寫。該代碼僅供參考。<br>
*/
public class DemoBase {
//默認配置的是UTF-8
public static String encoding = "UTF-8";
//全渠道固定值
public static String version = SDKConfig.getConfig().getVersion();
//後臺服務對應的寫法參照 FrontRcvResponse.java
public static String frontUrl = SDKConfig.getConfig().getFrontUrl();
//後臺服務對應的寫法參照 BackRcvResponse.java
public static String backUrl = SDKConfig.getConfig().getBackUrl();//受理方和髮卡方自選填寫的域[O]--後臺通知地址
// 商戶發送交易時間 格式:YYYYMMDDhhmmss
public static String getCurrentTime() {
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
}
// AN8..40 商戶訂單號,不能含"-"或"_"
public static String getOrderId() {
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
}
/**
* 組裝請求,返回報文字符串用於顯示
* @param data
* @return
*/
public static String genHtmlResult(Map<String, String> data){
TreeMap<String, String> tree = new TreeMap<String, String>();
Iterator<Entry<String, String>> it = data.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> en = it.next();
tree.put(en.getKey(), en.getValue());
}
it = tree.entrySet().iterator();
StringBuffer sf = new StringBuffer();
while (it.hasNext()) {
Entry<String, String> en = it.next();
String key = en.getKey();
String value = en.getValue();
if("respCode".equals(key)){
sf.append("<b>"+key + SDKConstants.EQUAL + value+"</br></b>");
}else
sf.append(key + SDKConstants.EQUAL + value+"</br>");
}
return sf.toString();
}
/**
* 功能:解析全渠道商戶對賬文件中的ZM文件並以List<Map>方式返回
* 適用交易:對賬文件下載後對文件的查看
* @param filePath ZM文件全路徑
* @return 包含每一筆交易中 序列號 和 值 的map序列
*/
public static List<Map> parseZMFile(String filePath){
int lengthArray[] = {3,11,11,6,10,19,12,4,2,21,2,32,2,6,10,13,13,4,15,2,2,6,2,4,32,1,21,15,1,15,32,13,13,8,32,13,13,12,2,1,32,98};
return parseFile(filePath,lengthArray);
}
/**
* 功能:解析全渠道商戶對賬文件中的ZME文件並以List<Map>方式返回
* 適用交易:對賬文件下載後對文件的查看
* @param filePath ZME文件全路徑
* @return 包含每一筆交易中 序列號 和 值 的map序列
*/
public static List<Map> parseZMEFile(String filePath){
int lengthArray[] = {3,11,11,6,10,19,12,4,2,2,6,10,4,12,13,13,15,15,1,12,2,135};
return parseFile(filePath,lengthArray);
}
/**
* 功能:解析全渠道商戶 ZM,ZME對賬文件
* @param filePath
* @param lengthArray 參照《全渠道平臺接入接口規範 第3部分 文件接口》 全渠道商戶對賬文件 6.1 ZM文件和6.2 ZME 文件 格式的類型長度組成int型數組
* @return
*/
private static List<Map> parseFile(String filePath,int lengthArray[]){
List<Map> ZmDataList = new ArrayList<Map>();
try {
String encoding="UTF-8";
File file=new File(filePath);
if(file.isFile() && file.exists()){ //判斷文件是否存在
InputStreamReader read = new InputStreamReader(
new FileInputStream(file),encoding);//考慮到編碼格式
BufferedReader bufferedReader = new BufferedReader(read);
String lineTxt = null;
while((lineTxt = bufferedReader.readLine()) != null){
//解析的結果MAP,key爲對賬文件列序號,value爲解析的值
Map<Integer,String> ZmDataMap = new LinkedHashMap<Integer,String>();
//左側遊標
int leftIndex = 0;
//右側遊標
int rightIndex = 0;
for(int i=0;i<lengthArray.length;i++){
rightIndex = leftIndex + lengthArray[i];
String filed = lineTxt.substring(leftIndex,rightIndex);
leftIndex = rightIndex+1;
ZmDataMap.put(i, filed);
}
ZmDataList.add(ZmDataMap);
}
read.close();
}else{
System.out.println("找不到指定的文件");
}
} catch (Exception e) {
System.out.println("讀取文件內容出錯");
e.printStackTrace();
}
return ZmDataList;
}
public static String getFileContentTable(List<Map> dataList,String file){
StringBuffer tableSb = new StringBuffer("對賬文件的規範參考 https://open.unionpay.com/ajweb/help/file/ 產品接口規範->平臺接口規範:文件接口</br> 文件【"+file + "】解析後內容如下:");
tableSb.append("<table border=\"1\">");
if(dataList.size() > 0){
Map<Integer,String> dataMapTmp = dataList.get(0);
tableSb.append("<tr>");
for(Iterator<Integer> it = dataMapTmp.keySet().iterator();it.hasNext();){
Integer key = it.next();
String value = dataMapTmp.get(key);
System.out.println("序號:"+ (key+1) + " 值: '"+ value +"'");
tableSb.append("<td>序號"+(key+1)+"</td>");
}
tableSb.append("</tr>");
}
for(int i=0;i<dataList.size();i++){
System.out.println("行數: "+ (i+1));
Map<Integer,String> dataMapTmp = dataList.get(i);
tableSb.append("<tr>");
for(Iterator<Integer> it = dataMapTmp.keySet().iterator();it.hasNext();){
Integer key = it.next();
String value = dataMapTmp.get(key);
System.out.println("序號:"+ (key+1) + " 值: '"+ value +"'");
tableSb.append("<td>"+value+"</td>");
}
tableSb.append("</tr>");
}
tableSb.append("</table>");
return tableSb.toString();
}
public static List<String> unzip(String zipFilePath,String outPutDirectory){
List<String> fileList = new ArrayList<String>();
try {
ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFilePath));//輸入源zip路徑
BufferedInputStream bin = new BufferedInputStream(zin);
BufferedOutputStream bout = null;
File file=null;
ZipEntry entry;
try {
while((entry = zin.getNextEntry())!=null && !entry.isDirectory()){
file = new File(outPutDirectory,entry.getName());
if(!file.exists()){
(new File(file.getParent())).mkdirs();
}
bout = new BufferedOutputStream(new FileOutputStream(file));
int b;
while((b=bin.read())!=-1){
bout.write(b);
}
bout.flush();
fileList.add(file.getAbsolutePath());
System.out.println(file+"解壓成功");
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bin.close();
zin.close();
if(bout!=null){
bout.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return fileList;
}
}
/**
*
* 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.example.demo.union.util;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.digests.SM3Digest;
import javax.crypto.Cipher;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
/**
*
* @ClassName SecureUtil
* @Description acpsdk安全算法工具類
* @date 2016-7-22 下午4:08:32
*
*/
public class SecureUtil {
/**
* 算法常量: SHA1
*/
private static final String ALGORITHM_SHA1 = "SHA-1";
/**
* 算法常量: SHA256
*/
private static final String ALGORITHM_SHA256 = "SHA-256";
/**
* 算法常量:SHA1withRSA
*/
private static final String BC_PROV_ALGORITHM_SHA1RSA = "SHA1withRSA";
/**
* 算法常量:SHA256withRSA
*/
private static final String BC_PROV_ALGORITHM_SHA256RSA = "SHA256withRSA";
/**
* sm3計算後進行16進制轉換
*
* @param data
* 待計算的數據
* @param encoding
* 編碼
* @return 計算結果
*/
public static String sm3X16Str(String data, String encoding) {
byte[] bytes = sm3(data, encoding);
StringBuilder sm3StrBuff = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {
sm3StrBuff.append("0").append(
Integer.toHexString(0xFF & bytes[i]));
} else {
sm3StrBuff.append(Integer.toHexString(0xFF & bytes[i]));
}
}
return sm3StrBuff.toString();
}
/**
* sha1計算後進行16進制轉換
*
* @param data
* 待計算的數據
* @param encoding
* 編碼
* @return 計算結果
*/
public static byte[] sha1X16(String data, String encoding) {
byte[] bytes = sha1(data, encoding);
StringBuilder sha1StrBuff = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {
sha1StrBuff.append("0").append(
Integer.toHexString(0xFF & bytes[i]));
} else {
sha1StrBuff.append(Integer.toHexString(0xFF & bytes[i]));
}
}
try {
return sha1StrBuff.toString().getBytes(encoding);
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return null;
}
}
/**
* sha256計算後進行16進制轉換
*
* @param data
* 待計算的數據
* @param encoding
* 編碼
* @return 計算結果
*/
public static String sha256X16Str(String data, String encoding) {
byte[] bytes = sha256(data, encoding);
StringBuilder sha256StrBuff = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {
sha256StrBuff.append("0").append(
Integer.toHexString(0xFF & bytes[i]));
} else {
sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i]));
}
}
return sha256StrBuff.toString();
}
/**
* sha256計算後進行16進制轉換
*
* @param data
* 待計算的數據
* @param encoding
* 編碼
* @return 計算結果
*/
public static byte[] sha256X16(String data, String encoding) {
byte[] bytes = sha256(data, encoding);
StringBuilder sha256StrBuff = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
if (Integer.toHexString(0xFF & bytes[i]).length() == 1) {
sha256StrBuff.append("0").append(
Integer.toHexString(0xFF & bytes[i]));
} else {
sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i]));
}
}
try {
return sha256StrBuff.toString().getBytes(encoding);
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return null;
}
}
/**
* sha1計算.
*
* @param data
* 待計算的數據
* @return 計算結果
*/
private static byte[] sha1(byte[] data) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(ALGORITHM_SHA1);
md.reset();
md.update(data);
return md.digest();
} catch (Exception e) {
LogUtil.writeErrorLog("SHA1計算失敗", e);
return null;
}
}
/**
* sha256計算.
*
* @param data
* 待計算的數據
* @return 計算結果
*/
private static byte[] sha256(byte[] data) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(ALGORITHM_SHA256);
md.reset();
md.update(data);
return md.digest();
} catch (Exception e) {
LogUtil.writeErrorLog("SHA256計算失敗", e);
return null;
}
}
/**
* SM3計算.
*
* @param data
* 待計算的數據
* @return 計算結果
*/
private static byte[] sm3(byte[] data) {
SM3Digest sm3 = new SM3Digest();
sm3.update(data, 0, data.length);
byte[] result = new byte[sm3.getDigestSize()];
sm3.doFinal(result, 0);
return result;
}
/**
* sha1計算
*
* @param datas
* 待計算的數據
* @param encoding
* 字符集編碼
* @return
*/
private static byte[] sha1(String datas, String encoding) {
try {
return sha1(datas.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog("SHA1計算失敗", e);
return null;
}
}
/**
* sha256計算
*
* @param datas
* 待計算的數據
* @param encoding
* 字符集編碼
* @return
*/
private static byte[] sha256(String datas, String encoding) {
try {
return sha256(datas.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog("SHA256計算失敗", e);
return null;
}
}
/**
* sm3計算
*
* @param datas
* 待計算的數據
* @param encoding
* 字符集編碼
* @return
*/
private static byte[] sm3(String datas, String encoding) {
try {
return sm3(datas.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog("SM3計算失敗", e);
return null;
}
}
/**
*
* @param privateKey
* @param data
* @return
* @throws Exception
*/
public static byte[] signBySoft(PrivateKey privateKey, byte[] data)
throws Exception {
byte[] result = null;
Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA, "BC");
st.initSign(privateKey);
st.update(data);
result = st.sign();
return result;
}
/**
* @param privateKey
* @param data
* @return
* @throws Exception
*/
public static byte[] signBySoft256(PrivateKey privateKey, byte[] data)
throws Exception {
byte[] result = null;
Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA256RSA, "BC");
st.initSign(privateKey);
st.update(data);
result = st.sign();
return result;
}
public static boolean validateSignBySoft(PublicKey publicKey,
byte[] signData, byte[] srcData) throws Exception {
Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA, "BC");
st.initVerify(publicKey);
st.update(srcData);
return st.verify(signData);
}
public static boolean validateSignBySoft256(PublicKey publicKey,
byte[] signData, byte[] srcData) throws Exception {
Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA256RSA, "BC");
st.initVerify(publicKey);
st.update(srcData);
return st.verify(signData);
}
/**
* 對數據通過公鑰進行加密,並進行base64計算
*
* @param dataString
* 待處理數據
* @param encoding
* 字符編碼
* @param key
* 公鑰
* @return
*/
public static String encryptData(String dataString, String encoding,
PublicKey key) {
/** 使用公鑰對密碼加密 **/
byte[] data = null;
try {
data = encryptData(key, dataString.getBytes(encoding));
return new String(SecureUtil.base64Encode(data), encoding);
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return "";
}
}
/**
* 對數據通過公鑰進行加密,並進行base64計算
*
* @param pin
* 待處理數據
* @param encoding
* 字符編碼
* @param key
* 公鑰
* @return
*/
public static String encryptPin(String accNo, String pin, String encoding,
PublicKey key) {
/** 使用公鑰對密碼加密 **/
byte[] data = null;
try {
data = pin2PinBlockWithCardNO(pin, accNo);
data = encryptData(key, data);
return new String(SecureUtil.base64Encode(data), encoding);
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return "";
}
}
/**
* 通過私鑰解密
*
* @param dataString
* base64過的數據
* @param encoding
* 編碼
* @param key
* 私鑰
* @return 解密後的數據
*/
public static String decryptData(String dataString, String encoding,
PrivateKey key) {
byte[] data = null;
try {
data = SecureUtil.base64Decode(dataString.getBytes(encoding));
data = decryptData(key, data);
return new String(data, encoding);
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return "";
}
}
/**
* BASE64解碼
*
* @param inputByte
* 待解碼數據
* @return 解碼後的數據
* @throws IOException
*/
public static byte[] base64Decode(byte[] inputByte) throws IOException {
return Base64.decodeBase64(inputByte);
}
/**
* BASE64編碼
*
* @param inputByte
* 待編碼數據
* @return 解碼後的數據
* @throws IOException
*/
public static byte[] base64Encode(byte[] inputByte) throws IOException {
return Base64.encodeBase64(inputByte);
}
/**
* 加密除pin之外的其他信息
*
* @param publicKey
* @param plainData
* @return
* @throws Exception
*/
private static byte[] encryptData(PublicKey publicKey, byte[] plainData)
throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plainData);
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* @param privateKey
* @param data
* @return
* @throws Exception
*/
private static byte[] decryptData(PrivateKey privateKey, byte[] data)
throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
} catch (Exception e) {
LogUtil.writeErrorLog("解密失敗", e);
}
return null;
}
/**
*
* @param aPin
* @return
*/
private static byte[] pin2PinBlock(String aPin) {
int tTemp = 1;
int tPinLen = aPin.length();
byte[] tByte = new byte[8];
try {
/*******************************************************************
* if (tPinLen > 9) { tByte[0] = (byte) Integer.parseInt(new
* Integer(tPinLen) .toString(), 16); } else { tByte[0] = (byte)
* Integer.parseInt(new Integer(tPinLen) .toString(), 10); }
******************************************************************/
// tByte[0] = (byte) Integer.parseInt(new Integer(tPinLen).toString(),
// 10);
tByte[0] = (byte) Integer.parseInt(Integer.toString(tPinLen), 10);
if (tPinLen % 2 == 0) {
for (int i = 0; i < tPinLen;) {
String a = aPin.substring(i, i + 2);
tByte[tTemp] = (byte) Integer.parseInt(a, 16);
if (i == (tPinLen - 2)) {
if (tTemp < 7) {
for (int x = (tTemp + 1); x < 8; x++) {
tByte[x] = (byte) 0xff;
}
}
}
tTemp++;
i = i + 2;
}
} else {
for (int i = 0; i < tPinLen - 1;) {
String a;
a = aPin.substring(i, i + 2);
tByte[tTemp] = (byte) Integer.parseInt(a, 16);
if (i == (tPinLen - 3)) {
String b = aPin.substring(tPinLen - 1) + "F";
tByte[tTemp + 1] = (byte) Integer.parseInt(b, 16);
if ((tTemp + 1) < 7) {
for (int x = (tTemp + 2); x < 8; x++) {
tByte[x] = (byte) 0xff;
}
}
}
tTemp++;
i = i + 2;
}
}
} catch (Exception e) {
}
return tByte;
}
/**
*
* @param aPan
* @return
*/
private static byte[] formatPan(String aPan) {
int tPanLen = aPan.length();
byte[] tByte = new byte[8];
;
int temp = tPanLen - 13;
try {
tByte[0] = (byte) 0x00;
tByte[1] = (byte) 0x00;
for (int i = 2; i < 8; i++) {
String a = aPan.substring(temp, temp + 2);
tByte[i] = (byte) Integer.parseInt(a, 16);
temp = temp + 2;
}
} catch (Exception e) {
}
return tByte;
}
/**
*
* @param aPin
* @param aCardNO
* @return
*/
private static byte[] pin2PinBlockWithCardNO(String aPin, String aCardNO) {
byte[] tPinByte = pin2PinBlock(aPin);
if (aCardNO.length() == 11) {
aCardNO = "00" + aCardNO;
} else if (aCardNO.length() == 12) {
aCardNO = "0" + aCardNO;
}
byte[] tPanByte = formatPan(aCardNO);
byte[] tByte = new byte[8];
for (int i = 0; i < 8; i++) {
tByte[i] = (byte) (tPinByte[i] ^ tPanByte[i]);
}
return tByte;
}
/**
* 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.example.demo.union.util;
import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.constants.SDKConstants;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import static com.example.demo.union.constants.SDKConstants.*;
/**
*
* @ClassName SDKUtil
* @Description acpsdk工具類
* @date 2016-7-22 下午4:06:18
*
*/
public class SDKUtil {
/**
* 根據signMethod的值,提供三種計算簽名的方法
*
* @param data
* 待簽名數據Map鍵值對形式
* @param encoding
* 編碼
* @return 簽名是否成功
*/
public static boolean sign(Map<String, String> data, String encoding) {
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(param_signMethod);
String version = data.get(SDKConstants.param_version);
if (!VERSION_1_0_0.equals(version) && !VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {
LogUtil.writeErrorLog("signMethod must Not null");
return false;
}
if (isEmpty(version)) {
LogUtil.writeErrorLog("version must Not null");
return false;
}
if (SIGNMETHOD_RSA.equals(signMethod)|| VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
if (VERSION_5_0_0.equals(version)|| VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
// 設置簽名證書序列號
data.put(SDKConstants.param_certId, CertUtil.getSignCertId());
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
byte[] byteSign = null;
String stringSign = null;
try {
// 通過SHA1進行摘要並轉16進制
byte[] signDigest = SecureUtil
.sha1X16(stringData, encoding);
byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(
CertUtil.getSignCertPrivateKey(), signDigest));
stringSign = new String(byteSign);
// 設置簽名域值
data.put(SDKConstants.param_signature, stringSign);
return true;
} catch (Exception e) {
LogUtil.writeErrorLog("Sign Error", e);
return false;
}
} else if (VERSION_5_1_0.equals(version)) {
// 設置簽名證書序列號
data.put(SDKConstants.param_certId, CertUtil.getSignCertId());
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
byte[] byteSign = null;
String stringSign = null;
try {
// 通過SHA256進行摘要並轉16進制
byte[] signDigest = SecureUtil
.sha256X16(stringData, encoding);
byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(
CertUtil.getSignCertPrivateKey(), signDigest));
stringSign = new String(byteSign);
// 設置簽名域值
data.put(SDKConstants.param_signature, stringSign);
return true;
} catch (Exception e) {
LogUtil.writeErrorLog("Sign Error", e);
return false;
}
}
} 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);
}
return false;
}
/**
* 通過傳入的證書絕對路徑和證書密碼讀取簽名證書進行簽名並返回簽名值<br>
*
* @param data
* 待簽名數據Map鍵值對形式
* @param encoding
* 編碼
* @param secureKey
* 證書絕對路徑
* @para certPwd
* 證書密碼
* @return 簽名值
*/
public static boolean signBySecureKey(Map<String, String> data, String secureKey,
String encoding) {
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
if (isEmpty(secureKey)) {
LogUtil.writeErrorLog("secureKey is empty");
return false;
}
String signMethod = data.get(param_signMethod);
if (isEmpty(signMethod)) {
LogUtil.writeErrorLog("signMethod must Not null");
return false;
}
if (SIGNMETHOD_SHA256.equals(signMethod)) {
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
String strBeforeSha256 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sha256X16Str(secureKey, encoding);
String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,
encoding);
// 設置簽名域值
data.put(SDKConstants.param_signature, strAfterSha256);
return true;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
String strBeforeSM3 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sm3X16Str(secureKey, encoding);
String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);
// 設置簽名域值
data.put(SDKConstants.param_signature, strAfterSM3);
return true;
}
return false;
}
/**
* 通過傳入的簽名密鑰進行簽名並返回簽名值<br>
*
* @param data
* 待簽名數據Map鍵值對形式
* @param encoding
* 編碼
* @param certPath
* 證書絕對路徑
* @param certPwd
* 證書密碼
* @return 簽名值
*/
public static boolean signByCertInfo(Map<String, String> data,
String certPath, String certPwd, String encoding) {
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
if (isEmpty(certPath) || isEmpty(certPwd)) {
LogUtil.writeErrorLog("CertPath or CertPwd is empty");
return false;
}
String signMethod = data.get(param_signMethod);
String version = data.get(SDKConstants.param_version);
if (!VERSION_1_0_0.equals(version) && !VERSION_5_0_1.equals(version) && isEmpty(signMethod)) {
LogUtil.writeErrorLog("signMethod must Not null");
return false;
}
if (isEmpty(version)) {
LogUtil.writeErrorLog("version must Not null");
return false;
}
if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
// 設置簽名證書序列號
data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
byte[] byteSign = null;
String stringSign = null;
try {
// 通過SHA1進行摘要並轉16進制
byte[] signDigest = SecureUtil
.sha1X16(stringData, encoding);
byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(
CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));
stringSign = new String(byteSign);
// 設置簽名域值
data.put(SDKConstants.param_signature, stringSign);
return true;
} catch (Exception e) {
LogUtil.writeErrorLog("Sign Error", e);
return false;
}
} else if (VERSION_5_1_0.equals(version)) {
// 設置簽名證書序列號
data.put(SDKConstants.param_certId, CertUtil.getCertIdByKeyStoreMap(certPath, certPwd));
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(data);
LogUtil.writeLog("待簽名請求報文串:[" + stringData + "]");
byte[] byteSign = null;
String stringSign = null;
try {
// 通過SHA256進行摘要並轉16進制
byte[] signDigest = SecureUtil
.sha256X16(stringData, encoding);
byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(
CertUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), signDigest));
stringSign = new String(byteSign);
// 設置簽名域值
data.put(SDKConstants.param_signature, stringSign);
return true;
} catch (Exception e) {
LogUtil.writeErrorLog("Sign Error", e);
return false;
}
}
}
return false;
}
/**
* 驗證簽名
*
* @param resData
* 返回報文數據
* @param encoding
* 編碼格式
* @return
*/
public static boolean validateBySecureKey(Map<String, String> resData, String secureKey, String encoding) {
LogUtil.writeLog("驗籤處理開始");
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = resData.get(SDKConstants.param_signMethod);
if (SIGNMETHOD_SHA256.equals(signMethod)) {
// 1.進行SHA256驗證
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
String strBeforeSha256 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sha256X16Str(secureKey, encoding);
String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,
encoding);
return stringSign.equals(strAfterSha256);
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
// 1.進行SM3驗證
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
String strBeforeSM3 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sm3X16Str(secureKey, encoding);
String strAfterSM3 = SecureUtil
.sm3X16Str(strBeforeSM3, encoding);
return stringSign.equals(strAfterSM3);
}
return false;
}
/**
* 驗證簽名
*
* @param resData
* 返回報文數據
* @param encoding
* 編碼格式
* @return
*/
public static boolean validate(Map<String, String> resData, String encoding) {
LogUtil.writeLog("驗籤處理開始");
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = resData.get(SDKConstants.param_signMethod);
String version = resData.get(SDKConstants.param_version);
if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
// 獲取返回報文的版本號
if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 從返回報文中獲取certId ,然後去證書靜態Map中查詢對應驗簽證書對象
String certId = resData.get(SDKConstants.param_certId);
LogUtil.writeLog("對返回報文串驗籤使用的驗籤公鑰序列號:["+certId+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
try {
// 驗證簽名需要用銀聯發給商戶的公鑰證書.
return SecureUtil.validateSignBySoft(CertUtil
.getValidatePublicKey(certId), SecureUtil
.base64Decode(stringSign.getBytes(encoding)),
SecureUtil.sha1X16(stringData, encoding));
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
} else if (VERSION_5_1_0.equals(version)) {
// 1.從返回報文中獲取公鑰信息轉換成公鑰對象
String strCert = resData.get(SDKConstants.param_signPubKeyCert);
// LogUtil.writeLog("驗籤公鑰證書:["+strCert+"]");
X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
if(x509Cert == null) {
LogUtil.writeErrorLog("convert signPubKeyCert failed");
return false;
}
// 2.驗證證書鏈
if (!CertUtil.verifyCertificate(x509Cert)) {
LogUtil.writeErrorLog("驗證公鑰證書失敗,證書信息:["+strCert+"]");
return false;
}
// 3.驗籤
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
try {
// 驗證簽名需要用銀聯發給商戶的公鑰證書.
boolean result = SecureUtil.validateSignBySoft256(x509Cert
.getPublicKey(), SecureUtil.base64Decode(stringSign
.getBytes(encoding)), SecureUtil.sha256X16(
stringData, encoding));
LogUtil.writeLog("驗證簽名" + (result? "成功":"失敗"));
return result;
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
}
} else if (SIGNMETHOD_SHA256.equals(signMethod)) {
// 1.進行SHA256驗證
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
String strBeforeSha256 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sha256X16Str(SDKConfig.getConfig()
.getSecureKey(), encoding);
String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,
encoding);
boolean result = stringSign.equals(strAfterSha256);
LogUtil.writeLog("驗證簽名" + (result? "成功":"失敗"));
return result;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
// 1.進行SM3驗證
String stringSign = resData.get(SDKConstants.param_signature);
LogUtil.writeLog("簽名原文:["+stringSign+"]");
// 將Map信息轉換成key1=value1&key2=value2的形式
String stringData = coverMap2String(resData);
LogUtil.writeLog("待驗籤返回報文串:["+stringData+"]");
String strBeforeSM3 = stringData
+ SDKConstants.AMPERSAND
+ SecureUtil.sm3X16Str(SDKConfig.getConfig()
.getSecureKey(), encoding);
String strAfterSM3 = SecureUtil
.sm3X16Str(strBeforeSM3, encoding);
boolean result = stringSign.equals(strAfterSM3);
LogUtil.writeLog("驗證簽名" + (result? "成功":"失敗"));
return result;
}
return false;
}
/**
* 將Map中的數據轉換成key1=value1&key2=value2的形式 不包含簽名域signature
*
* @param data
* 待拼接的Map數據
* @return 拼接好後的字符串
*/
public static String coverMap2String(Map<String, String> data) {
TreeMap<String, String> tree = new TreeMap<String, String>();
Iterator<Entry<String, String>> it = data.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> en = it.next();
if (SDKConstants.param_signature.equals(en.getKey().trim())) {
continue;
}
tree.put(en.getKey(), en.getValue());
}
it = tree.entrySet().iterator();
StringBuffer sf = new StringBuffer();
while (it.hasNext()) {
Entry<String, String> en = it.next();
sf.append(en.getKey() + SDKConstants.EQUAL + en.getValue()
+ SDKConstants.AMPERSAND);
}
return sf.substring(0, sf.length() - 1);
}
/**
* 兼容老方法 將形如key=value&key=value的字符串轉換爲相應的Map對象
*
* @param result
* @return
*/
public static Map<String, String> coverResultString2Map(String result) {
return convertResultStringToMap(result);
}
/**
* 將形如key=value&key=value的字符串轉換爲相應的Map對象
*
* @param result
* @return
*/
public static Map<String, String> convertResultStringToMap(String result) {
Map<String, String> map =null;
try {
if (StringUtils.isNotBlank(result)) {
if (result.startsWith("{") && result.endsWith("}")) {
result = result.substring(1, result.length() - 1);
}
map = parseQString(result);
}
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
return map;
}
/**
* 解析應答字符串,生成應答要素
*
* @param str
* 需要解析的字符串
* @return 解析的結果map
* @throws UnsupportedEncodingException
*/
public static Map<String, String> parseQString(String str)
throws UnsupportedEncodingException {
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;
if(len>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);
temp.setLength(0);
isKey = true;
}else{
temp.append(curChar);
}
}
}
putKeyValueToMap(temp, isKey, key, map);
}
return map;
}
private static void putKeyValueToMap(StringBuilder temp, boolean isKey,
String key, Map<String, String> map)
throws UnsupportedEncodingException {
if (isKey) {
key = temp.toString();
if (key.length() == 0) {
throw new RuntimeException("QString format illegal");
}
map.put(key, "");
} else {
if (key.length() == 0) {
throw new RuntimeException("QString format illegal");
}
map.put(key, temp.toString());
}
}
/**
*
* 獲取應答報文中的加密公鑰證書,並存儲到本地,並備份原始證書<br>
* 更新成功則返回1,無更新返回0,失敗異常返回-1。
*
* @param resData
* @param encoding
* @return
*/
public static int getEncryptCert(Map<String, String> resData,
String encoding) {
String strCert = resData.get(SDKConstants.param_encryptPubKeyCert);
String certType = resData.get(SDKConstants.param_certType);
if (isEmpty(strCert) || isEmpty(certType))
return -1;
X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
if (CERTTYPE_01.equals(certType)) {
// 更新敏感信息加密公鑰
if (!CertUtil.getEncryptCertId().equals(
x509Cert.getSerialNumber().toString())) {
// ID不同時進行本地證書更新操作
String localCertPath = SDKConfig.getConfig().getEncryptCertPath();
String newLocalCertPath = genBackupName(localCertPath);
// 1.將本地證書進行備份存儲
if (!copyFile(localCertPath, newLocalCertPath))
return -1;
// 2.備份成功,進行新證書的存儲
if (!writeFile(localCertPath, strCert, encoding))
return -1;
LogUtil.writeLog("save new encryptPubKeyCert success");
CertUtil.resetEncryptCertPublicKey();
return 1;
}else {
return 0;
}
} else if (CERTTYPE_02.equals(certType)) {
// // 更新磁道加密公鑰
// if (!CertUtil.getEncryptTrackCertId().equals(
// x509Cert.getSerialNumber().toString())) {
// // ID不同時進行本地證書更新操作
// String localCertPath = SDKConfig.getConfig().getEncryptTrackCertPath();
// String newLocalCertPath = genBackupName(localCertPath);
// // 1.將本地證書進行備份存儲
// if (!copyFile(localCertPath, newLocalCertPath))
// return -1;
// // 2.備份成功,進行新證書的存儲
// if (!writeFile(localCertPath, strCert, encoding))
// return -1;
// LogUtil.writeLog("save new encryptPubKeyCert success");
// CertUtil.resetEncryptTrackCertPublicKey();
// return 1;
// }else {
return 0;
// }
}else {
LogUtil.writeLog("unknown cerType:"+certType);
return -1;
}
}
/**
* 文件拷貝方法
*
* @param srcFile
* 源文件
* @param destFile
* 目標文件
* @return
* @throws IOException
*/
public static boolean copyFile(String srcFile, String destFile) {
boolean flag = false;
FileInputStream fin = null;
FileOutputStream fout = null;
FileChannel fcin = null;
FileChannel fcout = null;
try {
// 獲取源文件和目標文件的輸入輸出流
fin = new FileInputStream(srcFile);
fout = new FileOutputStream(destFile);
// 獲取輸入輸出通道
fcin = fin.getChannel();
fcout = fout.getChannel();
// 創建緩衝區
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
// clear方法重設緩衝區,使它可以接受讀入的數據
buffer.clear();
// 從輸入通道中將數據讀到緩衝區
int r = fcin.read(buffer);
// read方法返回讀取的字節數,可能爲零,如果該通道已到達流的末尾,則返回-1
if (r == -1) {
flag = true;
break;
}
// flip方法讓緩衝區可以將新讀入的數據寫入另一個通道
buffer.flip();
// 從輸出通道中將數據寫入緩衝區
fcout.write(buffer);
}
fout.flush();
} catch (IOException e) {
LogUtil.writeErrorLog("CopyFile fail", e);
} finally {
try {
if (null != fin)
fin.close();
if (null != fout)
fout.close();
if (null != fcin)
fcin.close();
if (null != fcout)
fcout.close();
} catch (IOException ex) {
LogUtil.writeErrorLog("Releases any system resources fail", ex);
}
}
return flag;
}
/**
* 寫文件方法
*
* @param filePath
* 文件路徑
* @param fileContent
* 文件內容
* @param encoding
* 編碼
* @return
*/
public static boolean writeFile(String filePath, String fileContent,
String encoding) {
FileOutputStream fout = null;
FileChannel fcout = null;
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
try {
fout = new FileOutputStream(filePath);
// 獲取輸出通道
fcout = fout.getChannel();
// 創建緩衝區
// ByteBuffer buffer = ByteBuffer.allocate(1024);
ByteBuffer buffer = ByteBuffer.wrap(fileContent.getBytes(encoding));
fcout.write(buffer);
fout.flush();
} catch (FileNotFoundException e) {
LogUtil.writeErrorLog("WriteFile fail", e);
return false;
} catch (IOException ex) {
LogUtil.writeErrorLog("WriteFile fail", ex);
return false;
} finally {
try {
if (null != fout)
fout.close();
if (null != fcout)
fcout.close();
} catch (IOException ex) {
LogUtil.writeErrorLog("Releases any system resources fail", ex);
return false;
}
}
return true;
}
/**
* 將傳入的文件名(xxx)改名 <br>
* 結果爲: xxx_backup.cer
*
* @param fileName
* @return
*/
public static String genBackupName(String fileName) {
if (isEmpty(fileName))
return "";
int i = fileName.lastIndexOf(POINT);
String leftFileName = fileName.substring(0, i);
String rightFileName = fileName.substring(i + 1);
String newFileName = leftFileName + "_backup" + POINT + rightFileName;
return newFileName;
}
public static byte[] readFileByNIO(String filePath) {
FileInputStream in = null;
FileChannel fc = null;
ByteBuffer bf = null;
try {
in = new FileInputStream(filePath);
fc = in.getChannel();
bf = ByteBuffer.allocate((int) fc.size());
fc.read(bf);
return bf.array();
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage());
return null;
} finally {
try {
if (null != fc) {
fc.close();
}
if (null != in) {
in.close();
}
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage());
return null;
}
}
}
/**
* 過濾請求報文中的空字符串或者空字符串
* @param contentData
* @return
*/
public static Map<String, String> filterBlank(Map<String, String> contentData){
LogUtil.writeLog("打印請求報文域 :");
Map<String, String> submitFromData = new HashMap<String, String>();
Set<String> keyset = contentData.keySet();
for(String key:keyset){
String value = contentData.get(key);
if (StringUtils.isNotBlank(value)) {
// 對value值進行去除前後空處理
submitFromData.put(key, value.trim());
LogUtil.writeLog(key + "-->" + String.valueOf(value));
}
}
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) {
System.err.println("Data format error!\n");
ex.printStackTrace();
} 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());
}
}
/**
*
* 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.example.demo.union.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/**
*
* @ClassName LogUtil
* @Description acpsdk日誌工具類
* @date 2016-7-22 下午4:04:35
*
*/
public class LogUtil {
private final static Logger GATELOG = LoggerFactory.getLogger("ACP_SDK_LOG");
private final static Logger GATELOG_ERROR = LoggerFactory.getLogger("SDK_ERR_LOG");
private final static Logger GATELOG_MESSAGE = LoggerFactory.getLogger("SDK_MSG_LOG");
final static String LOG_STRING_REQ_MSG_BEGIN = "============================== SDK REQ MSG BEGIN ==============================";
final static String LOG_STRING_REQ_MSG_END = "============================== SDK REQ MSG END ==============================";
final static String LOG_STRING_RSP_MSG_BEGIN = "============================== SDK RSP MSG BEGIN ==============================";
final static String LOG_STRING_RSP_MSG_END = "============================== SDK RSP MSG END ==============================";
/**
* 記錄普通日誌
*
* @param cont
*/
public static void writeLog(String cont) {
GATELOG.info(cont);
}
/**
* 記錄ERORR日誌
*
* @param cont
*/
public static void writeErrorLog(String cont) {
GATELOG_ERROR.error(cont);
}
/**
* 記錄ERROR日誌
*
* @param cont
* @param ex
*/
public static void writeErrorLog(String cont, Throwable ex) {
GATELOG_ERROR.error(cont, ex);
}
/**
* 記錄通信報文
*
* @param msg
*/
public static void writeMessage(String msg) {
GATELOG_MESSAGE.info(msg);
}
/**
* 打印請求報文
*
* @param reqParam
*/
public static void printRequestLog(Map<String, String> reqParam) {
writeMessage(LOG_STRING_REQ_MSG_BEGIN);
Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> en = it.next();
writeMessage("[" + en.getKey() + "] = [" + en.getValue() + "]");
}
writeMessage(LOG_STRING_REQ_MSG_END);
}
/**
* 打印響應報文.
*
* @param res
*/
public static void printResponseLog(String res) {
writeMessage(LOG_STRING_RSP_MSG_BEGIN);
writeMessage(res);
writeMessage(LOG_STRING_RSP_MSG_END);
}
/**
* debug方法
*
* @param cont
*/
public static void debug(String cont) {
if (GATELOG.isDebugEnabled()) {
GATELOG.debug(cont);
}
}
}
/**
*
* 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 HTTP通信工具類
* =============================================================================
*/
package com.example.demo.union.util;
import com.example.demo.union.config.SDKConfig;
import com.example.demo.union.service.BaseHttpSSLSocketFactory;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.*;
import java.util.Map;
import java.util.Map.Entry;
/**
*
* @ClassName HttpClient
* @Description acpsdk發送後臺http請求類
* @date 2016-7-22 下午4:03:25
*
*/
public class HttpClient {
/**
* 目標地址
*/
private URL url;
/**
* 通信連接超時時間
*/
private int connectionTimeout;
/**
* 通信讀超時時間
*/
private int readTimeOut;
/**
* 通信結果
*/
private String result;
/**
* 獲取通信結果
* @return
*/
public String getResult() {
return result;
}
/**
* 設置通信結果
* @param result
*/
public void setResult(String result) {
this.result = result;
}
/**
* 構造函數
* @param url 目標地址
* @param connectionTimeout HTTP連接超時時間
* @param readTimeOut HTTP讀寫超時時間
*/
public HttpClient(String url, int connectionTimeout, int readTimeOut) {
try {
this.url = new URL(url);
this.connectionTimeout = connectionTimeout;
this.readTimeOut = readTimeOut;
} catch (MalformedURLException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
}
/**
* 發送信息到服務端
* @param data
* @param encoding
* @return
* @throws Exception
*/
public int send(Map<String, String> data, String encoding) throws Exception {
try {
HttpURLConnection httpURLConnection = createConnection(encoding);
if (null == httpURLConnection) {
throw new Exception("Create httpURLConnection Failure");
}
String sendData = this.getRequestParamString(data, encoding);
LogUtil.writeLog("請求報文:[" + sendData + "]");
this.requestServer(httpURLConnection, sendData,
encoding);
this.result = this.response(httpURLConnection, encoding);
LogUtil.writeLog("Response message:[" + result + "]");
return httpURLConnection.getResponseCode();
} catch (Exception e) {
throw e;
}
}
/**
* 發送信息到服務端 GET方式
* @param encoding
* @return
* @throws Exception
*/
public int sendGet(String encoding) throws Exception {
try {
HttpURLConnection httpURLConnection = createConnectionGet(encoding);
if(null == httpURLConnection){
throw new Exception("創建聯接失敗");
}
this.result = this.response(httpURLConnection, encoding);
LogUtil.writeLog("同步返回報文:[" + result + "]");
return httpURLConnection.getResponseCode();
} catch (Exception e) {
throw e;
}
}
/**
* HTTP Post發送消息
*
* @param connection
* @param message
* @throws IOException
*/
private void requestServer(final URLConnection connection, String message, String encoder)
throws Exception {
PrintStream out = null;
try {
connection.connect();
out = new PrintStream(connection.getOutputStream(), false, encoder);
out.print(message);
out.flush();
} catch (Exception e) {
throw e;
} finally {
if (null != out) {
out.close();
}
}
}
/**
* 顯示Response消息
*
* @param connection
* @param encoding
* @return
* @throws URISyntaxException
* @throws IOException
*/
private String response(final HttpURLConnection connection, String encoding)
throws URISyntaxException, IOException, Exception {
InputStream in = null;
StringBuilder sb = new StringBuilder(1024);
BufferedReader br = null;
try {
if (200 == connection.getResponseCode()) {
in = connection.getInputStream();
sb.append(new String(read(in), encoding));
} else {
in = connection.getErrorStream();
sb.append(new String(read(in), encoding));
}
LogUtil.writeLog("HTTP Return Status-Code:["
+ connection.getResponseCode() + "]");
return sb.toString();
} catch (Exception e) {
throw e;
} finally {
if (null != br) {
br.close();
}
if (null != in) {
in.close();
}
if (null != connection) {
connection.disconnect();
}
}
}
public 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();
}
/**
* 創建連接
*
* @return
* @throws ProtocolException
*/
private HttpURLConnection createConnection(String encoding) throws ProtocolException {
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return null;
}
httpURLConnection.setConnectTimeout(this.connectionTimeout);// 連接超時時間
httpURLConnection.setReadTimeout(this.readTimeOut);// 讀取結果超時時間
httpURLConnection.setDoInput(true); // 可讀
httpURLConnection.setDoOutput(true); // 可寫
httpURLConnection.setUseCaches(false);// 取消緩存
httpURLConnection.setRequestProperty("Content-type",
"application/x-www-form-urlencoded;charset=" + encoding);
httpURLConnection.setRequestMethod("POST");
if ("https".equalsIgnoreCase(url.getProtocol())) {
HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;
//是否驗證https證書,測試環境請設置false,生產環境建議優先嚐試true,不行再false
if(!SDKConfig.getConfig().isIfValidateRemoteCert()){
husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory());
husn.setHostnameVerifier(new BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier());//解決由於服務器證書問題導致HTTPS無法訪問的情況
}
return husn;
}
return httpURLConnection;
}
/**
* 創建連接
*
* @return
* @throws ProtocolException
*/
private HttpURLConnection createConnectionGet(String encoding) throws ProtocolException {
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return null;
}
httpURLConnection.setConnectTimeout(this.connectionTimeout);// 連接超時時間
httpURLConnection.setReadTimeout(this.readTimeOut);// 讀取結果超時時間
httpURLConnection.setUseCaches(false);// 取消緩存
httpURLConnection.setRequestProperty("Content-type",
"application/x-www-form-urlencoded;charset=" + encoding);
httpURLConnection.setRequestMethod("GET");
if ("https".equalsIgnoreCase(url.getProtocol())) {
HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;
//是否驗證https證書,測試環境請設置false,生產環境建議優先嚐試true,不行再false
if(!SDKConfig.getConfig().isIfValidateRemoteCert()){
husn.setSSLSocketFactory(new BaseHttpSSLSocketFactory());
husn.setHostnameVerifier(new BaseHttpSSLSocketFactory.TrustAnyHostnameVerifier());//解決由於服務器證書問題導致HTTPS無法訪問的情況
}
return husn;
}
return httpURLConnection;
}
/**
* 將Map存儲的對象,轉換爲key=value&key=value的字符
*
* @param requestParam
* @param coder
* @return
*/
private String getRequestParamString(Map<String, String> requestParam, String coder) {
if (null == coder || "".equals(coder)) {
coder = "UTF-8";
}
StringBuffer sf = new StringBuffer("");
String reqstr = "";
if (null != requestParam && 0 != requestParam.size()) {
for (Entry<String, String> en : requestParam.entrySet()) {
try {
sf.append(en.getKey()
+ "="
+ (null == en.getValue() || "".equals(en.getValue()) ? "" : URLEncoder
.encode(en.getValue(), coder)) + "&");
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return "";
}
}
reqstr = sf.substring(0, sf.length() - 1);
}
LogUtil.writeLog("Request Message:[" + reqstr + "]");
return reqstr;
}
}
/**
*
* 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.bt.star.util;
import com.bt.star.config.SDKConfig;
import com.bt.star.properties.SDKConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
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;
/**
* @ClassName: CertUtil
* @Description: acpsdk證書工具類,主要用於對證書的加載和使用
* @date 2016-7-22 下午2:46:20
*
*/
@Slf4j
public class CertUtil {
/** 證書容器,存儲對商戶請求報文簽名私鑰證書. */
private static KeyStore keyStore = null;
/** 敏感信息加密公鑰證書 */
private static X509Certificate encryptCert = null;
/** 磁道加密公鑰 */
private static PublicKey encryptTrackKey = null;
/** 驗證銀聯返回報文簽名證書. */
private static X509Certificate validateCert = null;
/** 驗籤中級證書 */
private static X509Certificate middleCert = null;
/** 驗籤根證書 */
private static X509Certificate rootCert = null;
/** 驗證銀聯返回報文簽名的公鑰證書存儲Map. */
private static Map<String, X509Certificate> certMap = new HashMap<String, X509Certificate>();
/** 商戶私鑰存儲Map */
private final static Map<String, KeyStore> keyStoreMap = new ConcurrentHashMap<String, KeyStore>();
/**
* 初始化所有證書.
*/
public static void init() {
try {
addProvider();//向系統添加BC provider
initSignCert();//初始化簽名私鑰證書
initMiddleCert();//初始化驗簽證書的中級證書
initRootCert();//初始化驗簽證書的根證書
initEncryptCert();//初始化加密公鑰
initTrackKey();//構建磁道加密公鑰
initValidateCertFromDir();//初始化所有的驗簽證書
} catch (Exception e) {
log.error("init失敗。(如果是用對稱密鑰簽名的可無視此異常。)", e);
}
}
/**
* 添加簽名,驗籤,加密算法提供者
*/
private static void addProvider(){
if (Security.getProvider("BC") == null) {
log.info("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.info("re-add BC provider");
}
printSysInfo();
}
/**
* 用配置文件acp_sdk.properties中配置的私鑰路徑和密碼 加載簽名證書
*/
private static void initSignCert() {
if(!"01".equals(SDKConfig.getConfig().getSignMethod())){
log.info("非rsa簽名方式,不加載簽名證書。");
return;
}
if (SDKConfig.getConfig().getSignCertPath() == null
|| SDKConfig.getConfig().getSignCertPwd() == null
|| SDKConfig.getConfig().getSignCertType() == null) {
log.error("WARN: " + SDKConfig.SDK_SIGNCERT_PATH + "或" + SDKConfig.SDK_SIGNCERT_PWD
+ "或" + SDKConfig.SDK_SIGNCERT_TYPE + "爲空。 停止加載簽名證書。");
return;
}
if (null != keyStore) {
keyStore = null;
}
try {
keyStore = getKeyInfo(SDKConfig.getConfig().getSignCertPath(),
SDKConfig.getConfig().getSignCertPwd(), SDKConfig
.getConfig().getSignCertType());
log.info("InitSignCert Successful. CertId=["
+ getSignCertId() + "]");
} catch (IOException e) {
log.error("InitSignCert Error", e);
}
}
/**
* 用配置文件acp_sdk.properties配置路徑 加載敏感信息加密證書
*/
private static void initMiddleCert() {
log.info("加載中級證書==>"+SDKConfig.getConfig().getMiddleCertPath());
if (!SDKUtil.isEmpty(SDKConfig.getConfig().getMiddleCertPath())) {
middleCert = initCert(SDKConfig.getConfig().getMiddleCertPath());
log.info("Load MiddleCert Successful");
} else {
log.info("WARN: acpsdk.middle.path is empty");
}
}
/**
* 用配置文件acp_sdk.properties配置路徑 加載敏感信息加密證書
*/
private static void initRootCert() {
log.info("加載根證書==>"+SDKConfig.getConfig().getRootCertPath());
if (!SDKUtil.isEmpty(SDKConfig.getConfig().getRootCertPath())) {
rootCert = initCert(SDKConfig.getConfig().getRootCertPath());
log.info("Load RootCert Successful");
} else {
log.info("WARN: acpsdk.rootCert.path is empty");
}
}
/**
* 用配置文件acp_sdk.properties配置路徑 加載銀聯公鑰上級證書(中級證書)
*/
private static void initEncryptCert() {
log.info("加載敏感信息加密證書==>"+SDKConfig.getConfig().getEncryptCertPath());
if (!SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptCertPath())) {
encryptCert = initCert(SDKConfig.getConfig().getEncryptCertPath());
log.info("Load EncryptCert Successful");
} else {
log.info("WARN: acpsdk.encryptCert.path is empty");
}
}
/**
* 用配置文件acp_sdk.properties配置路徑 加載磁道公鑰
*/
private static void initTrackKey() {
if (!SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptTrackKeyModulus())
&& !SDKUtil.isEmpty(SDKConfig.getConfig().getEncryptTrackKeyExponent())) {
encryptTrackKey = getPublicKey(SDKConfig.getConfig().getEncryptTrackKeyModulus(),
SDKConfig.getConfig().getEncryptTrackKeyExponent());
log.info("LoadEncryptTrackKey Successful");
} else {
log.info("WARN: acpsdk.encryptTrackKey.modulus or acpsdk.encryptTrackKey.exponent is empty");
}
}
/**
* 用配置文件acp_sdk.properties配置路徑 加載驗證簽名證書
*/
private static void initValidateCertFromDir() {
if(!"01".equals(SDKConfig.getConfig().getSignMethod())){
log.info("非rsa簽名方式,不加載驗簽證書。");
return;
}
certMap.clear();
String dir = SDKConfig.getConfig().getValidateCertDir();
log.info("加載驗證簽名證書目錄==>" + dir +" 注:如果請求報文中version=5.1.0那麼此驗簽證書目錄使用不到,可以不需要設置(version=5.0.0必須設置)。");
if (SDKUtil.isEmpty(dir)) {
log.error("WARN: acpsdk.validateCert.dir is empty");
return;
}
CertificateFactory cf = null;
FileInputStream in = null;
try {
cf = CertificateFactory.getInstance("X.509", "BC");
File fileDir = new File(dir);
File[] files = fileDir.listFiles(new CerFilter());
for (int i = 0; i < files.length; i++) {
File file = files[i];
in = new FileInputStream(file.getAbsolutePath());
validateCert = (X509Certificate) cf.generateCertificate(in);
certMap.put(validateCert.getSerialNumber().toString(),
validateCert);
// 打印證書加載信息,供測試階段調試
log.info("[" + file.getAbsolutePath() + "][CertId="
+ validateCert.getSerialNumber().toString() + "]");
}
log.info("LoadVerifyCert Successful");
} catch (CertificateException e) {
log.error("LoadVerifyCert Error", e);
} catch (FileNotFoundException e) {
log.error("LoadVerifyCert Error File Not Found", e);
} catch (NoSuchProviderException e) {
log.error("LoadVerifyCert Error No BC Provider", e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
log.error(e.toString());
}
}
}
}
/**
* 用給定的路徑和密碼 加載簽名證書,並保存到certKeyStoreMap
*
* @param certFilePath
* @param certPwd
*/
private static void loadSignCert(String certFilePath, String certPwd) {
KeyStore keyStore = null;
try {
keyStore = getKeyInfo(certFilePath, certPwd, "PKCS12");
keyStoreMap.put(certFilePath, keyStore);
log.info("LoadRsaCert Successful");
} catch (IOException e) {
log.error("LoadRsaCert Error", e);
}
}
/**
* 通過證書路徑初始化爲公鑰證書
* @param path
* @return
*/
private static X509Certificate initCert(String path) {
X509Certificate encryptCertTemp = null;
CertificateFactory cf = null;
InputStream in = null;
//相對路徑變爲絕對路徑
try {
cf = CertificateFactory.getInstance("X.509", "BC");
Resource resource = new ClassPathResource(path);
in = resource.getInputStream();
encryptCertTemp = (X509Certificate) cf.generateCertificate(in);
// 打印證書加載信息,供測試階段調試
log.info("[" + path + "][CertId="
+ encryptCertTemp.getSerialNumber().toString() + "]");
} catch (CertificateException e) {
log.error("InitCert Error", e);
} catch (FileNotFoundException e) {
log.error("InitCert Error File Not Found", e);
} catch (NoSuchProviderException e) {
log.error("LoadVerifyCert Error No BC Provider", e);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
log.error(e.toString());
}
}
}
return encryptCertTemp;
}
/**
* 通過keyStore 獲取私鑰簽名證書PrivateKey對象
*
* @return
*/
public static PrivateKey getSignCertPrivateKey() {
try {
Enumeration<String> aliasenum = keyStore.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias,
SDKConfig.getConfig().getSignCertPwd().toCharArray());
return privateKey;
} catch (KeyStoreException e) {
log.error("getSignCertPrivateKey Error", e);
return null;
} catch (UnrecoverableKeyException e) {
log.error("getSignCertPrivateKey Error", e);
return null;
} catch (NoSuchAlgorithmException e) {
log.error("getSignCertPrivateKey Error", e);
return null;
}
}
/**
* 通過指定路徑的私鑰證書 獲取PrivateKey對象
*
* @return
*/
public static PrivateKey getSignCertPrivateKeyByStoreMap(String certPath,
String certPwd) {
if (!keyStoreMap.containsKey(certPath)) {
loadSignCert(certPath, certPwd);
}
try {
Enumeration<String> aliasenum = keyStoreMap.get(certPath)
.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
PrivateKey privateKey = (PrivateKey) keyStoreMap.get(certPath)
.getKey(keyAlias, certPwd.toCharArray());
return privateKey;
} catch (KeyStoreException e) {
log.error("getSignCertPrivateKeyByStoreMap Error", e);
return null;
} catch (UnrecoverableKeyException e) {
log.error("getSignCertPrivateKeyByStoreMap Error", e);
return null;
} catch (NoSuchAlgorithmException e) {
log.error("getSignCertPrivateKeyByStoreMap Error", e);
return null;
}
}
/**
* 獲取敏感信息加密證書PublicKey
*
* @return
*/
public static PublicKey getEncryptCertPublicKey() {
if (null == encryptCert) {
String path = SDKConfig.getConfig().getEncryptCertPath();
if (!SDKUtil.isEmpty(path)) {
encryptCert = initCert(path);
return encryptCert.getPublicKey();
} else {
log.error("acpsdk.encryptCert.path is empty");
return null;
}
} else {
return encryptCert.getPublicKey();
}
}
/**
* 重置敏感信息加密證書公鑰
*/
public static void resetEncryptCertPublicKey() {
encryptCert = null;
}
/**
* 獲取磁道加密證書PublicKey
*
* @return
*/
public static PublicKey getEncryptTrackPublicKey() {
if (null == encryptTrackKey) {
initTrackKey();
}
return encryptTrackKey;
}
/**
* 通過certId獲取驗簽證書Map中對應證書PublicKey
*
* @param certId 證書物理序號
* @return 通過證書編號獲取到的公鑰
*/
public static PublicKey getValidatePublicKey(String certId) {
X509Certificate cf = null;
if (certMap.containsKey(certId)) {
// 存在certId對應的證書對象
cf = certMap.get(certId);
return cf.getPublicKey();
} else {
// 不存在則重新Load證書文件目錄
initValidateCertFromDir();
if (certMap.containsKey(certId)) {
// 存在certId對應的證書對象
cf = certMap.get(certId);
return cf.getPublicKey();
} else {
log.error("缺少certId=[" + certId + "]對應的驗簽證書.");
return null;
}
}
}
/**
* 獲取配置文件acp_sdk.properties中配置的簽名私鑰證書certId
*
* @return 證書的物理編號
*/
public static String getSignCertId() {
try {
Enumeration<String> aliasenum = keyStore.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
X509Certificate cert = (X509Certificate) keyStore
.getCertificate(keyAlias);
return cert.getSerialNumber().toString();
} catch (Exception e) {
log.error("getSignCertId Error", e);
return null;
}
}
/**
* 獲取敏感信息加密證書的certId
*
* @return
*/
public static String getEncryptCertId() {
if (null == encryptCert) {
String path = SDKConfig.getConfig().getEncryptCertPath();
if (!SDKUtil.isEmpty(path)) {
encryptCert = initCert(path);
return encryptCert.getSerialNumber().toString();
} else {
log.error("acpsdk.encryptCert.path is empty");
return null;
}
} else {
return encryptCert.getSerialNumber().toString();
}
}
/**
* 將簽名私鑰證書文件讀取爲證書存儲對象
*
* @param pfxkeyfile
* 證書文件名
* @param keypwd
* 證書密碼
* @param type
* 證書類型
* @return 證書對象
* @throws IOException
*/
private static KeyStore getKeyInfo(String pfxkeyfile, String keypwd,
String type) throws IOException {
log.info("加載簽名證書==>" + pfxkeyfile);
InputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type, "BC");
log.info("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=["+ keypwd + "],type=["+type+"]");
//File file=new File(getRealCertPath() + pfxkeyfile);
Resource resource = new ClassPathResource(pfxkeyfile);
fis = resource.getInputStream();
char[] nPassword = null;
nPassword = null == keypwd || "".equals(keypwd.trim()) ? null: keypwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
return ks;
} catch (Exception e) {
log.error("getKeyInfo Error", e);
return null;
} finally {
if(null!=fis)
fis.close();
}
}
/**
* 通過簽名私鑰證書路徑,密碼獲取私鑰證書certId
* @param certPath
* @param certPwd
* @return
*/
public static String getCertIdByKeyStoreMap(String certPath, String certPwd) {
if (!keyStoreMap.containsKey(certPath)) {
// 緩存中未查詢到,則加載RSA證書
loadSignCert(certPath, certPwd);
}
return getCertIdIdByStore(keyStoreMap.get(certPath));
}
/**
* 通過keystore獲取私鑰證書的certId值
* @param keyStore
* @return
*/
private static String getCertIdIdByStore(KeyStore keyStore) {
Enumeration<String> aliasenum = null;
try {
aliasenum = keyStore.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
X509Certificate cert = (X509Certificate) keyStore
.getCertificate(keyAlias);
return cert.getSerialNumber().toString();
} catch (KeyStoreException e) {
log.error("getCertIdIdByStore Error", e);
return null;
}
}
/**
* 使用模和指數生成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
*/
public static X509Certificate getMiddleCert() {
if (null == middleCert) {
String path = SDKConfig.getConfig().getMiddleCertPath();
if (!SDKUtil.isEmpty(path)) {
initMiddleCert();
} else {
log.error(SDKConfig.SDK_MIDDLECERT_PATH + " not set in " + SDKConfig.FILE_NAME);
return null;
}
}
return middleCert;
}
/**
* 從配置文件acp_sdk.properties中獲取驗籤公鑰使用的根證書
* @return
*/
public static X509Certificate getRootCert() {
if (null == rootCert) {
String path = SDKConfig.getConfig().getRootCertPath();
if (!SDKUtil.isEmpty(path)) {
initRootCert();
} else {
log.error(SDKConfig.SDK_ROOTCERT_PATH + " not set in " + SDKConfig.FILE_NAME);
return null;
}
}
return rootCert;
}
/**
* 獲取證書的CN
* @param aCert
* @return
*/
private 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
*/
private static boolean verifyCertificateChain(X509Certificate cert){
if ( null == cert) {
log.error("cert must Not null");
return false;
}
X509Certificate middleCert = CertUtil.getMiddleCert();
if (null == middleCert) {
log.error("middleCert must Not null");
return false;
}
X509Certificate rootCert = CertUtil.getRootCert();
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 (CertPathBuilderException e){
log.error("verify certificate chain fail.", e);
} catch (Exception e) {
log.error("verify certificate chain exception: ", e);
}
return false;
}
/**
* 檢查證書鏈
*
* rootCerts
* 根證書
* @param cert
* 待驗證的證書
* @return
*/
public static boolean verifyCertificate(X509Certificate cert) {
if ( null == cert) {
log.error("cert must Not null");
return false;
}
try {
cert.checkValidity();//驗證有效期
// cert.verify(middleCert.getPublicKey());
if(!verifyCertificateChain(cert)){
return false;
}
} catch (Exception e) {
log.error("verifyCertificate fail", e);
return false;
}
if(SDKConfig.getConfig().isIfValidateCNName()){
// 驗證公鑰是否屬於銀聯
if(!SDKConstants.UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))) {
log.error("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));
return false;
}
} else {
// 驗證公鑰是否屬於銀聯
if(!SDKConstants.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) {
if (name.toLowerCase().endsWith(".cer")) {
return true;
} else {
return false;
}
}
public boolean accept(File dir, String name) {
return isCer(name);
}
}
private static String getRealCertPath(){
String clazzPath=Thread.currentThread().getContextClassLoader().getResource("").getPath();
clazzPath=clazzPath.replace("test-classes","classes");
clazzPath=clazzPath.replace("/classes/","/classes");
return clazzPath;
}
}
package com.example.demo.union.service;
import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.util.*;
import java.io.*;
import java.util.HashMap;
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;
/**
* @ClassName AcpService
* @Description acpsdk接口服務類,接入商戶集成請可以直接參考使用本類中的方法
* @date 2016-7-22 下午2:44:37
*/
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) {
reqData = SDKUtil.filterBlank(reqData);
SDKUtil.sign(reqData, encoding);
return reqData;
}
/**
* 同signByCertInfo<br>
* @param reqData
* @param certPath
* @param certPwd
* @param encoding
* @return
* @deprecated
*/
public static Map<String, String> sign(Map<String, String> reqData, String certPath,
String certPwd,String encoding) {
reqData = SDKUtil.filterBlank(reqData);
SDKUtil.signByCertInfo(reqData,certPath,certPwd,encoding);
return reqData;
}
/**
* 多證書籤名(通過傳入私鑰證書路徑和密碼簽名)<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) {
reqData = SDKUtil.filterBlank(reqData);
SDKUtil.signByCertInfo(reqData,certPath,certPwd,encoding);
return reqData;
}
/**
* 多密鑰簽名(通過傳入密鑰簽名)<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) {
reqData = SDKUtil.filterBlank(reqData);
SDKUtil.signBySecureKey(reqData, secureKey, encoding);
return reqData;
}
/**
* 驗證簽名(SHA-1摘要算法)<br>
* @param rspData 返回報文數據<br>
* @param encoding 上送請求報文域encoding字段的值<br>
* @return true 通過 false 未通過<br>
*/
public static boolean validate(Map<String, String> rspData, String encoding) {
return SDKUtil.validate(rspData, encoding);
}
/**
* 多密鑰驗籤(通過傳入密鑰簽名)<br>
* @param rspData 返回報文數據<br>
* @param encoding 上送請求報文域encoding字段的值<br>
* @return true 通過 false 未通過<br>
*/
public static boolean validateBySecureKey(Map<String, String> rspData, String secureKey, String encoding) {
return SDKUtil.validateBySecureKey(rspData, secureKey, encoding);
}
/**
* @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) {
LogUtil.writeLog("控件應答信息驗籤處理開始:[" + 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 SecureUtil.validateSignBySoft(CertUtil
.getValidatePublicKey(certId), SecureUtil.base64Decode(sign
.getBytes(encoding)), SecureUtil.sha1X16(data,
encoding));
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (Exception e) {
LogUtil.writeErrorLog(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) {
Map<String, String> rspData = new HashMap<String,String>();
LogUtil.writeLog("請求銀聯地址:" + reqUrl);
//發送後臺請求數據
HttpClient hc = new HttpClient(reqUrl, 30000, 30000);
try {
int status = hc.send(reqData, encoding);
if (200 == status) {
String resultString = hc.getResult();
if (null != resultString && !"".equals(resultString)) {
// 將返回結果轉換爲map
Map<String,String> tmpRspData = SDKUtil.convertResultStringToMap(resultString);
rspData.putAll(tmpRspData);
}
}else{
LogUtil.writeLog("返回http狀態碼["+status+"],請檢查請求報文或者請求地址是否正確");
}
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
return rspData;
}
/**
* 功能:http Get方法 便民繳費產品中使用<br>
* @param reqUrl 請求地址<br>
* @param encoding<br>
* @return
*/
public static String get(String reqUrl,String encoding) {
LogUtil.writeLog("請求銀聯地址:" + reqUrl);
//發送後臺請求數據
HttpClient hc = new HttpClient(reqUrl, 30000, 30000);
try {
int status = hc.sendGet(encoding);
if (200 == status) {
String resultString = hc.getResult();
if (null != resultString && !"".equals(resultString)) {
return resultString;
}
}else{
LogUtil.writeLog("返回http狀態碼["+status+"],請檢查請求報文或者請求地址是否正確");
}
} catch (Exception e) {
LogUtil.writeErrorLog(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>");
return sf.toString();
}
/**
* 功能:將批量文件內容使用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) {
LogUtil.writeErrorLog(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 = new String(SecureUtil.base64Encode(SDKUtil.deflater(s)),encoding);
}
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
}
}
return baseFileContent;
}
/**
* 功能:解析交易返回的fileContent字符串並落地 ( 解base64,解DEFLATE壓縮並落地)<br>
* 適用到的交易:對賬文件下載,批量交易狀態查詢<br>
* @param resData 返回報文map<br>
* @param fileDirectory 落地的文件目錄(絕對路徑)
* @param encoding 上送請求報文域encoding字段的值<br>
*/
public static String deCodeFileContent(Map<String, String> resData,String fileDirectory,String encoding) {
// 解析返回文件
String filePath = null;
String fileContent = resData.get(SDKConstants.param_fileContent);
if (null != fileContent && !"".equals(fileContent)) {
FileOutputStream out = null;
try {
byte[] fileArray = SDKUtil.inflater(SecureUtil
.base64Decode(fileContent.getBytes(encoding)));
if (SDKUtil.isEmpty(resData.get("fileName"))) {
filePath = fileDirectory + File.separator + resData.get("merId")
+ "_" + resData.get("batchNo") + "_"
+ resData.get("txnTime") + ".txt";
} else {
filePath = fileDirectory + File.separator + resData.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) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}finally{
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
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(SecureUtil.base64Decode(fileContent.getBytes())),encoding);
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(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", "1711"); //有效期 年在前月在後(不加密)<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())){
LogUtil.writeLog("送了密碼(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();
LogUtil.writeLog("組裝的customerInfo明文:"+customerInfo);
try {
return new String(SecureUtil.base64Encode(sf.toString().getBytes(
encoding)),encoding);
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(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", "1711"); //有效期 年在前月在後(加密) <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())){
LogUtil.writeLog("送了密碼(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);//去掉最後一個&符號
LogUtil.writeLog("組裝的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();
LogUtil.writeLog("組裝的customerInfo明文:"+customerInfo);
try {
return new String(SecureUtil.base64Encode(sf.toString().getBytes(encoding)),encoding);
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(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 = SecureUtil.base64Decode(customerInfo.getBytes(encoding));
String customerInfoNoBase64 = new String(b,encoding);
LogUtil.writeLog("解base64後===>" +customerInfoNoBase64);
//去掉前後的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length()-1);
customerInfoMap = SDKUtil.parseQString(customerInfoNoBase64);
if(customerInfoMap.containsKey("encryptedInfo")){
String encInfoStr = customerInfoMap.get("encryptedInfo");
customerInfoMap.remove("encryptedInfo");
String encryptedInfoStr = decryptData(encInfoStr, encoding);
Map<String,String> encryptedInfoMap = SDKUtil.parseQString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(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 = SecureUtil.base64Decode(customerInfo.getBytes(encoding));
String customerInfoNoBase64 = new String(b,encoding);
LogUtil.writeLog("解base64後===>" +customerInfoNoBase64);
//去掉前後的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length()-1);
customerInfoMap = SDKUtil.parseQString(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.parseQString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (UnsupportedEncodingException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(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) {
return SecureUtil.encryptPin(accNo, pin, encoding, CertUtil
.getEncryptCertPublicKey());
}
/**
* 敏感信息加密並做base64(卡號,手機號,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public static String encryptData(String data, String encoding) {
return SecureUtil.encryptData(data, encoding, CertUtil
.getEncryptCertPublicKey());
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密後的明文<br>
*/
public static String decryptData(String base64EncryptedInfo, String encoding) {
return SecureUtil.decryptData(base64EncryptedInfo, encoding, CertUtil
.getSignCertPrivateKey());
}
/**
* 敏感信息解密,通過傳入的私鑰解密<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 SecureUtil.decryptData(base64EncryptedInfo, encoding, CertUtil
.getSignCertPrivateKeyByStoreMap(certPath, certPwd));
}
/**
* 5.0.0加密磁道信息,5.1.0接口請勿使用<br>
* @param trackData 待加密磁道數據<br>
* @param encoding 編碼格式<br>
* @return 加密的密文<br>
* @deprecated
*/
public static String encryptTrack(String trackData, String encoding) {
return SecureUtil.encryptData(trackData, encoding,
CertUtil.getEncryptTrackPublicKey());
}
/**
* 獲取敏感信息加密證書的物理序列號<br>
* @return
*/
public static String getEncryptCertId(){
return CertUtil.getEncryptCertId();
}
/**
* 對字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr,String encoding) throws IOException{
byte [] rawByte = rawStr.getBytes(encoding);
return new String(SecureUtil.base64Encode(rawByte),encoding);
}
/**
* 對base64的字符串解base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str,String encoding) throws IOException{
byte [] rawByte = base64Str.getBytes(encoding);
return new String(SecureUtil.base64Decode(rawByte),encoding);
}
/**
*
* 有卡交易信息域(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.coverMap2String(cardTransDataMap))
.append(SDKConstants.RIGHT_BRACE).toString();
}
}
/**
* 獲取應答報文中的加密公鑰證書,並存儲到本地,備份原始證書,並自動替換證書<br>
* 更新成功則返回1,無更新返回0,失敗異常返回-1<br>
* @param resData 返回報文
* @param encoding
* @return
*/
public static int updateEncryptCert(Map<String, String> resData,
String encoding) {
return SDKUtil.getEncryptCert(resData, encoding);
}
/**
* 獲取
* @param number
* @return
*/
public static int genLuhn(String number){
return SecureUtil.genLuhn(number);
}
}
/**
*
* 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 SSLSocket 鏈接工具類(用於https)
* =============================================================================
*/
package com.example.demo.union.service;
import com.example.demo.union.util.LogUtil;
import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;
/**
*
* @ClassName BaseHttpSSLSocketFactory
* @Description 忽略驗證ssl證書
* @date 2016-7-22 下午4:10:14
*
*/
public class BaseHttpSSLSocketFactory extends SSLSocketFactory {
private SSLContext getSSLContext() {
return createEasySSLContext();
}
@Override
public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2,
int arg3) throws IOException {
return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
}
@Override
public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
}
@Override
public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
return getSSLContext().getSocketFactory().createSocket(arg0, arg1);
}
@Override
public Socket createSocket(String arg0, int arg1) throws IOException,
UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(arg0, arg1);
}
@Override
public String[] getSupportedCipherSuites() {
// TODO Auto-generated method stub
return null;
}
@Override
public String[] getDefaultCipherSuites() {
// TODO Auto-generated method stub
return null;
}
@Override
public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3)
throws IOException {
return getSSLContext().getSocketFactory().createSocket(arg0, arg1,
arg2, arg3);
}
private SSLContext createEasySSLContext() {
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null,
new TrustManager[] { MyX509TrustManager.manger }, null);
return context;
} catch (Exception e) {
LogUtil.writeErrorLog(e.getMessage(), e);
return null;
}
}
public static class MyX509TrustManager implements X509TrustManager {
static MyX509TrustManager manger = new MyX509TrustManager();
public MyX509TrustManager() {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
}
/**
* 解決由於服務器證書問題導致HTTPS無法訪問的情況 PS:HTTPS hostname wrong: should be <localhost>
*/
public static class TrustAnyHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
//直接返回true
return 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 MPI插件包常量定義
* =============================================================================
*/
package com.example.demo.union.constants;
/**
*
* @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 SIGNMETHOD_RSA = "01";
public static final String SIGNMETHOD_SHA256 = "11";
public static final String SIGNMETHOD_SM3 = "12";
public static final String UNIONPAY_CNNAME = "中國銀聯股份有限公司";
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";
}
/**
*
* 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.example.demo.union.config;
import com.example.demo.union.constants.SDKConstants;
import com.example.demo.union.util.LogUtil;
import com.example.demo.union.util.SDKUtil;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.util.Properties;
/**
*
* @ClassName SDKConfig
* @Description acpsdk配置文件acp_sdk.properties配置信息類
* @date 2016-7-22 下午4:04:55
*
*/
public class SDKConfig {
public static final String FILE_NAME = "properties/acp_sdk.properties";
/** 前臺請求URL. */
private String frontRequestUrl;
/** 後臺請求URL. */
private String backRequestUrl;
/** 單筆查詢 */
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;
/** 有卡交易. */
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;
/*繳費相關地址*/
private String jfFrontRequestUrl;
private String jfBackRequestUrl;
private String jfSingleQueryUrl;
private String jfCardRequestUrl;
private String jfAppRequestUrl;
private String qrcBackTransUrl;
private String qrcB2cIssBackTransUrl;
private String qrcB2cMerBackTransUrl;
/** 配置文件中的前臺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_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 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";
/** 配置文件中磁道加密公鑰模數常量. */
public static final String SDK_ENCRYPTTRACKKEY_MODULUS = "acpsdk.encryptTrackKey.modulus";
/** 配置文件中磁道加密公鑰指數常量. */
public static final String SDK_ENCRYPTTRACKKEY_EXPONENT = "acpsdk.encryptTrackKey.exponent";
/** 配置文件中驗證簽名證書目錄常量. */
public static final String SDK_VALIDATECERT_DIR = "acpsdk.validateCert.dir";
/** 配置文件中是否加密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";
/** 操作對象. */
private static SDKConfig config = new SDKConfig();
/** 屬性文件對象. */
private Properties properties;
private SDKConfig() {
super();
}
/**
* 獲取config對象.
* @return
*/
public static SDKConfig getConfig() {
return config;
}
/**
* 從properties文件加載
*
* @param rootPath
* 不包含文件名的目錄.
*/
public void loadPropertiesFromPath(String rootPath) {
if (StringUtils.isNotBlank(rootPath)) {
LogUtil.writeLog("從路徑讀取配置文件: " + rootPath+File.separator+FILE_NAME);
File file = new File(rootPath + File.separator + FILE_NAME);
InputStream in = null;
if (file.exists()) {
try {
in = new FileInputStream(file);
properties = new Properties();
properties.load(in);
loadProperties(properties);
} catch (FileNotFoundException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
}
}
} else {
// 由於此時可能還沒有完成LOG的加載,因此採用標準輸出來打印日誌信息
LogUtil.writeErrorLog(rootPath + FILE_NAME + "不存在,加載參數失敗");
}
} else {
loadPropertiesFromSrc();
}
}
/**
* 從classpath路徑下加載配置參數
*/
public void loadPropertiesFromSrc() {
InputStream in = null;
try {
LogUtil.writeLog("從classpath: " +SDKConfig.class.getClassLoader().getResource("").getPath()+" 獲取屬性文件"+FILE_NAME);
in = this.getClass().getClassLoader().getResourceAsStream(FILE_NAME);
if (null != in) {
properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
throw e;
}
} else {
LogUtil.writeErrorLog(FILE_NAME + "屬性文件未能在classpath指定的目錄下 "+SDKConfig.class.getClassLoader().getResource("").getPath()+" 找到!");
return;
}
loadProperties(properties);
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
LogUtil.writeErrorLog(e.getMessage(), e);
}
}
}
}
/**
* 根據傳入的 {@link #(Properties)}對象設置配置參數
*
* @param pro
*/
public void loadProperties(Properties pro) {
LogUtil.writeLog("開始從屬性文件中加載配置項");
String value = null;
value = pro.getProperty(SDK_SIGNCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.signCertPath = value.trim();
LogUtil.writeLog("配置項:私鑰簽名證書路徑==>"+SDK_SIGNCERT_PATH +"==>"+ value+" 已加載");
}
value = pro.getProperty(SDK_SIGNCERT_PWD);
if (!SDKUtil.isEmpty(value)) {
this.signCertPwd = value.trim();
LogUtil.writeLog("配置項:私鑰簽名證書密碼==>"+SDK_SIGNCERT_PWD +" 已加載");
}
value = pro.getProperty(SDK_SIGNCERT_TYPE);
if (!SDKUtil.isEmpty(value)) {
this.signCertType = value.trim();
LogUtil.writeLog("配置項:私鑰簽名證書類型==>"+SDK_SIGNCERT_TYPE +"==>"+ value+" 已加載");
}
value = pro.getProperty(SDK_ENCRYPTCERT_PATH);
if (!SDKUtil.isEmpty(value)) {
this.encryptCertPath = value.trim();
LogUtil.writeLog("配置項:敏感信息加密證書==>"+SDK_ENCRYPTCERT_PATH +"==>"+ value+" 已加載");
}
value = pro.getProperty(SDK_VALIDATECERT_DIR);
if (!SDKUtil.isEmpty(value)) {
this.validateCertDir = value.trim();
LogUtil.writeLog("配置項:驗證簽名證書路徑(這裏配置的是目錄,不要指定到公鑰文件)==>"+SDK_VALIDATECERT_DIR +"==>"+ 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_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(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();
}
}
public String getFrontRequestUrl() {
return frontRequestUrl;
}
public void setFrontRequestUrl(String frontRequestUrl) {
this.frontRequestUrl = frontRequestUrl;
}
public String getBackRequestUrl() {
return backRequestUrl;
}
public void setBackRequestUrl(String backRequestUrl) {
this.backRequestUrl = backRequestUrl;
}
public String getSignCertPath() {
return signCertPath;
}
public void setSignCertPath(String signCertPath) {
this.signCertPath = signCertPath;
}
public String getSignCertPwd() {
return signCertPwd;
}
public void setSignCertPwd(String signCertPwd) {
this.signCertPwd = signCertPwd;
}
public String getSignCertType() {
return signCertType;
}
public void setSignCertType(String signCertType) {
this.signCertType = signCertType;
}
public String getEncryptCertPath() {
return encryptCertPath;
}
public void setEncryptCertPath(String encryptCertPath) {
this.encryptCertPath = encryptCertPath;
}
public String getValidateCertDir() {
return validateCertDir;
}
public void setValidateCertDir(String validateCertDir) {
this.validateCertDir = validateCertDir;
}
public String getSingleQueryUrl() {
return singleQueryUrl;
}
public void setSingleQueryUrl(String singleQueryUrl) {
this.singleQueryUrl = singleQueryUrl;
}
public String getBatchQueryUrl() {
return batchQueryUrl;
}
public void setBatchQueryUrl(String batchQueryUrl) {
this.batchQueryUrl = batchQueryUrl;
}
public String getBatchTransUrl() {
return batchTransUrl;
}
public void setBatchTransUrl(String batchTransUrl) {
this.batchTransUrl = batchTransUrl;
}
public String getFileTransUrl() {
return fileTransUrl;
}
public void setFileTransUrl(String fileTransUrl) {
this.fileTransUrl = fileTransUrl;
}
public String getSignCertDir() {
return signCertDir;
}
public void setSignCertDir(String signCertDir) {
this.signCertDir = signCertDir;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public String getCardRequestUrl() {
return cardRequestUrl;
}
public void setCardRequestUrl(String cardRequestUrl) {
this.cardRequestUrl = cardRequestUrl;
}
public String getAppRequestUrl() {
return appRequestUrl;
}
public void setAppRequestUrl(String appRequestUrl) {
this.appRequestUrl = appRequestUrl;
}
public String getEncryptTrackCertPath() {
return encryptTrackCertPath;
}
public void setEncryptTrackCertPath(String encryptTrackCertPath) {
this.encryptTrackCertPath = encryptTrackCertPath;
}
public String getJfFrontRequestUrl() {
return jfFrontRequestUrl;
}
public void setJfFrontRequestUrl(String jfFrontRequestUrl) {
this.jfFrontRequestUrl = jfFrontRequestUrl;
}
public String getJfBackRequestUrl() {
return jfBackRequestUrl;
}
public void setJfBackRequestUrl(String jfBackRequestUrl) {
this.jfBackRequestUrl = jfBackRequestUrl;
}
public String getJfSingleQueryUrl() {
return jfSingleQueryUrl;
}
public void setJfSingleQueryUrl(String jfSingleQueryUrl) {
this.jfSingleQueryUrl = jfSingleQueryUrl;
}
public String getJfCardRequestUrl() {
return jfCardRequestUrl;
}
public void setJfCardRequestUrl(String jfCardRequestUrl) {
this.jfCardRequestUrl = jfCardRequestUrl;
}
public String getJfAppRequestUrl() {
return jfAppRequestUrl;
}
public void setJfAppRequestUrl(String jfAppRequestUrl) {
this.jfAppRequestUrl = jfAppRequestUrl;
}
public String getSingleMode() {
return singleMode;
}
public void setSingleMode(String singleMode) {
this.singleMode = singleMode;
}
public String getEncryptTrackKeyExponent() {
return encryptTrackKeyExponent;
}
public void setEncryptTrackKeyExponent(String encryptTrackKeyExponent) {
this.encryptTrackKeyExponent = encryptTrackKeyExponent;
}
public String getEncryptTrackKeyModulus() {
return encryptTrackKeyModulus;
}
public void setEncryptTrackKeyModulus(String encryptTrackKeyModulus) {
this.encryptTrackKeyModulus = encryptTrackKeyModulus;
}
public String getSecureKey() {
return secureKey;
}
public void setSecureKey(String securityKey) {
this.secureKey = securityKey;
}
public String getMiddleCertPath() {
return middleCertPath;
}
public void setMiddleCertPath(String middleCertPath) {
this.middleCertPath = middleCertPath;
}
public boolean isIfValidateCNName() {
return ifValidateCNName;
}
public void setIfValidateCNName(boolean ifValidateCNName) {
this.ifValidateCNName = ifValidateCNName;
}
public boolean isIfValidateRemoteCert() {
return ifValidateRemoteCert;
}
public void setIfValidateRemoteCert(boolean ifValidateRemoteCert) {
this.ifValidateRemoteCert = ifValidateRemoteCert;
}
public String getSignMethod() {
return signMethod;
}
public void setSignMethod(String signMethod) {
this.signMethod = signMethod;
}
public String getQrcBackTransUrl() {
return qrcBackTransUrl;
}
public void setQrcBackTransUrl(String qrcBackTransUrl) {
this.qrcBackTransUrl = qrcBackTransUrl;
}
public String getQrcB2cIssBackTransUrl() {
return qrcB2cIssBackTransUrl;
}
public void setQrcB2cIssBackTransUrl(String qrcB2cIssBackTransUrl) {
this.qrcB2cIssBackTransUrl = qrcB2cIssBackTransUrl;
}
public String getQrcB2cMerBackTransUrl() {
return qrcB2cMerBackTransUrl;
}
public void setQrcB2cMerBackTransUrl(String qrcB2cMerBackTransUrl) {
this.qrcB2cMerBackTransUrl = qrcB2cMerBackTransUrl;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getFrontUrl() {
return frontUrl;
}
public void setFrontUrl(String frontUrl) {
this.frontUrl = frontUrl;
}
public String getBackUrl() {
return backUrl;
}
public void setBackUrl(String backUrl) {
this.backUrl = backUrl;
}
public String getRootCertPath() {
return rootCertPath;
}
public void setRootCertPath(String rootCertPath) {
this.rootCertPath = rootCertPath;
}
}