赞
踩
目录
在商户系统(自开发)向微信支付平台系统发送请求,和接收结果的过程中,都是有生成签名和校验签名的过程,只是这些操作都被 SDK 的 生成CloseableHttpClient 之前已经处理过了。
签名的生成分为3步骤,在SDK中也有代码的体现。
签名串一共有五行,每一行为一个参数。行尾以 \n(换行符,ASCII编码值为0x0A)结束,包括最后一行。如果参数本身以\n结束,也需要附加一个\n。
HTTP请求方法\n GET\n
URL\n /v3/certificates\n
请求时间戳\n 1554208460\n
请求随机串\n 593BEC0C930BF1AFEB40B4A08C8FB242\n
请求报文主体\n {ifn:xxxx} \n
绝大多数编程语言提供的签名函数支持对签名数据进行签名。强烈建议商户调用该类函数,使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。
通过商户私钥使用 SHA256 withRSA 算法加密 ,在进行 BASE64 处理。
微信支付商户API v3要求请求通过HTTP Authorization头来传递签名。 Authorization由认证类型和签名信息两个部分组成。
认证类型 :这是使用什么类型进行加密处理
- Authorization: 认证类型 签名信息
-
- 具体组成为:
-
- 1.认证类型,目前为WECHATPAY2-SHA256-RSA2048
-
- 2.签名信息
-
- 发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid
- 商户API证书序列号serial_no,用于声明所使用的证书
- 请求随机串nonce_str
- 时间戳timestamp
- 签名值signature
- 注:以上五项签名信息,无顺序要求。
-
-
-
- Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"

1.通过log日志的打印得出 在生成 ScheduledUpdateCertificatesVerifier 校验器的时候已经 做好了签名的生成工作。
2.通过日志分析得出处理类是 WechatPay2Credentials,进行了签名的处理。
- public class WechatPay2Credentials implements Credentials {
-
- protected static final Logger log = LoggerFactory.getLogger(WechatPay2Credentials.class);
-
- // 私用符号
- protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- // 安全随机数生成器
- protected static final SecureRandom RANDOM = new SecureRandom();
- // 商户编号
- protected final String merchantId;
- // 签名生成器
- protected final Signer signer;
-
- public WechatPay2Credentials(String merchantId, Signer signer) {
- this.merchantId = merchantId;
- this.signer = signer;
- }
-
- public String getMerchantId() {
- return merchantId;
- }
-
- // 获取当前时间戳
- protected long generateTimestamp() {
- return System.currentTimeMillis() / 1000;
- }
-
- // 生成字符串
- protected String generateNonceStr() {
- char[] nonceChars = new char[32];
- for (int index = 0; index < nonceChars.length; ++index) {
- nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
- }
- return new String(nonceChars);
- }
-
- // 获取认证类型
- @Override
- public final String getSchema() {
- return "WECHATPAY2-SHA256-RSA2048";
- }
-
- /**
- * 生成签名串+计算签名值+设置HTTP请求头
- *
- **/
- @Override
- public final String getToken(HttpRequestWrapper request) throws IOException {
-
- // 生成随机字符串
- String nonceStr = generateNonceStr();
-
- // 生成当前时间戳
- long timestamp = generateTimestamp();
-
- // 构造签名串
- String message = buildMessage(nonceStr, timestamp, request);
-
- log.debug("authorization message=[{}]", message);
- // 使用 PrivateKeySigner 进行签名算法处理 ,通过 商户私钥 + SHA256withRSA 的摘要计算 对签名数据进行加密处理
- Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));
-
- String token = "mchid=\"" + getMerchantId() + "\","
- + "nonce_str=\"" + nonceStr + "\","
- + "timestamp=\"" + timestamp + "\","
- + "serial_no=\"" + signature.certificateSerialNumber + "\","
- + "signature=\"" + signature.sign + "\"";
- log.debug("authorization token=[{}]", token);
-
- return token;
- }
-
- /**
- * 构造签名串方法
- * @Param nonce 随机字符串
- * @Param timestamp 当前时间戳
- * @Param request 请求对象
- *
- **/
- protected String buildMessage(String nonce, long timestamp, HttpRequestWrapper request) throws IOException {
- // 1.获取当前的请求路径对象,
- URI uri = request.getURI();
-
- // 2.获取到当前请求uri 路径
- String canonicalUrl = uri.getRawPath(); // " /v3/certificates "
-
- // 3.判断是否有请求参数如果有则进行拼接上
- if (uri.getQuery() != null) {
- canonicalUrl += "?" + uri.getRawQuery();
- }
-
- // 4.签名主体
- String body = "";
-
- // 判断是否微信上传文件请求 请求方法为GET时,报文主体为空。
- // 当请求方法为POST或PUT时,请使用真实发送的JSON报文。
- // 图片上传API,请使用meta对应的JSON报文。
- // PATCH,POST,PUT
- if (request.getOriginal() instanceof WechatPayUploadHttpPost) {
- body = ((WechatPayUploadHttpPost) request.getOriginal()).getMeta();
- //
- } else if (request instanceof HttpEntityEnclosingRequest) {
- body = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity(), StandardCharsets.UTF_8);
- }
- // 5.构建签名串 GET请求 body 是空的
- return request.getRequestLine().getMethod() + "\n"
- + canonicalUrl + "\n"
- + timestamp + "\n"
- + nonce + "\n"
- + body + "\n";
- }
-
- }

