支付宝证书公钥实现方式

This commit is contained in:
egzosn
2020-10-12 22:52:22 +08:00
parent c5604f5d3a
commit 708a7b16b6
17 changed files with 3371 additions and 170 deletions

View File

@@ -1,17 +1,31 @@
package com.egzosn.pay.ali.api;
import java.io.IOException;
import java.io.InputStream;
import com.egzosn.pay.ali.bean.CertEnvironment;
import com.egzosn.pay.common.api.BasePayConfigStorage;
import com.egzosn.pay.common.api.CertStore;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
/**
* 支付配置存储
*
* @author egan
* <p>
* email egzosn@gmail.com
* date 2016-5-18 14:09:01
* <p>
* email egzosn@gmail.com
* date 2016-5-18 14:09:01
*
*
* 以下证书签名相关触发前提是 {@link BasePayConfigStorage#isCertSign}等于true的情况。不然走的就是普通的方式
*/
public class AliPayConfigStorage extends BasePayConfigStorage {
/**
* ISV代商户代用指定appAuthToken
*/
private String appAuthToken;
/**
* 商户应用id
*/
@@ -27,6 +41,40 @@ public class AliPayConfigStorage extends BasePayConfigStorage {
private String seller;
/**
* 应用公钥证书
*/
private Object merchantCert;
/**
* 支付宝公钥证书
*/
private Object aliPayCert;
/**
* 支付宝CA证书根证书
*/
private Object aliPayRootCert;
/**
* 证书存储类型
*/
private CertStore certStoreType;
/**
* 证书信息
*/
private CertEnvironment certEnvironment;
public String getAppAuthToken() {
return appAuthToken;
}
public void setAppAuthToken(String appAuthToken) {
this.appAuthToken = appAuthToken;
}
public void setAppid(String appid) {
this.appid = appid;
}
@@ -55,5 +103,63 @@ public class AliPayConfigStorage extends BasePayConfigStorage {
this.seller = seller;
}
public Object getMerchantCert() {
return merchantCert;
}
public void setMerchantCert(Object merchantCert) {
this.merchantCert = merchantCert;
}
public Object getAliPayCert() {
return aliPayCert;
}
public void setAliPayCert(Object aliPayCert) {
this.aliPayCert = aliPayCert;
}
public Object getAliPayRootCert() {
return aliPayRootCert;
}
public void setAliPayRootCert(Object aliPayRootCert) {
this.aliPayRootCert = aliPayRootCert;
}
public CertStore getCertStoreType() {
return certStoreType;
}
public void setCertStoreType(CertStore certStoreType) {
this.certStoreType = certStoreType;
}
public CertEnvironment getCertEnvironment() {
return certEnvironment;
}
public void setCertEnvironment(CertEnvironment certEnvironment) {
this.certEnvironment = certEnvironment;
}
/**
* 初始化证书信息
*/
public void loadCertEnvironment() {
if (isCertSign() && null != this.certEnvironment){
return;
}
try (InputStream merchantCertStream = certStoreType.getInputStream(merchantCert);
InputStream aliPayCertStream = certStoreType.getInputStream(aliPayCert);
InputStream aliPayRootCertStream = certStoreType.getInputStream(aliPayRootCert);
){
this.certEnvironment = new CertEnvironment(merchantCertStream, aliPayCertStream, aliPayRootCertStream);
}
catch (IOException e) {
throw new PayErrorException(new PayException("读取证书异常", e.getMessage()));
}
}
}

View File

@@ -1,14 +1,40 @@
package com.egzosn.pay.ali.api;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import static com.egzosn.pay.ali.bean.AliPayConst.APP_AUTH_TOKEN;
import static com.egzosn.pay.ali.bean.AliPayConst.BIZ_CONTENT;
import static com.egzosn.pay.ali.bean.AliPayConst.CODE;
import static com.egzosn.pay.ali.bean.AliPayConst.HTTPS_REQ_URL;
import static com.egzosn.pay.ali.bean.AliPayConst.PASSBACK_PARAMS;
import static com.egzosn.pay.ali.bean.AliPayConst.PAYEE_INFO;
import static com.egzosn.pay.ali.bean.AliPayConst.PRODUCT_CODE;
import static com.egzosn.pay.ali.bean.AliPayConst.RETURN_URL;
import static com.egzosn.pay.ali.bean.AliPayConst.SIGN;
import static com.egzosn.pay.ali.bean.AliPayConst.SUCCESS_CODE;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.ali.bean.AliPayConst;
import com.egzosn.pay.ali.bean.AliPayMessage;
import com.egzosn.pay.ali.bean.AliRefundResult;
import com.egzosn.pay.ali.bean.AliTransactionType;
import com.egzosn.pay.ali.bean.AliTransferType;
import com.egzosn.pay.ali.bean.CertEnvironment;
import com.egzosn.pay.ali.bean.OrderSettle;
import com.egzosn.pay.common.api.BasePayService;
import com.egzosn.pay.common.bean.*;
import com.egzosn.pay.common.bean.MethodType;
import com.egzosn.pay.common.bean.Order;
import com.egzosn.pay.common.bean.PayMessage;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.common.bean.PayOutMessage;
import com.egzosn.pay.common.bean.RefundOrder;
import com.egzosn.pay.common.bean.TransactionType;
import com.egzosn.pay.common.bean.TransferOrder;
import com.egzosn.pay.common.bean.TransferType;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
import com.egzosn.pay.common.http.HttpConfigStorage;
@@ -18,57 +44,17 @@ import com.egzosn.pay.common.util.Util;
import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.common.util.str.StringUtils;
import java.util.*;
/**
* 支付宝支付服务
*
* @author egan
* <p>
* email egzosn@gmail.com
* date 2017-2-22 20:09
* <p>
* email egzosn@gmail.com
* date 2017-2-22 20:09
*/
public class AliPayService extends BasePayService<AliPayConfigStorage> {
/**
* 正式测试环境
*/
private static final String HTTPS_REQ_URL = "https://openapi.alipay.com/gateway.do";
/**
* 沙箱测试环境账号
*/
private static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do";
private static final String SIGN = "sign";
private static final String SUCCESS_CODE = "10000";
private static final String CODE = "code";
/**
* 附加参数
*/
private static final String PASSBACK_PARAMS = "passback_params";
/**
* 产品代码
*/
private static final String PRODUCT_CODE = "product_code";
/**
* 返回地址
*/
private static final String RETURN_URL = "return_url";
/**
* 请求内容
*/
private static final String BIZ_CONTENT = "biz_content";
/**
* 应用授权概述
*/
private static final String APP_AUTH_TOKEN = "app_auth_token";
/**
* 收款方信息
*/
private static final String PAYEE_INFO = "payee_info";
/**
* 获取对应的请求地址
@@ -77,8 +63,9 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
*/
@Override
public String getReqUrl(TransactionType transactionType) {
return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL;
return payConfigStorage.isTest() ? AliPayConst.DEV_REQ_URL : HTTPS_REQ_URL;
}
/**
* 获取对应的请求地址
*
@@ -89,12 +76,25 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
}
/**
* 设置支付配置
*
* @param payConfigStorage 支付配置
*/
@Override
public AliPayService setPayConfigStorage(AliPayConfigStorage payConfigStorage) {
payConfigStorage.loadCertEnvironment();
super.setPayConfigStorage(payConfigStorage);
return this;
}
public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) {
super(payConfigStorage, configStorage);
}
public AliPayService(AliPayConfigStorage payConfigStorage) {
super(payConfigStorage);
this(payConfigStorage, null);
}
@@ -131,18 +131,37 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
if (SIGN.equals(entry.getKey())) {
continue;
}
TreeMap<String, Object> response = new TreeMap((Map<String, Object> )entry.getValue());
TreeMap<String, Object> response = new TreeMap((Map<String, Object>) entry.getValue());
LinkedHashMap<Object, Object> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put(CODE, response.remove(CODE));
linkedHashMap.put("msg", response.remove("msg"));
linkedHashMap.putAll(response);
return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset());
return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, getKeyPublic(params), payConfigStorage.getInputCharset());
}
}
return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset());
return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, getKeyPublic(params), payConfigStorage.getInputCharset());
}
/**
* 获取公钥信息
* @param params 响应参数
* @return 公钥信息
*/
private String getKeyPublic(Map<String, Object> params){
if (!payConfigStorage.isCertSign()){
return payConfigStorage.getKeyPublic();
}
return payConfigStorage.getCertEnvironment().getAliPayPublicKey(getAliPayCertSN(params));
}
/**
* 从响应Map中提取支付宝公钥证书序列号
*
* @param respMap 响应Map
* @return 支付宝公钥证书序列号
*/
public String getAliPayCertSN(java.util.Map<String, Object> respMap){
return (String) respMap.get(AliPayConst.ALIPAY_CERT_SN_FIELD);
}
/**
* 校验数据来源
@@ -200,6 +219,7 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
orderInfo.put("notify_url", payConfigStorage.getNotifyUrl());
orderInfo.put("format", "json");
setAppAuthToken(orderInfo, order.getAttrs());
Map<String, Object> bizContent = new TreeMap<>();
@@ -240,9 +260,11 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
if (null != order.getExpirationTime()) {
bizContent.put(order.getTransactionType() == AliTransactionType.SWEEPPAY ? "qr_code_timeout_express" : "timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m");
}
bizContent.putAll(order.getAttrs());
orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
return preOrderHandler(orderInfo, order);
return preOrderHandler(orderInfo, order);
}
/**
@@ -258,9 +280,22 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
orderInfo.put("charset", payConfigStorage.getInputCharset());
orderInfo.put("timestamp", DateUtils.format(new Date()));
orderInfo.put("version", "1.0");
loadCertSn(orderInfo);
return orderInfo;
}
/**
* 加载证书序列
* @param orderInfo 订单信息
*/
private void loadCertSn(Map<String, Object> orderInfo){
if (payConfigStorage.isCertSign()){
final CertEnvironment certEnvironment = payConfigStorage.getCertEnvironment();
setParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN());
setParameters(orderInfo, "alipay_root_cert_sn", certEnvironment.getRootCertSN());
}
}
/**
* 获取输出消息,用户返回给支付端
@@ -308,7 +343,7 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
String bizContent = (String) orderInfo.remove(BIZ_CONTENT);
formHtml.append(getReqUrl()).append("?").append(UriVariables.getMapToParameters(orderInfo))
.append("\" method=\"").append(method.name().toLowerCase()).append("\">");
formHtml.append("<input type=\"hidden\" name=\"biz_content\" value=\'" ).append( bizContent ).append( "\'/>");
formHtml.append("<input type=\"hidden\" name=\"biz_content\" value=\'").append(bizContent).append("\'/>");
formHtml.append("</form>");
formHtml.append("<script>document.forms['_alipaysubmit_'].submit();</script>");
@@ -316,7 +351,6 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
}
/**
* 获取输出二维码信息,
*
@@ -324,7 +358,7 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
* @return 返回二维码信息,,支付时需要的
*/
@Override
public String getQrPay(PayOrder order){
public String getQrPay(PayOrder order) {
order.setTransactionType(AliTransactionType.SWEEPPAY);
Map<String, Object> orderInfo = orderInfo(order);
//预订单
@@ -345,9 +379,9 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
*/
@Override
public Map<String, Object> microPay(PayOrder order) {
if (null == order.getTransactionType()){
if (null == order.getTransactionType()) {
order.setTransactionType(AliTransactionType.BAR_CODE);
}else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE){
} else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE) {
throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType()));
}
@@ -364,10 +398,11 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
/**
* 统一收单交易结算接口
*
* @param order 交易结算信息
* @return 结算结果
*/
public Map<String, Object> settle(OrderSettle order){
public Map<String, Object> settle(OrderSettle order) {
//获取公共参数
Map<String, Object> parameters = getPublicParameters(AliTransactionType.SETTLE);
setAppAuthToken(parameters, order.getAttrs());
@@ -422,13 +457,24 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
/**
* 设置支付宝授权Token
*
* @param parameters 参数
* @param attrs 订单属性
* @param attrs 订单属性
* @return 参数
*/
private void setAppAuthToken(Map<String, Object> parameters, Map<String, Object> attrs) {
setAppAuthToken(parameters);
setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN));
}
/**
* 设置支付宝授权Token
*
* @param parameters 参数
* @return 参数
*/
private void setAppAuthToken(Map<String, Object> parameters) {
setParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken());
}
/**
@@ -458,7 +504,6 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
}
/**
* 查询退款
*
@@ -507,7 +552,6 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
}
/**
* @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请
* 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException}
@@ -558,11 +602,11 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
bizContent.put("out_biz_no", order.getOutNo());
bizContent.put("trans_amount", order.getAmount());
transferType.setAttr(bizContent, order);
setParameters(bizContent, "order_title", order);
setParameters(bizContent, "original_order_id", order);
setParameters(bizContent, "order_title", order);
setParameters(bizContent, "original_order_id", order);
setPayeeInfo(bizContent, order);
bizContent.put("remark", order.getRemark());
setParameters(bizContent, "business_params", order);
setParameters(bizContent, "business_params", order);
//设置请求参数的集合
parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
@@ -571,24 +615,23 @@ public class AliPayService extends BasePayService<AliPayConfigStorage> {
return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class);
}
private Map<String, Object> setPayeeInfo(Map<String, Object> bizContent, Order order){
private Map<String, Object> setPayeeInfo(Map<String, Object> bizContent, Order order) {
final Object attr = order.getAttr(PAYEE_INFO);
if (attr instanceof String){
if (attr instanceof String) {
bizContent.put(PAYEE_INFO, attr);
}
if (attr instanceof TreeMap){
if (attr instanceof TreeMap) {
bizContent.put(PAYEE_INFO, attr);
}
if (attr instanceof Map){
Map<String, Object> payeeInfo = new TreeMap<String, Object>((Map)attr);
if (attr instanceof Map) {
Map<String, Object> payeeInfo = new TreeMap<String, Object>((Map) attr);
bizContent.put(PAYEE_INFO, payeeInfo);
}
return bizContent;
}
/**
* 转账查询
*

View File

@@ -0,0 +1,59 @@
package com.egzosn.pay.ali.bean;
/**
* 常量
* @author Egan
* <pre>
* email egzosn@gmail.com
* date 2020/10/8
* </pre>
*/
public final class AliPayConst {
private AliPayConst() {
}
/**
* 正式测试环境
*/
public static final String HTTPS_REQ_URL = "https://openapi.alipay.com/gateway.do";
/**
* 沙箱测试环境账号
*/
public static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do";
public static final String SIGN = "sign";
public static final String SUCCESS_CODE = "10000";
public static final String CODE = "code";
/**
* 附加参数
*/
public static final String PASSBACK_PARAMS = "passback_params";
/**
* 产品代码
*/
public static final String PRODUCT_CODE = "product_code";
/**
* 返回地址
*/
public static final String RETURN_URL = "return_url";
/**
* 请求内容
*/
public static final String BIZ_CONTENT = "biz_content";
/**
* 应用授权概述
*/
public static final String APP_AUTH_TOKEN = "app_auth_token";
/**
* 收款方信息
*/
public static final String PAYEE_INFO = "payee_info";
/**
* 收款方信息
*/
public static final String ALIPAY_CERT_SN_FIELD = "alipay_cert_sn";
}

