fix: 多租户证书无法复用的问题

Closes #77
This commit is contained in:
xiafang
2022-09-22 15:28:32 +08:00
parent 24f270acd6
commit a9ab004a96

View File

@@ -58,12 +58,7 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -96,7 +91,7 @@ public class SignatureProvider {
/** /**
* 微信平台证书容器 key = 序列号 value = 证书对象 * 微信平台证书容器 key = 序列号 value = 证书对象
*/ */
private static final Map<String, X509WechatCertificateInfo> CERTIFICATE_MAP = new ConcurrentHashMap<>(); private static final Set<X509WechatCertificateInfo> CERTIFICATE_SET = Collections.synchronizedSet(new HashSet<>());
/** /**
* 加密算法提供方 - BouncyCastle * 加密算法提供方 - BouncyCastle
*/ */
@@ -186,14 +181,21 @@ public class SignatureProvider {
public boolean responseSignVerify(ResponseSignVerifyParams params) { public boolean responseSignVerify(ResponseSignVerifyParams params) {
String wechatpaySerial = params.getWechatpaySerial(); String wechatpaySerial = params.getWechatpaySerial();
if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { X509WechatCertificateInfo certificate = CERTIFICATE_SET.stream()
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); .filter(cert -> Objects.equals(wechatpaySerial, cert.getWechatPaySerial()))
} .findAny()
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial).getX509Certificate(); .orElseGet(() -> {
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
return CERTIFICATE_SET.stream()
.filter(cert -> Objects.equals(wechatpaySerial, cert.getWechatPaySerial()))
.findAny()
.orElseThrow(()->new PayException("cannot obtain the certificate"));
});
final String signatureStr = createSign(params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody()); final String signatureStr = createSign(params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody());
Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER); Signature signer = Signature.getInstance("SHA256withRSA", BC_PROVIDER);
signer.initVerify(certificate); signer.initVerify(certificate.getX509Certificate());
signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return signer.verify(Base64Utils.decodeFromString(params.getWechatpaySignature())); return signer.verify(Base64Utils.decodeFromString(params.getWechatpaySignature()));
@@ -235,7 +237,7 @@ public class SignatureProvider {
} }
ArrayNode certificates = bodyObjectNode.withArray("data"); ArrayNode certificates = bodyObjectNode.withArray("data");
if (certificates.isArray() && certificates.size() > 0) { if (certificates.isArray() && certificates.size() > 0) {
CERTIFICATE_MAP.remove(tenantId); CERTIFICATE_SET.remove(tenantId);
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509", BC_PROVIDER); final CertificateFactory certificateFactory = CertificateFactory.getInstance("X509", BC_PROVIDER);
certificates.forEach(objectNode -> { certificates.forEach(objectNode -> {
JsonNode encryptCertificate = objectNode.get("encrypt_certificate"); JsonNode encryptCertificate = objectNode.get("encrypt_certificate");
@@ -253,7 +255,7 @@ public class SignatureProvider {
x509WechatCertificateInfo.setWechatPaySerial(responseSerialNo); x509WechatCertificateInfo.setWechatPaySerial(responseSerialNo);
x509WechatCertificateInfo.setTenantId(tenantId); x509WechatCertificateInfo.setTenantId(tenantId);
x509WechatCertificateInfo.setX509Certificate((X509Certificate) certificate); x509WechatCertificateInfo.setX509Certificate((X509Certificate) certificate);
CERTIFICATE_MAP.put(responseSerialNo, x509WechatCertificateInfo); CERTIFICATE_SET.add( x509WechatCertificateInfo);
} catch (CertificateException e) { } catch (CertificateException e) {
throw new PayException("An error occurred while generating the wechat v3 certificate, reason : " + e.getMessage()); throw new PayException("An error occurred while generating the wechat v3 certificate, reason : " + e.getMessage());
} }
@@ -296,7 +298,8 @@ public class SignatureProvider {
throw new PayException(e); throw new PayException(e);
} }
return new String(bytes, StandardCharsets.UTF_8); return new String(bytes, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchProviderException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
InvalidAlgorithmParameterException | NoSuchProviderException e) {
throw new PayException(e); throw new PayException(e);
} }
} }
@@ -336,10 +339,10 @@ public class SignatureProvider {
WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId); WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId);
PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate(); PrivateKey privateKey = wechatMetaBean.getKeyPair().getPrivate();
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", BC_PROVIDER); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", BC_PROVIDER);
cipher.init(Cipher.DECRYPT_MODE,privateKey); cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] data = Base64Utils.decodeFromString(message); byte[] data = Base64Utils.decodeFromString(message);
byte[] cipherData = cipher.doFinal(data); byte[] cipherData = cipher.doFinal(data);
return new String(cipherData,StandardCharsets.UTF_8); return new String(cipherData, StandardCharsets.UTF_8);
} catch (Exception e) { } catch (Exception e) {
throw new PayException(e); throw new PayException(e);
@@ -353,22 +356,17 @@ public class SignatureProvider {
* @return the x 509 wechat certificate info * @return the x 509 wechat certificate info
*/ */
public X509WechatCertificateInfo getCertificate(String tenantId) { public X509WechatCertificateInfo getCertificate(String tenantId) {
for (String serial : CERTIFICATE_MAP.keySet()) {
X509WechatCertificateInfo wechatCertificateInfo = CERTIFICATE_MAP.get(serial);
X509Certificate x509Cert = wechatCertificateInfo.getX509Certificate();
if (wechatCertificateInfo.getTenantId().equals(tenantId)){
try {
x509Cert.checkValidity();
return wechatCertificateInfo; return CERTIFICATE_SET.stream()
} catch (Exception e) { .filter(cert -> Objects.equals(tenantId, cert.getTenantId()))
log.warn("the wechat certificate is invalid , {}", e.getMessage()); .findAny()
// Async? .orElseGet(() -> {
wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate);
} return CERTIFICATE_SET.stream()
} .filter(cert -> Objects.equals(tenantId, cert.getTenantId()))
} .findAny()
throw new PayException("failed to obtain wechat pay x509Certificate "); .orElseThrow(() -> new PayException("cannot obtain the certificate"));
});
} }