- /**
- * @author xy-peng
- */
- public class PrivateKeySigner implements Signer {
-
- // 商户证书序列号
- protected final String certificateSerialNumber;
- // 商户私钥
- protected final PrivateKey privateKey;
-
- public PrivateKeySigner(String serialNumber, PrivateKey privateKey) {
- this.certificateSerialNumber = serialNumber;
- this.privateKey = privateKey;
- }
-
- // 对签名串进行摘要处理加密处理
- @Override
- public SignatureResult sign(byte[] message) {
- try {
-
- // 创建SHA256withRSA加密方式的签名生成器实例对焦
- Signature sign = Signature.getInstance("SHA256withRSA");
-
- // 初始化签名生成器,将商户私钥配置
- sign.initSign(privateKey);
-
- // 设置要加密的数据
- sign.update(message);
-
- // 签名生成方法,并将结果进行 Base64的编码处理,并他封装签名结果对象
- return new SignatureResult(Base64.getEncoder().encodeToString(sign.sign()), certificateSerialNumber);
-
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
- } catch (SignatureException e) {
- throw new RuntimeException("签名计算失败", e);
- } catch (InvalidKeyException e) {
- throw new RuntimeException("无效的私钥", e);
- }
- }
-
- }

4.在 SignatureExec 的 executeWithSignature 方法中设置HTTP请求信息,并执行请求方法,获取请求结果,并进行结果的验签操作。
签名生成流程总结
核心的类是 ScheduledUpdateCertificatesVerifier 是在原有CertificatesVerifier基础上,增加定时更新证书功能(默认1小时)
- /**
- * 初始化平台证书管理器实例,在使用前需先调用该方法
- *
- * @param credentials 认证器
- * @param apiV3Key APIv3密钥
- * @param minutesInterval 定时更新间隔时间
- */
- public synchronized void init(Credentials credentials, byte[] apiV3Key, long minutesInterval) {
- if (credentials == null || apiV3Key.length == 0 || minutesInterval == 0) {
- throw new IllegalArgumentException("credentials或apiV3Key或minutesInterval为空");
- }
- if (this.credentials == null || this.apiV3Key.length == 0 || this.executor == null
- || this.certificates == null) {
- this.credentials = credentials;
- this.apiV3Key = apiV3Key;
- this.executor = new SafeSingleScheduleExecutor();// 创建线程池
- this.certificates = new ConcurrentHashMap<>(); // 证书存放的Map
-
- // 初始化证书
- initCertificates();
-
- // 启动定时更新证书任务
- Runnable runnable = () -> {
- try {
- Thread.currentThread().setName(SCHEDULE_UPDATE_CERT_THREAD_NAME);
- log.info("Begin update Certificate.Date:{}", Instant.now());
- updateCertificates(); // 更新证书的方法
- log.info("Finish update Certificate.Date:{}", Instant.now());
- } catch (Throwable t) {
- log.error("Update Certificate failed", t);
- }
- };
- // 执行线程任务
- executor.scheduleAtFixedRate(runnable, 0, minutesInterval, TimeUnit.MINUTES);
- }
- }
-
-
- // 更新证书的方法
- private void updateCertificates() {
- Verifier verifier = null;
- if (!certificates.isEmpty()) {
- verifier = new CertificatesVerifier(certificates);
- }
- // 调用下载证书的方法
- downloadAndUpdateCert(verifier);
- }

