- 支付签名验证失败,怎么解决啊(普通服务商模式)
参数没问题、大小写没问题、签名和一次签名一致都是sha256加密,且和官方工具的生成的签名一致,MD5和sha256都试过了,且都和官方工具生成的签名一致,不知道怎么解决了,心累 [图片][图片][图片][图片]
2020-08-09 - 微信支付 API V3 创建订单
package org.jeecg.modules.milk.utils; import lombok.extern.slf4j.Slf4j; import okhttp3.HttpUrl; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import java.util.Random; @Slf4j public class WxV3Authorization { /** * 请求头【authorization】的组装(【Authorization: 认证类型 签名信息】) * 1、举例【WECHATPAY2-SHA256-RSA2048 mchid="商户号", * nonce_str="随机字符串32位", * signature="签名",timestamp="时间戳", * serial_no="证书序列号"】 * 2、注意:前缀固定值(WECHATPAY2-SHA256-RSA2048 )后面有空格 * 3、组装过程封装为一个工具类【WxV3Authorization】 * 4、参考页面【https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng】 */ /** * @param method 请求方法(与接口请求的方法保持一致【必需大写】) * @param url 请求的url(即,把请求的url配置在这里,不需要参数) * @param body 请求体(需要转换为json字符串) * @param mchid 商户号 * @param serialNo 商户证书序列号 * @param authorizationType 认证类型,目前格式固定【WECHATPAY2-SHA256-RSA2048】 * @param serialPath 证书路径(其中存放的是私钥) * @return 返回组装好的(Authorization请求头) */ public static String getAuthorization(String method, HttpUrl url, String body, String mchid, String serialNo, String authorizationType, String serialPath){ //准备32位的随机字符串 String nonceStr = generateNonceStr(); //时间戳(去掉后面三个0) long timestamp = System.currentTimeMillis() / 1000; //组装需要签名的数据 String message = buildMessage(method, url, timestamp, nonceStr, body); //使用私钥对数据进行签名(加密),【数据签名之后需要转换为字节数组(采用utf-8)】 String signature = null; try { signature = sign(message.getBytes("utf-8"), serialPath); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return authorizationType + "mchid=\"" + mchid + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\"" + serialNo + "\""; } /**对字节数据进行私钥签名(加密)*/ private static String sign(byte[] message, String serialPath){ try { //签名方式(固定SHA256withRSA) Signature sign = Signature.getInstance("SHA256withRSA"); //使用私钥进行初始化签名(私钥需要从私钥文件【证书】中读取) sign.initSign(getPrivateKey(serialPath)); //签名更新 sign.update(message); //对签名结果进行Base64编码 return Base64.getEncoder().encodeToString(sign.sign()); } catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * 组装需要签名的字符串 * @param method 请求接口的方法 * @param url 请求接口的url(截取其中:1、域名后;2、?问题前面-》中间部分的内容) * @param timestamp 时间戳 * @param nonceStr 随机32位字符串 * @param body 请求接口提交的数据(json数据字符串) * @return 返回组装之后的数据(注意,每一种数据后面都需要换行【包括最后】) */ private static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) { String canonicalUrl = url.encodedPath(); if (url.encodedQuery() != null) { canonicalUrl += "?" + url.encodedQuery(); } return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; } /** * 获取私钥。 * * @param filename 私钥文件路径 (required) * @return 私钥对象 */ private static PrivateKey getPrivateKey(String filename){ try { String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); } catch (NoSuchAlgorithmException e) { log.debug("当前Java环境不支持RSA"); e.printStackTrace(); } catch (InvalidKeySpecException e) { log.debug("无效的密钥格式"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 获取随机字符串 Nonce Str * 随机字符从symbols获取 * SecureRandom真随机数 * @return String 随机字符串 */ private static String generateNonceStr() { String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; Random random = new SecureRandom(); 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); } }
2020-07-11