From 94278307c7bb6fb9e47650e73691f04a54513854 Mon Sep 17 00:00:00 2001 From: KKSU <15274802129@163.com> Date: Wed, 17 Jan 2024 14:31:23 +0800 Subject: [PATCH] fapiao --- src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java | 280 +++++++++++++++++-------------------------------------- 1 files changed, 89 insertions(+), 191 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java index f35b58f..db77555 100644 --- a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java @@ -5,6 +5,9 @@ import cc.mrbird.febs.common.utils.SpringContextHolder; import cc.mrbird.febs.mall.entity.MallOrderInfo; import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper; +import cc.mrbird.febs.pay.model.FPCertificateVo; +import cc.mrbird.febs.pay.model.FPCertificates; +import cc.mrbird.febs.pay.model.FPEncryptCertificate; import cc.mrbird.febs.pay.model.HeaderDto; import cc.mrbird.febs.pay.service.WxFaPiaoService; import cc.mrbird.febs.pay.util.RandomStringGenerator; @@ -50,8 +53,10 @@ import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Base64; +import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -210,7 +215,7 @@ } @Override - public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { + public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, ParseException { Map<String,Object> map = new HashMap<>(); String signature = request.getHeader("Wechatpay-Signature"); String timestamp = request.getHeader("Wechatpay-Timestamp"); @@ -224,20 +229,9 @@ log.info("头信息---平台证书序列号:" + serial); log.info("获取到的body信息:" + body); //验签 - boolean signCheck = verifySign(request, body); -// boolean signCheck = signCheck(timestamp, nonce, requestBody, signature); + boolean signCheck = signCheck(timestamp, nonce, requestBody, signature); log.info("验签结果:" + signCheck); if (signCheck) { -// //解密参数 -// Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class); -// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8")); -// String string = aesUtil.decryptToString(resource.getAssociated_data().getBytes("utf-8"), resource.getNonce().getBytes("utf-8"), resource.getCiphertext()); -// ComplaintInfo complaintInfo = JSONObject.parseObject(string, ComplaintInfo.class); -// //获取投诉详情 -// ComplaintDetail complaintDetail = CommonUtils.GetComplaintsInfo(complaintInfo.getComplaint_id()); -// data.put("code", "SUCCESS"); -// data.put("message", "成功"); -// return data; try { //解析请求体 // Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class); @@ -287,198 +281,102 @@ return map; } -// /** -// * 验证签名 -// * -// * @param timestamp 微信平台传入的时间戳 -// * @param nonce 微信平台传入的随机字符串 -// * @param requestBody 微信平台传入的消息体 -// * @param signature 微信平台传入的签名 -// * @return -// * @throws NoSuchAlgorithmException -// * @throws SignatureException -// * @throws IOException -// * @throws InvalidKeyException -// */ -// public static boolean signCheck(String timestamp, String nonce, Map<String, Object> requestBody, String signature) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException { -// //构造验签名串 -// String signatureStr = timestamp + "\n" + nonce + "\n" + com.alibaba.fastjson.JSONObject.toJSONString(requestBody) + "\n"; -// // 加载SHA256withRSA签名器 -// Signature signer = Signature.getInstance("SHA256withRSA"); -// // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法) -// signer.initVerify(getCertificates()); -// // 把我们构造的验签名串更新到签名器中 -// signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); -// // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证 -// boolean result = signer.verify(Base64Utils.decodeFromString(signature)); -// return result; -// } + /** + * 验证签名 + * + * @param timestamp 微信平台传入的时间戳 + * @param nonce 微信平台传入的随机字符串 + * @param requestBody 微信平台传入的消息体 + * @param signature 微信平台传入的签名 + * @return + * @throws NoSuchAlgorithmException + * @throws SignatureException + * @throws IOException + * @throws InvalidKeyException + */ + public boolean signCheck(String timestamp, String nonce, Map<String, Object> requestBody, String signature) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException, ParseException { + //构造验签名串 + String signatureStr = timestamp + "\n" + nonce + "\n" + com.alibaba.fastjson.JSONObject.toJSONString(requestBody) + "\n"; + // 加载SHA256withRSA签名器 + Signature signer = Signature.getInstance("SHA256withRSA"); + // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法) + signer.initVerify(getCertificates()); + // 把我们构造的验签名串更新到签名器中 + signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); + // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证 + boolean result = signer.verify(Base64Utils.decodeFromString(signature)); + return result; + } /** * 获取平台证书 */ -// public static X509Certificate getCertificates() throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException { -// SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); -// CloseableHttpClient httpClient = CommonUtils.httpClient(); -// //请求URL -// HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/certificates"); -// httpGet.setHeader("Accept", "application/json"); -// //生成签名 -// httpGet.setHeader("Authorization ", SignUtils.getSign("GET", HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates"), "")); -// httpGet.setHeader("User-Agent", "https://zh.wikipedia.org/wiki/User_agent"); -// //完成签名并执行请求 -// CloseableHttpResponse response = httpClient.execute(httpGet); -// X509Certificate x509Certificate = null; -// try { -// int statusCode = response.getStatusLine().getStatusCode(); -// if (statusCode == 200) { //处理成功 -//// System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); -// CertificateVo certificateVo = JSONObject.parseObject(EntityUtils.toString(response.getEntity()), CertificateVo.class); -// for (Certificates certificates : certificateVo.getData()) { -// if (format.parse(certificates.getEffective_time()).before(new Date()) && format.parse(certificates.getExpire_time()).after(new Date())) { -// EncryptCertificate encrypt_certificate = certificates.getEncrypt_certificate(); -// //解密 -// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8")); -// String pulicKey = aesUtil.decryptToString(encrypt_certificate.getAssociated_data().getBytes("utf-8"), encrypt_certificate.getNonce().getBytes("utf-8"), encrypt_certificate.getCiphertext()); -// -// //获取平台证书 -// final CertificateFactory cf = CertificateFactory.getInstance("X509"); -// -// ByteArrayInputStream inputStream = new ByteArrayInputStream(pulicKey.getBytes(StandardCharsets.UTF_8)); -// -// x509Certificate = (X509Certificate) cf.generateCertificate(inputStream); -// } -// } -// return x509Certificate; -// } else if (statusCode == 204) { //处理成功,无返回Body -// System.out.println("success"); -// return x509Certificate; -// } else { -// System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity())); -// return x509Certificate; -// } -// } catch (GeneralSecurityException | ParseException e) { -// e.printStackTrace(); -// return null; -// } finally { -// response.close(); -// CommonUtils.after(httpClient); -// } -// } + public X509Certificate getCertificates() throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, ParseException { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + CloseableHttpClient httpClient = HttpClients.createDefault(); - /** - * 功能描述: 验证签名 - * 注意:使用微信支付平台公钥验签 - * Wechatpay-Signature 微信返签名 - * Wechatpay-Serial 微信平台证书序列号 - * - * @return java.lang.String - * @author 影子 - */ - @SneakyThrows - public boolean verifySign(HttpServletRequest request,String body) { - boolean verify = false; + PrivateKey privateKey = this.getPrivateKeyV3(); + String baseUrl = "https://api.mch.weixin.qq.com"; + String canonicalUrl = "/v3/certificates"; + String postStr = null; try { - String wechatPaySignature = request.getHeader("Wechatpay-Signature"); - String wechatPayTimestamp = request.getHeader("Wechatpay-Timestamp"); - String wechatPayNonce = request.getHeader("Wechatpay-Nonce"); - String wechatPaySerial = request.getHeader("Wechatpay-Serial"); - //组装签名串 - String signStr = Stream.of(wechatPayTimestamp, wechatPayNonce, body) - .collect(Collectors.joining("\n", "", "\n")); - //获取平台证书 - AutoUpdateCertificatesVerifier verifier = getVerifier(wechatPaySerial); - //获取失败 验证失败 - if (verifier != null) { - Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initVerify(verifier.getValidCertificate()); - //放入签名串 - signature.update(signStr.getBytes(StandardCharsets.UTF_8)); - verify = signature.verify(Base64.getDecoder().decode(wechatPaySignature.getBytes())); - } - } catch (InvalidKeyException e) { - e.printStackTrace(); + postStr = this.createAuthorization( + "GET", + baseUrl+canonicalUrl, + "", + privateKey + + ); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } - return verify; - } - - /** - * 保存微信平台证书 - */ - private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>(); - - /** - * 功能描述:获取平台证书,自动更新 - * 注意:这个方法内置了平台证书的获取和返回值解密 - */ - public AutoUpdateCertificatesVerifier getVerifier(String mchSerialNo) { - AutoUpdateCertificatesVerifier verifier = null; - if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) { - verifierMap.clear(); - try { - //传入证书 - PrivateKey privateKey = getPrivateKeyV3(); - //刷新 - PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey); - WechatPay2Credentials credentials = new WechatPay2Credentials(xcxProperties.getWecharpayMchid(), signer); - verifier = new AutoUpdateCertificatesVerifier(credentials - , xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); - verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier); - -// AutoUpdateCertificatesVerifier verifierNew = new AutoUpdateCertificatesVerifier( -// new WechatPay2Credentials( -// xcxProperties.getWecharpayMchid(), -// new PrivateKeySigner( -// mchSerialNo, -// privateKey)), -// xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); -// new WechatPay2Validator(verifierNew).; - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - verifier = verifierMap.get(mchSerialNo); - } - return verifier; - } - - - /** - * 获取公私钥.通过证书 - */ - private KeyStore store; - private final Object lock = new Object(); - public KeyPair createPKCS12(String keyAlias, String keyPass) { -// ClassPathResource resource = new ClassPathResource(xcxProperties.getCertLocalPath()); - ClassPathResource resource = new ClassPathResource("wxP12/apiclient_cert.p12"); -// File file = new File("src/main/resources/wxP12/apiclient_cert.p12"); - char[] pem = keyPass.toCharArray(); + //请求URL + HttpGet httpGet = new HttpGet(baseUrl+canonicalUrl); + httpGet.setHeader("Accept", "application/json"); + //生成签名 + httpGet.setHeader("Authorization ", "WECHATPAY2-SHA256-RSA2048"+postStr); + httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36"); + //完成签名并执行请求 + CloseableHttpResponse response = httpClient.execute(httpGet); + X509Certificate x509Certificate = null; try { - synchronized (lock) { - if (store == null) { - synchronized (lock) { - store = KeyStore.getInstance("PKCS12"); - store.load(resource.getInputStream(), pem); -// store.load(new FileInputStream(file), pem); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200) { //处理成功 +// System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); + FPCertificateVo certificateVo = com.alibaba.fastjson.JSONObject.parseObject(EntityUtils.toString(response.getEntity()), FPCertificateVo.class); + for (FPCertificates certificates : certificateVo.getData()) { + if (format.parse(certificates.getEffective_time()).before(new Date()) + && format.parse(certificates.getExpire_time()).after(new Date())) { + FPEncryptCertificate encrypt_certificate = certificates.getEncrypt_certificate(); + //解密 + AesUtil aesUtil = new AesUtil(xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); + String pulicKey = aesUtil.decryptToString( + encrypt_certificate.getAssociated_data().getBytes("utf-8"), + encrypt_certificate.getNonce().getBytes("utf-8"), + encrypt_certificate.getCiphertext()); + //获取平台证书 + final CertificateFactory cf = CertificateFactory.getInstance("X509"); + + ByteArrayInputStream inputStream = new ByteArrayInputStream(pulicKey.getBytes(StandardCharsets.UTF_8)); + + x509Certificate = (X509Certificate) cf.generateCertificate(inputStream); } } + return x509Certificate; + } else if (statusCode == 204) { //处理成功,无返回Body + System.out.println("success"); + return x509Certificate; + } else { + System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity())); + return x509Certificate; } - X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias); - certificate.checkValidity(); - // 证书的序列号 也有用 50F37206347BCC9E6AC9860DAACE52AC035F7C24 - String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); - // 证书的 公钥 - PublicKey publicKey = certificate.getPublicKey(); - // 证书的私钥 - PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem); - return new KeyPair(publicKey, storeKey); - } catch (Exception e) { - throw new IllegalStateException("Cannot load keys from store: " , e); + } catch (GeneralSecurityException | ParseException e) { + e.printStackTrace(); + return null; + } finally { + response.close(); + httpClient.close(); } } -- Gitblit v1.9.1