- // 初始化证书方法
- private void initCertificates() {
- downloadAndUpdateCert(null);
- }
- // 线程安全的证书下载方法
- private synchronized void downloadAndUpdateCert(Verifier verifier) {
- // 创建出httpClient对象
- try (CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
- .withCredentials(credentials)
- .withValidator(verifier == null ? (response) -> true
- : new WechatPay2Validator(verifier))
- .build()) {
- // 发送GET请求设置请求通信息
- HttpGet httpGet = new HttpGet(CERT_DOWNLOAD_PATH);
- httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
- // 发送请求
- try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
- // 获取结果code
- int statusCode = response.getStatusLine().getStatusCode();
- // 获取结果数据
- String body = EntityUtils.toString(response.getEntity());
- // 如果结果的 200
- if (statusCode == SC_OK) {
- // 将证书文件解码后存放到map中
- Map<BigInteger, X509Certificate> newCertList = CertSerializeUtil.deserializeToCerts(apiV3Key, body);
- if (newCertList.isEmpty()) {
- log.warn("Cert list is empty");
- return;
- }
- // 清除所有的证书
- certificates.clear();
- // 重新添加证书
- certificates.putAll(newCertList);
- } else {
- log.error("Auto update cert failed, statusCode = {}, body = {}", statusCode, body);
- }
- }
- } catch (IOException | GeneralSecurityException e) {
- log.error("Download Certificate failed", e);
- }
- }

总结:
证书每隔60分钟刷新证书,两个核心东西一个是定时器一个是存储身份信息对象(Credentials),证书更新是调用证书下载的方法,证书信息被放到响应体中被加密处理过,用的是对称加密的密钥,获取到证书数据后进行解密,然后存放到缓存Map中,每次重新获取证书都会清空当前map。
需要验证签名的时间点分为两处:
注意:应答签名验证是可做可不做的,回调必须要验证签名。
如果验证商户的请求签名正确,微信支付会在应答的HTTP头部中包括应答签名。我们建议商户验证应答签名。同样的,微信支付会在回调的HTTP头部中包括回调报文的签名。商户必须 验证回调的签名,以确保回调是由微信支付发送。
- 200: OK
- {
- "data": [
- {
- "serial_no": "5157F09EFDC096DE15EBE81A47057A7232F1B8E1",
- "effective_time ": "2018-06-08T10:34:56+08:00",
- "expire_time ": "2018-12-08T10:34:56+08:00",
- "encrypt_certificate": {
- "algorithm": "AEAD_AES_256_GCM",
- "nonce": "61f9c719728a",
- "associated_data": "certificate",
- "ciphertext": "sRvt… "
- }
- },
- {
- "serial_no": "50062CE505775F070CAB06E697F1BBD1AD4F4D87",
- "effective_time ": "2018-12-07T10:34:56+08:00",
- "expire_time ": "2020-12-07T10:34:56+08:00",
- "encrypt_certificate": {
- "algorithm": "AEAD_AES_256_GCM",
- "nonce": "35f9c719727b",
- "associated_data": "certificate",
- "ciphertext": "aBvt… "
- }
- }
- ]
- }

