无关风月
2024-12-09 2053b8fe0e98d4b4449bc756a93ced78f42277c4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.jilongda.common.wxPay.certificate;
 
import static java.util.Objects.requireNonNull;
 
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.AeadAesCipher;
import com.wechat.pay.java.core.cipher.AeadCipher;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.http.Constant;
import com.wechat.pay.java.core.http.DefaultHttpClientBuilder;
import com.wechat.pay.java.core.http.HostName;
import com.wechat.pay.java.core.http.HttpClient;
import com.wechat.pay.java.core.http.HttpMethod;
import com.wechat.pay.java.core.http.HttpRequest;
import com.wechat.pay.java.core.http.HttpResponse;
import com.wechat.pay.java.core.http.MediaType;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.certificate.model.Data;
import com.wechat.pay.java.service.certificate.model.DownloadCertificateResponse;
import com.wechat.pay.java.service.certificate.model.EncryptCertificate;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.Function;
 
/** 证书服务 */
public class CertificateService {
 
  private final HttpClient httpClient;
  private final HostName hostName;
 
  private static final String RSA_URL = "https://api.mch.weixin.qq.com/v3/certificates";
 
  private CertificateService(HttpClient httpClient, HostName hostName) {
    this.httpClient = requireNonNull(httpClient);
    this.hostName = hostName;
  }
 
  /** CertificateService构造器 */
  public static class Builder {
 
    private HttpClient httpClient;
    private HostName hostName;
 
    public Builder config(Config config) {
      this.httpClient = new DefaultHttpClientBuilder().config(config).build();
      return this;
    }
 
    public Builder httpClient(HttpClient httpClient) {
      this.httpClient = requireNonNull(httpClient);
      return this;
    }
 
    public Builder hostName(HostName hostName) {
      this.hostName = hostName;
      return this;
    }
 
    public CertificateService build() {
      return new CertificateService(httpClient, hostName);
    }
  }
 
  /**
   * 下载微信支付平台证书列表,仅下载RSA证书
   *
   * @param apiV3Key 微信支付 APIv3 密钥
   * @return 微信支付平台证书 X509Certificate 列表
   * @throws HttpException 发送HTTP请求失败。例如构建请求参数失败、发送请求失败、I/O错误等。包含请求信息。
   * @throws ValidationException 发送HTTP请求成功,验证微信支付返回签名失败。
   * @throws ServiceException 发送HTTP请求成功,服务返回异常。例如返回状态码小于200或大于等于300。
   * @throws MalformedMessageException 服务返回成功,content-type不为application/json、解析返回体失败。
   */
  public List<X509Certificate> downloadCertificate(byte[] apiV3Key) {
    AeadCipher aeadCipher = new AeadAesCipher(apiV3Key);
    return downloadCertificate(aeadCipher);
  }
 
  /**
   * 下载微信支付平台证书列表
   *
   * @param requestPath 下载证书的请求路径
   * @param aeadCipher 认证加密器,用于解密证书
   * @param certificateGenerator 从证书字符串到证书对象的生成器
   * @return 微信支付平台证书 X509Certificate 列表
   * @throws HttpException 发送HTTP请求失败。例如构建请求参数失败、发送请求失败、I/O错误等。包含请求信息。
   * @throws ValidationException 发送HTTP请求成功,验证微信支付返回签名失败。
   * @throws ServiceException 发送HTTP请求成功,服务返回异常。例如返回状态码小于200或大于等于300。
   * @throws MalformedMessageException 服务返回成功,content-type不为application/json、解析返回体失败。
   */
  public List<X509Certificate> downloadCertificate(
      String requestPath,
      AeadCipher aeadCipher,
      Function<String, X509Certificate> certificateGenerator) {
    if (hostName != null) {
      requestPath = requestPath.replaceFirst(HostName.API.getValue(), hostName.getValue());
    }
    HttpRequest request =
        new HttpRequest.Builder()
            .httpMethod(HttpMethod.GET)
            .url(requestPath)
            .addHeader(Constant.ACCEPT, " */*")
            .addHeader(Constant.CONTENT_TYPE, MediaType.APPLICATION_JSON.getValue())
            .build();
    HttpResponse<DownloadCertificateResponse> httpResponse =
        httpClient.execute(request, DownloadCertificateResponse.class);
    List<Data> dataList = httpResponse.getServiceResponse().getData();
    List<X509Certificate> certificates = new ArrayList<>();
    for (Data data : dataList) {
      EncryptCertificate encryptCertificate = data.getEncryptCertificate();
      String decryptCertificate =
          aeadCipher.decrypt(
              encryptCertificate.getAssociatedData().getBytes(StandardCharsets.UTF_8),
              encryptCertificate.getNonce().getBytes(StandardCharsets.UTF_8),
              Base64.getDecoder().decode(encryptCertificate.getCiphertext()));
 
      certificates.add(certificateGenerator.apply(decryptCertificate));
    }
    return certificates;
  }
 
  /**
   * 下载微信支付平台证书列表,仅下载RSA证书
   *
   * @param aeadCipher 认证加密器,用于解密证书
   * @return 证书列表
   * @throws HttpException 发送HTTP请求失败。例如构建请求参数失败、发送请求失败、I/O错误等。包含请求信息。
   * @throws ValidationException 发送HTTP请求成功,验证微信支付返回签名失败。
   * @throws ServiceException 发送HTTP请求成功,服务返回异常。例如返回状态码小于200或大于等于300。
   * @throws MalformedMessageException 服务返回成功,content-type不为application/json、解析返回体失败。
   */
  public List<X509Certificate> downloadCertificate(AeadCipher aeadCipher) {
    return downloadCertificate(RSA_URL, aeadCipher, PemUtil::loadX509FromString);
  }
}