View File

@@ -0,0 +1,93 @@
/**
* Alipay.com Inc. Copyright (c) 2004-2020 All Rights Reserved.
*/
package com.egzosn.pay.ali.bean;
import com.egzosn.pay.ali.utils.AntCertificationUtil;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
import com.egzosn.pay.common.util.str.StringUtils;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 证书模式运行时环境
*
* @author zhongyu
* @version $Id: CertEnvironment.java, v 0.1 2020年01月02日 5:21 PM zhongyu Exp $
*
* @author egan update 2020/10/12
*
*/
public class CertEnvironment {
/**
* 支付宝根证书内容
*/
private String rootCertContent;
/**
* 支付宝根证书序列号
*/
private String rootCertSN;
/**
* 商户应用公钥证书序列号
*/
private String merchantCertSN;
/**
* 默认的支付宝公钥证书序列号
*/
private String aliPayPublicKeySN;
/**
* 缓存的不同支付宝公钥证书序列号对应的支付宝公钥
*/
private Map<String, String> cachedAliPayPublicKey = new ConcurrentHashMap<String, String>();
/**
* 构造证书运行环境
*
* @param merchantCert 商户公钥证书路径
* @param aliPayCert 支付宝公钥证书路径
* @param aliPayRootCert 支付宝根证书路径
*/
public CertEnvironment(InputStream merchantCert, InputStream aliPayCert, InputStream aliPayRootCert) {
if (null == merchantCert || null == aliPayCert || null == aliPayRootCert) {
throw new PayErrorException(new PayException("", "证书参数merchantCert、aliPayCert或aliPayRootCert设置不完整。"));
}
this.rootCertContent = AntCertificationUtil.readFromInputStream(aliPayRootCert);
this.rootCertSN = AntCertificationUtil.getRootCertSN(rootCertContent);
this.merchantCertSN = AntCertificationUtil.getCertSN(AntCertificationUtil.readFromInputStream((merchantCert)));
String aliPayPublicCertContent = AntCertificationUtil.readFromInputStream(aliPayCert);
aliPayPublicKeySN = AntCertificationUtil.getCertSN(aliPayPublicCertContent);
cachedAliPayPublicKey.put(aliPayPublicKeySN,
AntCertificationUtil.getCertPublicKey(aliPayPublicCertContent));
}
public String getRootCertSN() {
return rootCertSN;
}
public String getMerchantCertSN() {
return merchantCertSN;
}
public String getAliPayPublicKey(String sn) {
//如果没有指定sn则默认取缓存中的第一个值
if (StringUtils.isEmpty(sn)) {
return cachedAliPayPublicKey.values().iterator().next();
}
if (cachedAliPayPublicKey.containsKey(sn)) {
return cachedAliPayPublicKey.get(sn);
} else {
//网关在支付宝公钥证书变更前,一定会确认通知到商户并在商户做出反馈后,才会更新该商户的支付宝公钥证书
//TODO: 后续可以考虑加入自动升级支付宝公钥证书逻辑,注意并发更新冲突问题
throw new PayErrorException(new PayException("", "支付宝公钥证书[" + sn + "]已过期,请重新下载最新支付宝公钥证书并替换原证书文件"));
}
}
}

