🎨 #3587【微信支付】支持完全公钥模式,新增fullPublicKeyModel字段来控制,默认关闭,关闭时走老逻辑,开启时,只加载公钥所需相关配置,避免下载平台证书使灰度切换无法达到100%覆盖

This commit is contained in:
SynchPj 2025-05-14 16:27:29 +08:00 committed by GitHub
parent 47051bdf31
commit d0b7a526f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 30 deletions

View File

@ -92,6 +92,16 @@ class VerifierBuilder {
return null; return null;
} }
/**
* 针对完全使用公钥的场景
* @param publicKeyId 公钥id
* @param publicKey 公钥
* @return
*/
static Verifier buildPublicCertVerifier(String publicKeyId, PublicKey publicKey) {
return getPublicCertVerifier(publicKeyId, publicKey, null);
}
/** /**
* 获取证书验证器. * 获取证书验证器.
* *

View File

@ -232,6 +232,11 @@ public class WxPayConfig {
*/ */
private boolean strictlyNeedWechatPaySerial = false; private boolean strictlyNeedWechatPaySerial = false;
/**
* 是否完全使用公钥模式(用以微信从平台证书到公钥的灰度切换)默认不使用
*/
private boolean fullPublicKeyModel = false;
/** /**
* 返回所设置的微信支付接口请求地址域名. * 返回所设置的微信支付接口请求地址域名.
* *
@ -289,23 +294,37 @@ public class WxPayConfig {
if (StringUtils.isBlank(this.getApiV3Key())) { if (StringUtils.isBlank(this.getApiV3Key())) {
throw new WxPayException("请确保apiV3Key值已设置"); throw new WxPayException("请确保apiV3Key值已设置");
} }
try {
// 尝试从p12证书中加载私钥和证书
PrivateKey merchantPrivateKey = null; PrivateKey merchantPrivateKey = null;
PublicKey publicKey = null;
// 使用完全公钥模式时只加载公钥相关配置避免下载平台证书使灰度切换无法达到100%覆盖
if (this.fullPublicKeyModel) {
if (StringUtils.isBlank(this.getCertSerialNo())) {
throw new WxPayException("使用公钥模式时请确保certSerialNo(apiV3证书序列号)值已设置");
}
if (StringUtils.isBlank(this.getPublicKeyId())) {
throw new WxPayException("使用公钥模式时请确保publicKeyId值已设置");
}
if (StringUtils.isBlank(this.getPublicKeyString()) && StringUtils.isBlank(this.getPublicKeyPath()) && this.getPublicKeyContent() == null) {
throw new WxPayException("使用公钥模式时请确保publicKeyString/publicKeyPath/publicKeyContent其中一项值已设置");
}
try (InputStream pubInputStream =
this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
this.getPublicKeyContent(), "publicKeyPath")) {
publicKey = PemUtils.loadPublicKey(pubInputStream);
}
} else {
// 不使用完全公钥模式时同时兼容平台证书和公钥
X509Certificate certificate = null; X509Certificate certificate = null;
// 尝试从p12证书中加载私钥和证书
Object[] objects = this.p12ToPem(); Object[] objects = this.p12ToPem();
if (objects != null) { if (objects != null) {
merchantPrivateKey = (PrivateKey) objects[0]; merchantPrivateKey = (PrivateKey) objects[0];
certificate = (X509Certificate) objects[1]; certificate = (X509Certificate) objects[1];
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase(); this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
} }
try {
if (merchantPrivateKey == null && StringUtils.isNotBlank(this.getPrivateKeyPath())) {
try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
this.privateKeyContent, "privateKeyPath")) {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
}
}
if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && StringUtils.isNotBlank(this.getPrivateCertPath())) { if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && StringUtils.isNotBlank(this.getPrivateCertPath())) {
try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(), try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
this.privateCertContent, "privateCertPath")) { this.privateCertContent, "privateCertPath")) {
@ -313,24 +332,38 @@ public class WxPayConfig {
} }
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase(); this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
} }
PublicKey publicKey = null;
if (this.getPublicKeyString() != null || this.getPublicKeyPath() != null || this.publicKeyContent != null) { if (this.getPublicKeyString() != null || this.getPublicKeyPath() != null || this.publicKeyContent != null) {
if (StringUtils.isBlank(this.getPublicKeyId())) {
throw new WxPayException("请确保和publicKeyId配套使用");
}
try (InputStream pubInputStream = try (InputStream pubInputStream =
this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(), this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
this.publicKeyContent, "publicKeyPath")) { this.publicKeyContent, "publicKeyPath")) {
publicKey = PemUtils.loadPublicKey(pubInputStream); publicKey = PemUtils.loadPublicKey(pubInputStream);
} }
} }
}
// 加载api私钥
if (merchantPrivateKey == null && StringUtils.isNotBlank(this.getPrivateKeyPath())) {
try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
this.privateKeyContent, "privateKeyPath")) {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
}
}
//构造Http Proxy正向代理 //构造Http Proxy正向代理
WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy(); WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();
// 构造证书验签器 // 构造证书验签器
Verifier certificatesVerifier = VerifierBuilder.build( Verifier certificatesVerifier;
if (this.fullPublicKeyModel) {
certificatesVerifier = VerifierBuilder.buildPublicCertVerifier(this.publicKeyId, publicKey);
} else {
certificatesVerifier = VerifierBuilder.build(
this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy, this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy,
this.getCertAutoUpdateTime(), this.getPayBaseUrl(), this.getCertAutoUpdateTime(), this.getPayBaseUrl(), this.getPublicKeyId(), publicKey);
this.getPublicKeyId(), publicKey }
);
WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create() WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create()
.withMerchant(mchId, certSerialNo, merchantPrivateKey) .withMerchant(mchId, certSerialNo, merchantPrivateKey)