微信支付的平台证书序列号位于HTTP头Wechatpay-Serial。验证签名前,请商户先检查序列号是否跟商户当前所持有的 微信支付平台证书的序列号一致。如果不一致,请重新获取证书。否则,签名的私钥和证书不匹配,将无法成功验证签名。
3.构造签名串
应答时间戳\n
应答随机串\n
应答报文主体\n
- HTTP/1.1 200 OK
- Server: nginx
- Date: Tue, 02 Apr 2019 12:59:40 GMT
- Content-Type: application/json; charset=utf-8
- Content-Length: 2204
- Connection: keep-alive
- Keep-Alive: timeout=8
- Content-Language: zh-CN
- Request-ID: e2762b10-b6b9-5108-a42c-16fe2422fc8a
- Wechatpay-Nonce: c5ac7061fccab6bf3e254dcf98995b8c // 应答随机串
- // 应答签名
- Wechatpay-Signature: CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==
- Wechatpay-Timestamp: 1554209980 // 应答时间戳
- Wechatpay-Serial: 5157F09EFDC096DE15EBE81A47057A7232F1B8E1
- Cache-Control: no-cache, must-revalidate
- // 应答报文主体
- {"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"4de73afd28b6","associated_data":"certificate","ciphertext":"..."}}]}

- 1554209980
- c5ac7061fccab6bf3e254dcf98995b8c
- {"data":[{"serial_no":"5157F09EFDC096DE15EBE81A47057A7232F1B8E1","effective_time":"2018-03-26T11:39:50+08:00","expire_time":"2023-03-25T11:39:50+08:00","encrypt_certificate":{"algorithm":"AEAD_AES_256_GCM","nonce":"4de73afd28b6","associated_data":"certificate","ciphertext":"..."}}]}
4.获取应答签名
Wechatpay-Signature: CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==
5.验证签名
通过源码分析得出真正的验签的类是 WechatPay2Validator ,这个类中有一个方法 validate 进行执行验证操作。
- package com.wechat.pay.contrib.apache.httpclient.auth;
-
-
- import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.REQUEST_ID;
- import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_NONCE;
- import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_SERIAL;
- import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE;
- import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP;
-
- import com.wechat.pay.contrib.apache.httpclient.Validator;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.time.DateTimeException;
- import java.time.Duration;
- import java.time.Instant;
- import org.apache.http.Header;
- import org.apache.http.HttpEntity;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.util.EntityUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
-
- /**
- * @author xy-peng
- */
- public class WechatPay2Validator implements Validator {
-
- protected static final Logger log = LoggerFactory.getLogger(WechatPay2Validator.class);
- /**
- * 应答超时时间,单位为分钟
- */
- protected static final long RESPONSE_EXPIRED_MINUTES = 5;
- // 验证器
- protected final Verifier verifier;
-
- public WechatPay2Validator(Verifier verifier) {
- this.verifier = verifier;
- }
- // 参数异常处理方法
- protected static IllegalArgumentException parameterError(String message, Object... args) {
- message = String.format(message, args);
- return new IllegalArgumentException("parameter error: " + message);
- }
-
- // 验证失败处理异常
- protected static IllegalArgumentException verifyFail(String message, Object... args) {
- message = String.format(message, args);
- return new IllegalArgumentException("signature verify fail: " + message);
- }
-
- // 核心方法验证方法
- @Override
- public final boolean validate(CloseableHttpResponse response) throws IOException {
- try {
- // 验证响应的结果参数是
- validateParameters(response);
- // 生成签名
- String message = buildMessage(response);
- // 获取微信平台证书序列号
- String serial = response.getFirstHeader(WECHAT_PAY_SERIAL).getValue();
- // 获取当前应答签名
- String signature = response.getFirstHeader(WECHAT_PAY_SIGNATURE).getValue();
-
- // 验签比较方法,
- if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
- throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
- serial, message, signature, response.getFirstHeader(REQUEST_ID).getValue());
- }
- } catch (IllegalArgumentException e) {
- log.warn(e.getMessage());
- return false;
- }
-
- return true;
- }
- // 校验参数方法
- protected final void validateParameters(CloseableHttpResponse response) {
- // 获取第一个请求为 Request-ID
- Header firstHeader = response.getFirstHeader(REQUEST_ID);
- // 如果请求头为null则验证失败
- if (firstHeader == null) {
- throw parameterError("empty " + REQUEST_ID);
- }
- // 获取请求id
- String requestId = firstHeader.getValue();
-
- // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
- // 组装一个 响应的参数名称集合 有 证书序列号、应答签名、时间戳、随机字符串
- String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
-
- Header header = null;
- // 遍历所需要的请求的参数名取从应答对象中拿如果没有则报错
- for (String headerName : headers) {
- // 拿去所需要的应答对象数据
- header = response.getFirstHeader(headerName);
- if (header == null) {
- throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
- }
- }
- // 因为最后的值是时间戳,所以这里直接获取到时间戳了
- String timestampStr = header.getValue();
-
- try {
- // 将时间戳进行转换为时间
- Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
- // 比较应答时间和当前时间超过默认5分钟, 拒绝过期应答
- if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
- throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
- }
- } catch (DateTimeException | NumberFormatException e) {
- throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
- }
- }
-
- // 验签名串
- protected final String buildMessage(CloseableHttpResponse response) throws IOException {
- String timestamp = response.getFirstHeader(WECHAT_PAY_TIMESTAMP).getValue();
- String nonce = response.getFirstHeader(WECHAT_PAY_NONCE).getValue();
- String body = getResponseBody(response);
- return timestamp + "\n"
- + nonce + "\n"
- + body + "\n";
- }
- // 获取报文主体结果
- protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
- HttpEntity entity = response.getEntity();
- return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
- }
-
- }