View File

@@ -0,0 +1,389 @@
package com.egzosn.pay.ali.utils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
import com.egzosn.pay.common.util.IOUtils;
import com.egzosn.pay.common.util.sign.encrypt.Base64;
import com.egzosn.pay.common.util.str.StringUtils;
/**
* 证书文件可信校验
*
* @author junying.wjy
* @version $Id: AntCertificationUtil.java, v 0.1 2019-07-29 下午04:46 junying.wjy Exp $
*
* @author egan update 2020/10/12
*
*/
public class AntCertificationUtil {
private static final Log LOGGER = LogFactory.getLog(AntCertificationUtil.class);
/**
* 验证证书是否可信
*
* @param certContent 需要验证的目标证书或者证书链
* @param rootCertContent 可信根证书列表
*/
public static boolean isTrusted(String certContent, String rootCertContent) {
X509Certificate[] certificates;
try {
certificates = readPemCertChain(certContent);
} catch (Exception e) {
LOGGER.error("读取证书失败", e);
throw new RuntimeException(e);
}
List<X509Certificate> rootCerts = new ArrayList<X509Certificate>();
try {
X509Certificate[] certs = readPemCertChain(rootCertContent);
rootCerts.addAll(Arrays.asList(certs));
} catch (Exception e) {
LOGGER.error("读取根证书失败", e);
throw new RuntimeException(e);
}
return verifyCertChain(certificates, rootCerts.toArray(new X509Certificate[rootCerts.size()]));
}
/**
* 验证证书是否是信任证书库中证书签发的
*
* @param cert 目标验证证书
* @param rootCerts 可信根证书列表
* @return 验证结果
*/
private static boolean verifyCert(X509Certificate cert, X509Certificate[] rootCerts) {
try {
cert.checkValidity();
} catch (CertificateExpiredException e) {
LOGGER.error("证书已经过期", e);
return false;
} catch (CertificateNotYetValidException e) {
LOGGER.error("证书未激活", e);
return false;
}
Map<Principal, X509Certificate> subjectMap = new HashMap<Principal, X509Certificate>();
for (X509Certificate root : rootCerts) {
subjectMap.put(root.getSubjectDN(), root);
}
Principal issuerDN = cert.getIssuerDN();
X509Certificate issuer = subjectMap.get(issuerDN);
if (issuer == null) {
LOGGER.error("证书链验证失败");
return false;
}
try {
PublicKey publicKey = issuer.getPublicKey();
verifySignature(publicKey, cert);
} catch (PayErrorException e) {
LOGGER.error("证书链验证失败", e);
return false;
}
return true;
}
/**
* 验证证书链是否是信任证书库中证书签发的
*
* @param certs 目标验证证书列表
* @param rootCerts 可信根证书列表
* @return 验证结果
*/
private static boolean verifyCertChain(X509Certificate[] certs, X509Certificate[] rootCerts) {
boolean sorted = sortByDn(certs);
if (!sorted) {
LOGGER.error("证书链验证失败:不是完整的证书链");
return false;
}
//先验证第一个证书是不是信任库中证书签发的
X509Certificate prev = certs[0];
boolean firstOK = verifyCert(prev, rootCerts);
if (!firstOK || certs.length == 1) {
return firstOK;
}
//验证证书链
for (int i = 1; i < certs.length; i++) {
X509Certificate cert = certs[i];
if (!checkValidity(cert)){
return false;
}
verifySignature(prev.getPublicKey(), cert);
prev = cert;
}
return true;
}
/**
* 验证证书链是否是信任证书库中证书签发的
*
* @param cert 目标验证证书
* @return 验证结果
*/
private static boolean checkValidity(X509Certificate cert) {
try {
cert.checkValidity();
} catch (CertificateExpiredException e) {
LOGGER.error("证书已经过期");
return false;
} catch (CertificateNotYetValidException e) {
LOGGER.error("证书未激活");
return false;
}
return true;
}
private static void verifySignature(PublicKey publicKey, X509Certificate cert){
try {
cert.verify(publicKey);
}
catch (GeneralSecurityException e) {
throw new PayErrorException(new PayException("证书校验失败", e.getMessage()));
}
}
/**
* 将证书链按照完整的签发顺序进行排序,排序后证书链为:[issuerA, subjectA]-[issuerA, subjectB]-[issuerB, subjectC]-[issuerC, subjectD]...
*
* @param certs 证书链
* @return true排序成功false证书链不完整
*/
private static boolean sortByDn(X509Certificate[] certs) {
//主题和证书的映射
Map<Principal, X509Certificate> subjectMap = new HashMap<Principal, X509Certificate>();
//签发者和证书的映射
Map<Principal, X509Certificate> issuerMap = new HashMap<Principal, X509Certificate>();
//是否包含自签名证书
boolean hasSelfSignedCert = false;
for (X509Certificate cert : certs) {
if (isSelfSigned(cert)) {
if (hasSelfSignedCert) {
return false;
}
hasSelfSignedCert = true;
}
Principal subjectDN = cert.getSubjectDN();
Principal issuerDN = cert.getIssuerDN();
subjectMap.put(subjectDN, cert);
issuerMap.put(issuerDN, cert);
}
List<X509Certificate> certChain = new ArrayList<X509Certificate>();
X509Certificate current = certs[0];
addressingUp(subjectMap, certChain, current);
addressingDown(issuerMap, certChain, current);
//说明证书链不完整
if (certs.length != certChain.size()) {
return false;
}
//将证书链复制到原先的数据
for (int i = 0; i < certChain.size(); i++) {
certs[i] = certChain.get(i);
}
return true;
}
/**
* 验证证书是否是自签发的
*
* @param cert 目标证书
* @return true自签发false不是自签发
*/
private static boolean isSelfSigned(X509Certificate cert) {
return cert.getSubjectDN().equals(cert.getIssuerDN());
}
/**
* 向上构造证书链
*
* @param subjectMap 主题和证书的映射
* @param certChain 证书链
* @param current 当前需要插入证书链的证书include
*/
private static void addressingUp(final Map<Principal, X509Certificate> subjectMap, List<X509Certificate> certChain,
final X509Certificate current) {
certChain.add(0, current);
if (isSelfSigned(current)) {
return;
}
Principal issuerDN = current.getIssuerDN();
X509Certificate issuer = subjectMap.get(issuerDN);
if (issuer == null) {
return;
}
addressingUp(subjectMap, certChain, issuer);
}
/**
* 向下构造证书链
*
* @param issuerMap 签发者和证书的映射
* @param certChain 证书链
* @param current 当前需要插入证书链的证书exclude
*/
private static void addressingDown(final Map<Principal, X509Certificate> issuerMap, List<X509Certificate> certChain,
final X509Certificate current) {
Principal subjectDN = current.getSubjectDN();
X509Certificate subject = issuerMap.get(subjectDN);
if (subject == null) {
return;
}
if (isSelfSigned(subject)) {
return;
}
certChain.add(subject);
addressingDown(issuerMap, certChain, subject);
}
private static X509Certificate[] readPemCertChain(String cert){
ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes());
CertificateFactory factory = null;
try {
factory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = factory.generateCertificates(inputStream);
return certificates.toArray(new X509Certificate[certificates.size()]);
} catch (CertificateException e) {
LOGGER.error("提取根证书失败", e);
}
return null;
}
/**
* 获取支付宝根证书序列号
*
* @param rootCertContent 支付宝根证书内容
* @return 支付宝根证书序列号
*/
public static String getRootCertSN(String rootCertContent) {
String rootCertSN = null;
try {
X509Certificate[] x509Certificates = readPemCertChain(rootCertContent);
if (null == x509Certificates){
return null;
}
MessageDigest md = MessageDigest.getInstance("MD5");
for (X509Certificate c : x509Certificates) {
if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")) {
md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes());
String certSN = new BigInteger(1, md.digest()).toString(16);
//BigInteger会把0省略掉需补全至32位
certSN = fillMD5(certSN);
if (StringUtils.isEmpty(rootCertSN)) {
rootCertSN = certSN;
} else {
rootCertSN = rootCertSN + "_" + certSN;
}
}
}
} catch (NoSuchAlgorithmException e) {
LOGGER.error("提取根证书失败", e);
}
return rootCertSN;
}
/**
* 获取公钥证书序列号
*
* @param certContent 公钥证书内容
* @return 公钥证书序列号
*/
public static String getCertSN(String certContent) {
try {
InputStream inputStream = new ByteArrayInputStream(certContent.getBytes());
CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream);
return md5((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes());
} catch (GeneralSecurityException e) {
throw new PayErrorException(new PayException(" 获取公钥证书序列号异常", e.getMessage()));
}
}
private static String md5(byte[] bytes) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new PayErrorException(new PayException("", e.getMessage()));
}
md.update(bytes);
String certSN = new BigInteger(1, md.digest()).toString(16);
//BigInteger会把0省略掉需补全至32位
certSN = fillMD5(certSN);
return certSN;
}
private static String fillMD5(String md5) {
return md5.length() == 32 ? md5 : fillMD5("0" + md5);
}
/**
* 提取公钥证书中的公钥
*
* @param certContent 公钥证书内容
* @return 公钥证书中的公钥
*/
public static String getCertPublicKey(String certContent) {
try {
InputStream inputStream = new ByteArrayInputStream(certContent.getBytes());
CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream);
return Base64.encode(cert.getPublicKey().getEncoded());
} catch (GeneralSecurityException e) {
throw new PayErrorException(new PayException(" 提取公钥证书中的公钥异常", e.getMessage()));
}
}
public static String readFromInputStream(InputStream cert) {
try {
return new String(IOUtils.toByteArray(cert), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new PayErrorException(new PayException("读取证书异常", e.getMessage()));
}
}
}

