Skip to content

Commit

Permalink
新增回调公钥验签类
Browse files Browse the repository at this point in the history
  • Loading branch information
wujunjiesd committed May 16, 2024
1 parent a98e5df commit 00ab107
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected AbstractVerifier(String algorithmName, CertificateProvider certificate
*
* @param algorithmName 获取Signature对象时指定的算法,例如SHA256withRSA
* @param publicKey 验签使用的微信支付平台公钥,非空
* @param publicKeyId 验签使用的微信支付平台公钥id
*/
protected AbstractVerifier(String algorithmName, PublicKey publicKey, String publicKeyId) {
this.publicKey = requireNonNull(publicKey);
Expand All @@ -48,6 +49,25 @@ protected AbstractVerifier(String algorithmName, PublicKey publicKey, String pub
this.certificateProvider = null;
}

/**
* AbstractVerifier 构造函数,仅在平台证书和平台公钥灰度切换阶段使用
*
* @param algorithmName 获取Signature对象时指定的算法,例如SHA256withRSA
* @param publicKey 验签使用的微信支付平台公钥,非空
* @param publicKeyId 验签使用的微信支付平台公钥id
* @param certificateProvider 验签使用的微信支付平台证书管理器,非空
*/
protected AbstractVerifier(
String algorithmName,
PublicKey publicKey,
String publicKeyId,
CertificateProvider certificateProvider) {
this.publicKey = requireNonNull(publicKey);
this.publicKeyId = publicKeyId;
this.algorithmName = requireNonNull(algorithmName);
this.certificateProvider = requireNonNull(certificateProvider);
}

protected boolean verify(X509Certificate certificate, String message, String signature) {
try {
Signature sign = Signature.getInstance(algorithmName);
Expand Down Expand Up @@ -87,15 +107,19 @@ public boolean verify(String serialNumber, String message, String signature) {
if (serialNumber.equals(publicKeyId)) {
return verify(message, signature);
}
logger.error("publicKeyId[{}] and serialNumber[{}] are not equal", publicKeyId, serialNumber);
return false;
// 如果证书为空,则说明是传入的publicKeyId错误,如果不为空,则继续使用证书验签
if (certificateProvider == null) {
logger.error(
"publicKeyId[{}] and serialNumber[{}] are not equal", publicKeyId, serialNumber);
return false;
}
}
// 使用证书验签
requireNonNull(certificateProvider);
X509Certificate certificate = certificateProvider.getCertificate(serialNumber);
if (certificate == null) {
logger.error(
"Verify the signature and get the WechatPay certificate corresponding to "
"Verify the signature and get the WechatPay certificate or publicKey corresponding to "
+ "serialNumber[{}] is empty.",
serialNumber);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ public RSAVerifier(CertificateProvider provider) {
public RSAVerifier(PublicKey publicKey, String publicKeyId) {
super(SHA256WITHRSA, publicKey, publicKeyId);
}

public RSAVerifier(PublicKey publicKey, String publicKeyId, CertificateProvider provider) {
super(SHA256WITHRSA, publicKey, publicKeyId, provider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
import com.wechat.pay.java.core.cipher.AeadCipher;
import com.wechat.pay.java.core.cipher.RSAVerifier;
import com.wechat.pay.java.core.cipher.Verifier;
import java.security.PublicKey;

public abstract class AbstractNotificationConfig implements NotificationConfig {

public final String signType;
private final String cipherAlgorithm;
private final CertificateProvider certificateProvider;
private final AeadCipher aeadCipher;
private final PublicKey publicKey;
private final String publicKeyId;

protected AbstractNotificationConfig(
String signType,
Expand All @@ -21,6 +24,37 @@ protected AbstractNotificationConfig(
this.cipherAlgorithm = cipherAlgorithm;
this.certificateProvider = certificateProvider;
this.aeadCipher = aeadCipher;
this.publicKey = null;
this.publicKeyId = null;
}

protected AbstractNotificationConfig(
String signType,
String cipherAlgorithm,
PublicKey publicKey,
String publicKeyId,
AeadCipher aeadCipher) {
this.signType = signType;
this.cipherAlgorithm = cipherAlgorithm;
this.publicKey = publicKey;
this.publicKeyId = publicKeyId;
this.aeadCipher = aeadCipher;
this.certificateProvider = null;
}

protected AbstractNotificationConfig(
String signType,
String cipherAlgorithm,
CertificateProvider certificateProvider,
PublicKey publicKey,
String publicKeyId,
AeadCipher aeadCipher) {
this.signType = signType;
this.cipherAlgorithm = cipherAlgorithm;
this.publicKey = publicKey;
this.publicKeyId = publicKeyId;
this.aeadCipher = aeadCipher;
this.certificateProvider = certificateProvider;
}

@Override
Expand All @@ -35,6 +69,12 @@ public String getCipherType() {

@Override
public Verifier createVerifier() {
if (publicKey != null && certificateProvider != null) {
return new RSAVerifier(publicKey, publicKeyId, certificateProvider);
}
if (publicKey != null) {
return new RSAVerifier(publicKey, publicKeyId);
}
return new RSAVerifier(certificateProvider);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.wechat.pay.java.core.notification;

import static com.wechat.pay.java.core.notification.Constant.AES_CIPHER_ALGORITHM;
import static com.wechat.pay.java.core.notification.Constant.RSA_SIGN_TYPE;
import static java.util.Objects.requireNonNull;

import com.wechat.pay.java.core.AbstractRSAConfigBuilder;
import com.wechat.pay.java.core.certificate.CertificateProvider;
import com.wechat.pay.java.core.certificate.RSAAutoCertificateProvider;
import com.wechat.pay.java.core.cipher.AeadAesCipher;
import com.wechat.pay.java.core.cipher.AeadCipher;
import com.wechat.pay.java.core.http.HttpClient;
import com.wechat.pay.java.core.util.PemUtil;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

/** 通知回调配置类 该类仅在商户由平台证书切换为平台公钥的灰度阶段使用,灰度完成后请切换为RSAPublicKeyNotificationConfig */
public final class RSACombinedNotificationConfig extends AbstractNotificationConfig {

private RSACombinedNotificationConfig(
CertificateProvider certificateProvider,
PublicKey publicKey,
String publicKeyId,
AeadCipher aeadAesCipher) {
super(
RSA_SIGN_TYPE,
AES_CIPHER_ALGORITHM,
certificateProvider,
publicKey,
publicKeyId,
aeadAesCipher);
}

public static class Builder extends AbstractRSAConfigBuilder<Builder> {
protected HttpClient httpClient;
protected byte[] apiV3Key;

private PublicKey publicKey;
private String publicKeyId;

public Builder apiV3Key(String apiV3Key) {
this.apiV3Key = apiV3Key.getBytes(StandardCharsets.UTF_8);
return this;
}

public Builder httpClient(HttpClient httpClient) {
this.httpClient = httpClient;
return this;
}

public Builder publicKey(String publicKey) {
this.publicKey = PemUtil.loadPublicKeyFromString(publicKey);
return this;
}

public Builder publicKey(PublicKey publicKey) {
this.publicKey = publicKey;
return this;
}

public Builder publicFromPath(String publicKeyPath) {
this.publicKey = PemUtil.loadPublicKeyFromPath(publicKeyPath);
return this;
}

public Builder publicKeyId(String publicKeyId) {
this.publicKeyId = publicKeyId;
return this;
}

@Override
protected Builder self() {
return this;
}

public RSACombinedNotificationConfig build() {

RSAAutoCertificateProvider.Builder builder =
new RSAAutoCertificateProvider.Builder()
.apiV3Key(requireNonNull(apiV3Key))
.privateKey(requireNonNull(privateKey))
.merchantId(requireNonNull(merchantId))
.merchantSerialNumber(requireNonNull(merchantSerialNumber));
if (httpClient != null) {
builder.httpClient(httpClient);
}
return new RSACombinedNotificationConfig(
builder.build(),
requireNonNull(publicKey),
requireNonNull(publicKeyId),
new AeadAesCipher(requireNonNull(apiV3Key)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
import java.util.Arrays;
import java.util.List;

/** 签名类型为RSA的通知配置参数 */
/**
* 通知回调配置类
*
* @deprecated 请使用 RSAAutoCertificateConfig,开发者应尽快迁移,我们将在未来某个时间移除这段废弃的代码。
*/
public final class RSANotificationConfig extends AbstractNotificationConfig {

private RSANotificationConfig(CertificateProvider certificateProvider, AeadCipher aeadCipher) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.wechat.pay.java.core.notification;

import static com.wechat.pay.java.core.notification.Constant.AES_CIPHER_ALGORITHM;
import static com.wechat.pay.java.core.notification.Constant.RSA_SIGN_TYPE;
import static java.util.Objects.requireNonNull;

import com.wechat.pay.java.core.cipher.AeadAesCipher;
import com.wechat.pay.java.core.cipher.AeadCipher;
import com.wechat.pay.java.core.util.PemUtil;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

/** 签名类型为RSA的通知配置参数 */
public final class RSAPublicKeyNotificationConfig extends AbstractNotificationConfig {

private RSAPublicKeyNotificationConfig(
PublicKey publicKey, String publicKeyId, AeadCipher aeadCipher) {
super(RSA_SIGN_TYPE, AES_CIPHER_ALGORITHM, publicKey, publicKeyId, aeadCipher);
}

public static class Builder {
private byte[] apiV3Key;

private PublicKey publicKey;
private String publicKeyId;

public Builder publicKey(String publicKey) {
this.publicKey = PemUtil.loadPublicKeyFromString(publicKey);
return this;
}

public Builder publicKey(PublicKey publicKey) {
this.publicKey = publicKey;
return this;
}

public Builder publicFromPath(String publicKeyPath) {
this.publicKey = PemUtil.loadPublicKeyFromPath(publicKeyPath);
return this;
}

public Builder apiV3Key(String apiV3Key) {
this.apiV3Key = apiV3Key.getBytes(StandardCharsets.UTF_8);
return this;
}

public Builder publicKeyId(String publicKeyId) {
this.publicKeyId = publicKeyId;
return this;
}

public RSAPublicKeyNotificationConfig build() {
requireNonNull(publicKey);
requireNonNull(publicKeyId);
requireNonNull(apiV3Key);
return new RSAPublicKeyNotificationConfig(
publicKey, requireNonNull(publicKeyId), new AeadAesCipher(requireNonNull(apiV3Key)));
}
}
}

0 comments on commit 00ab107

Please sign in to comment.