- protected boolean verify(X509Certificate certificate, byte[] message, String signature) {
- try {
- // 创建签名器,并指定签名算法
- Signature sign = Signature.getInstance("SHA256withRSA");
- sign.initVerify(certificate); // 设置签名器使用的证书公钥
- sign.update(message);// 要验证的数据
- // 验证传入的签名数据 也就是 update 和 verify 数据进行比较
- return sign.verify(Base64.getDecoder().decode(signature));
-
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
- } catch (SignatureException e) {
- throw new RuntimeException("签名验证过程发生了错误", e);
- } catch (InvalidKeyException e) {
- throw new RuntimeException("无效的证书", e);
- }
- }

- // sigBytes 应答的签名结果
- @Override
- protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
- if (publicKey == null) {
- throw new SignatureException("Missing public key");
- }
- try {
- if (sigBytes.length != RSACore.getByteLength(publicKey)) {
- throw new SignatureException("Signature length not correct: got " +
- sigBytes.length + " but was expecting " +
- RSACore.getByteLength(publicKey));
- }
- // 获取生成的签名串的摘要结果
- byte[] digest = getDigestValue();
- // 将应答数据进行rsa使用微信公钥解密
- byte[] decrypted = RSACore.rsa(sigBytes, publicKey);
- // 将处理的值进行拆包处理
- byte[] unpadded = padding.unpad(decrypted);
- // 使用digestOID 进行解密处理 得出 摘要的数据
- byte[] decodedDigest = decodeSignature(digestOID, unpadded);
- // 将摘要的值和应答的值进行equal比较
- return MessageDigest.isEqual(digest, decodedDigest);
- } catch (javax.crypto.BadPaddingException e) {
- // occurs if the app has used the wrong RSA public key
- // or if sigBytes is invalid
- // return false rather than propagating the exception for
- // compatibility/ease of use
- return false;
- } catch (IOException e) {
- throw new SignatureException("Signature encoding error", e);
- } finally {
- resetDigest();
- }
- }

正常的签名流程是 是对数据先进行摘要,在进行加密处理,然后在进行Base64的处理,验签也是反过来,最后一部就是验证签名
1.获取应答对象的请求头数据
2.将数据生成签名串
3.获取应答签名串
4.将生成的签名串进行摘要计算,生成摘要串。
5.使用rsa算法对应答签名进行解密得出摘要结果。
6.将生成的摘要串和解密的摘要串进行比较 。
检验的话他会先将用公钥对解码后(base64)的数据进行解密获取到加密的数据,在后用 使用rsa算法+微信公钥进行解码得出 sha256 对之前获取到微信的签名串数据进行摘要运算,在将在将两个摘要数据进行比较是否相同从而验证是否合法性。因为摘要运算,相同的数据得出的结果都是相同的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。