View File

@@ -2,6 +2,7 @@ import com.egzosn.pay.ali.api.AliPayConfigStorage;
import com.egzosn.pay.ali.api.AliPayService;
import com.egzosn.pay.ali.bean.AliTransactionType;
import com.egzosn.pay.common.api.PayService;
import com.egzosn.pay.common.bean.CertStoreType;
import com.egzosn.pay.common.bean.MethodType;
import com.egzosn.pay.common.bean.PayOrder;
@@ -20,12 +21,39 @@ import java.util.UUID;
*/
public class PayTest {
/**
* 设置普通公钥的方式
* 普通公钥方式与证书公钥方式为两者取其一的方式
* @param aliPayConfigStorage 支付宝配置信息
*
*/
private static void keyPublic(AliPayConfigStorage aliPayConfigStorage){
aliPayConfigStorage.setKeyPublic("支付宝公钥");
}
/**
* 设置证书公钥信息
* 普通公钥方式与证书公钥方式为两者取其一的方式
* @param aliPayConfigStorage 支付宝配置信息
*/
private static void certKeyPublic(AliPayConfigStorage aliPayConfigStorage){
//设置为证书方式
aliPayConfigStorage.setCertSign(true);
//设置证书存储方式,这里为路径
aliPayConfigStorage.setCertStoreType(CertStoreType.PATH);
aliPayConfigStorage.setMerchantCert("请填写您的应用公钥证书文件路径例如d:/appCertPublicKey_2019051064521003.crt");
aliPayConfigStorage.setAliPayCert("请填写您的支付宝公钥证书文件路径例如d:/alipayCertPublicKey_RSA2.crt");
aliPayConfigStorage.setAliPayRootCert("请填写您的支付宝根证书文件路径例如d:/alipayRootCert.crt");
}
public static void main(String[] args) {
AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage();
aliPayConfigStorage.setPid("合作者id");
aliPayConfigStorage.setAppid("应用id");
aliPayConfigStorage.setKeyPublic("支付宝公钥");
//普通公钥方式与证书公钥方式为两者取其一的方式
keyPublic(aliPayConfigStorage);
// certKeyPublic(aliPayConfigStorage);
aliPayConfigStorage.setKeyPrivate("应用私钥");
aliPayConfigStorage.setNotifyUrl("异步回调地址");
aliPayConfigStorage.setReturnUrl("同步回调地址");