Skip to content

fix: 证书使用 String、Content 格式时,验证器未正确初始化;新商户号无平台证书,初始化验证器抛出异常,导致v3请求构造异常 #3547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.github.binarywang.wxpay.config;

import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.v3.auth.*;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
* 验证器构建.
*
* @author holy
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
class VerifierBuilder {
/**
* 构建验证器.
* <p>
* 场景
* <pre>
* 1. 老商户号,只有平台证书,未开通公钥 (已验证)
* 2. 新商户号,被强制开通公钥,没有平台证书 (已验证)
* 3. 老商户号,有平台证书,主动开通公钥 (未验证,具备条件的朋友,可以帮忙验证下)
* ...
* </pre>
*
* @param certSerialNo c
* @param mchId m
* @param apiV3Key a
* @param merchantPrivateKey m
* @param wxPayHttpProxy w
* @param certAutoUpdateTime c
* @param payBaseUrl p
* @param publicKeyId p
* @param publicKey p
* @return v
* @throws WxPayException e
*/
@SuppressWarnings("java:S107")
static Verifier build(
// 平台证书 - 依赖参数
String certSerialNo,
String mchId,
String apiV3Key,
PrivateKey merchantPrivateKey,
WxPayHttpProxy wxPayHttpProxy,
int certAutoUpdateTime,
String payBaseUrl,
// 公钥 - 依赖参数
String publicKeyId,
PublicKey publicKey
) throws WxPayException {
Verifier certificatesVerifier = null;
Exception ex = null;

// 构建平台证书验证器
// (沿用旧逻辑)优先构建平台证书验证器,因为公钥验证器需要平台证书验证器 (见以下 .setOtherVerifier )
// 新商户号默认无平台证书,已确认无法构建平台证书验证器,会抛出异常;老商户号,有平台证书主动开通公钥的情况,待具备条件的朋友验证
// 建议公钥模式稳定后,优先构建公钥验证器,以免每次都尝试构建平台证书验证器,且失败 {@link com.github.binarywang.wxpay.v3.auth.PublicCertificateVerifier.verify}
if (merchantPrivateKey != null && StringUtils.isNoneBlank(certSerialNo, apiV3Key)) {
try {
certificatesVerifier = getCertificatesVerifier(
certSerialNo, mchId, apiV3Key, merchantPrivateKey, wxPayHttpProxy, certAutoUpdateTime, payBaseUrl
);
} catch (Exception e) {
ex = e;
}
}

// 构建公钥验证器
if (publicKey != null && StringUtils.isNotBlank(publicKeyId)) {
try {
certificatesVerifier = getPublicCertVerifier(publicKeyId, publicKey, certificatesVerifier);
} catch (Exception e) {
ex = e;
}
}
if (certificatesVerifier != null) {
return certificatesVerifier;
}

// 有异常时抛出
if (ex != null) {
throw new WxPayException(ex.getMessage(), ex);
}

// 没有证书验证器时。不确定是否抛出异常,沿用之前逻辑,返回 null
return null;
}

/**
* 获取证书验证器.
*
* @param certSerialNo certSerialNo
* @param mchId mchId
* @param apiV3Key apiV3Key
* @param merchantPrivateKey merchantPrivateKey
* @param wxPayHttpProxy wxPayHttpProxy
* @param certAutoUpdateTime certAutoUpdateTime
* @param payBaseUrl payBaseUrl
* @return verifier
*/
private static AutoUpdateCertificatesVerifier getCertificatesVerifier(
String certSerialNo, String mchId, String apiV3Key, PrivateKey merchantPrivateKey,
WxPayHttpProxy wxPayHttpProxy, int certAutoUpdateTime, String payBaseUrl
) {
return new AutoUpdateCertificatesVerifier(
new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8), certAutoUpdateTime,
payBaseUrl, wxPayHttpProxy);
}

/**
* 获取公钥验证器.
*
* @param publicKeyId id
* @param publicKey key
* @param certificatesVerifier verifier
* @return verifier
*/
private static Verifier getPublicCertVerifier(String publicKeyId, PublicKey publicKey, Verifier certificatesVerifier) {
Verifier publicCertificatesVerifier = new PublicCertificateVerifier(publicKey, publicKeyId);
publicCertificatesVerifier.setOtherVerifier(certificatesVerifier);
certificatesVerifier = publicCertificatesVerifier;
return certificatesVerifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import com.github.binarywang.wxpay.util.HttpProxyUtils;
import com.github.binarywang.wxpay.util.ResourcesUtils;
import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder;
import com.github.binarywang.wxpay.v3.auth.*;
import com.github.binarywang.wxpay.v3.auth.Verifier;
import com.github.binarywang.wxpay.v3.auth.WxPayValidator;
import com.github.binarywang.wxpay.v3.util.PemUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
Expand All @@ -19,7 +20,6 @@
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
Expand Down Expand Up @@ -320,7 +320,12 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
//构造Http Proxy正向代理
WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();

Verifier certificatesVerifier = getVerifier(merchantPrivateKey, wxPayHttpProxy, publicKey);
// 构造证书验签器
Verifier certificatesVerifier = VerifierBuilder.build(
this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy,
this.getCertAutoUpdateTime(), this.getPayBaseUrl(),
this.getPublicKeyId(), publicKey
);

WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create()
.withMerchant(mchId, certSerialNo, merchantPrivateKey)
Expand All @@ -346,23 +351,6 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
}
}

private Verifier getVerifier(PrivateKey merchantPrivateKey, WxPayHttpProxy wxPayHttpProxy, PublicKey publicKey) {
Verifier certificatesVerifier = null;
// 如果配置了平台证书,则初始化验证器以备v2版本接口验签(公钥灰度实现)
if (this.getPrivateCertPath() != null && this.getPrivateKeyPath() != null) {
certificatesVerifier = new AutoUpdateCertificatesVerifier(
new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
this.getApiV3Key().getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime(),
this.getPayBaseUrl(), wxPayHttpProxy);
}
if (publicKey != null) {
Verifier publicCertificatesVerifier = new PublicCertificateVerifier(publicKey, publicKeyId);
publicCertificatesVerifier.setOtherVerifier(certificatesVerifier);
certificatesVerifier = publicCertificatesVerifier;
}
return certificatesVerifier;
}

/**
* 初始化一个WxPayHttpProxy对象
*
Expand Down